diff --git a/api/csharp/Chirpstack/api/Application.cs b/api/csharp/Chirpstack/api/Application.cs
new file mode 100644
index 00000000..2b388bc7
--- /dev/null
+++ b/api/csharp/Chirpstack/api/Application.cs
@@ -0,0 +1,17979 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/application.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/application.proto</summary>
+  public static partial class ApplicationReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/application.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static ApplicationReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChVhcGkvYXBwbGljYXRpb24ucHJvdG8SA2FwaRocZ29vZ2xlL2FwaS9hbm5v",
+            "dGF0aW9ucy5wcm90bxofZ29vZ2xlL3Byb3RvYnVmL3RpbWVzdGFtcC5wcm90",
+            "bxobZ29vZ2xlL3Byb3RvYnVmL2VtcHR5LnByb3RvIk8KC0FwcGxpY2F0aW9u",
+            "EgoKAmlkGAEgASgJEgwKBG5hbWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyAB",
+            "KAkSEQoJdGVuYW50X2lkGAQgASgJIqQBChNBcHBsaWNhdGlvbkxpc3RJdGVt",
+            "EgoKAmlkGAEgASgJEi4KCmNyZWF0ZWRfYXQYAiABKAsyGi5nb29nbGUucHJv",
+            "dG9idWYuVGltZXN0YW1wEi4KCnVwZGF0ZWRfYXQYAyABKAsyGi5nb29nbGUu",
+            "cHJvdG9idWYuVGltZXN0YW1wEgwKBG5hbWUYBCABKAkSEwoLZGVzY3JpcHRp",
+            "b24YBSABKAkiQQoYQ3JlYXRlQXBwbGljYXRpb25SZXF1ZXN0EiUKC2FwcGxp",
+            "Y2F0aW9uGAEgASgLMhAuYXBpLkFwcGxpY2F0aW9uIicKGUNyZWF0ZUFwcGxp",
+            "Y2F0aW9uUmVzcG9uc2USCgoCaWQYASABKAkiIwoVR2V0QXBwbGljYXRpb25S",
+            "ZXF1ZXN0EgoKAmlkGAEgASgJIrkBChZHZXRBcHBsaWNhdGlvblJlc3BvbnNl",
+            "EiUKC2FwcGxpY2F0aW9uGAEgASgLMhAuYXBpLkFwcGxpY2F0aW9uEi4KCmNy",
+            "ZWF0ZWRfYXQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4K",
+            "CnVwZGF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1w",
+            "EhgKEG1lYXN1cmVtZW50X2tleXMYBCADKAkiQQoYVXBkYXRlQXBwbGljYXRp",
+            "b25SZXF1ZXN0EiUKC2FwcGxpY2F0aW9uGAEgASgLMhAuYXBpLkFwcGxpY2F0",
+            "aW9uIiYKGERlbGV0ZUFwcGxpY2F0aW9uUmVxdWVzdBIKCgJpZBgBIAEoCSJb",
+            "ChdMaXN0QXBwbGljYXRpb25zUmVxdWVzdBINCgVsaW1pdBgBIAEoDRIOCgZv",
+            "ZmZzZXQYAiABKA0SDgoGc2VhcmNoGAMgASgJEhEKCXRlbmFudF9pZBgEIAEo",
+            "CSJZChhMaXN0QXBwbGljYXRpb25zUmVzcG9uc2USEwoLdG90YWxfY291bnQY",
+            "ASABKA0SKAoGcmVzdWx0GAIgAygLMhguYXBpLkFwcGxpY2F0aW9uTGlzdEl0",
+            "ZW0iMQoXTGlzdEludGVncmF0aW9uc1JlcXVlc3QSFgoOYXBwbGljYXRpb25f",
+            "aWQYASABKAkiOQoTSW50ZWdyYXRpb25MaXN0SXRlbRIiCgRraW5kGAEgASgO",
+            "MhQuYXBpLkludGVncmF0aW9uS2luZCJZChhMaXN0SW50ZWdyYXRpb25zUmVz",
+            "cG9uc2USEwoLdG90YWxfY291bnQYASABKA0SKAoGcmVzdWx0GAIgAygLMhgu",
+            "YXBpLkludGVncmF0aW9uTGlzdEl0ZW0iygEKD0h0dHBJbnRlZ3JhdGlvbhIW",
+            "Cg5hcHBsaWNhdGlvbl9pZBgBIAEoCRIyCgdoZWFkZXJzGAIgAygLMiEuYXBp",
+            "Lkh0dHBJbnRlZ3JhdGlvbi5IZWFkZXJzRW50cnkSHwoIZW5jb2RpbmcYAyAB",
+            "KA4yDS5hcGkuRW5jb2RpbmcSGgoSZXZlbnRfZW5kcG9pbnRfdXJsGAQgASgJ",
+            "Gi4KDEhlYWRlcnNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6",
+            "AjgBIkkKHENyZWF0ZUh0dHBJbnRlZ3JhdGlvblJlcXVlc3QSKQoLaW50ZWdy",
+            "YXRpb24YASABKAsyFC5hcGkuSHR0cEludGVncmF0aW9uIjMKGUdldEh0dHBJ",
+            "bnRlZ3JhdGlvblJlcXVlc3QSFgoOYXBwbGljYXRpb25faWQYASABKAkiRwoa",
+            "R2V0SHR0cEludGVncmF0aW9uUmVzcG9uc2USKQoLaW50ZWdyYXRpb24YASAB",
+            "KAsyFC5hcGkuSHR0cEludGVncmF0aW9uIkkKHFVwZGF0ZUh0dHBJbnRlZ3Jh",
+            "dGlvblJlcXVlc3QSKQoLaW50ZWdyYXRpb24YASABKAsyFC5hcGkuSHR0cElu",
+            "dGVncmF0aW9uIjYKHERlbGV0ZUh0dHBJbnRlZ3JhdGlvblJlcXVlc3QSFgoO",
+            "YXBwbGljYXRpb25faWQYASABKAkilQIKE0luZmx1eERiSW50ZWdyYXRpb24S",
+            "FgoOYXBwbGljYXRpb25faWQYASABKAkSEAoIZW5kcG9pbnQYAiABKAkSCgoC",
+            "ZGIYAyABKAkSEAoIdXNlcm5hbWUYBCABKAkSEAoIcGFzc3dvcmQYBSABKAkS",
+            "HQoVcmV0ZW50aW9uX3BvbGljeV9uYW1lGAYgASgJEikKCXByZWNpc2lvbhgH",
+            "IAEoDjIWLmFwaS5JbmZsdXhEYlByZWNpc2lvbhIlCgd2ZXJzaW9uGAggASgO",
+            "MhQuYXBpLkluZmx1eERiVmVyc2lvbhINCgV0b2tlbhgJIAEoCRIUCgxvcmdh",
+            "bml6YXRpb24YCiABKAkSDgoGYnVja2V0GAsgASgJIlEKIENyZWF0ZUluZmx1",
+            "eERiSW50ZWdyYXRpb25SZXF1ZXN0Ei0KC2ludGVncmF0aW9uGAEgASgLMhgu",
+            "YXBpLkluZmx1eERiSW50ZWdyYXRpb24iNwodR2V0SW5mbHV4RGJJbnRlZ3Jh",
+            "dGlvblJlcXVlc3QSFgoOYXBwbGljYXRpb25faWQYASABKAkiTwoeR2V0SW5m",
+            "bHV4RGJJbnRlZ3JhdGlvblJlc3BvbnNlEi0KC2ludGVncmF0aW9uGAEgASgL",
+            "MhguYXBpLkluZmx1eERiSW50ZWdyYXRpb24iUQogVXBkYXRlSW5mbHV4RGJJ",
+            "bnRlZ3JhdGlvblJlcXVlc3QSLQoLaW50ZWdyYXRpb24YASABKAsyGC5hcGku",
+            "SW5mbHV4RGJJbnRlZ3JhdGlvbiI6CiBEZWxldGVJbmZsdXhEYkludGVncmF0",
+            "aW9uUmVxdWVzdBIWCg5hcHBsaWNhdGlvbl9pZBgBIAEoCSJAChZUaGluZ3NC",
+            "b2FyZEludGVncmF0aW9uEhYKDmFwcGxpY2F0aW9uX2lkGAEgASgJEg4KBnNl",
+            "cnZlchgCIAEoCSJXCiNDcmVhdGVUaGluZ3NCb2FyZEludGVncmF0aW9uUmVx",
+            "dWVzdBIwCgtpbnRlZ3JhdGlvbhgBIAEoCzIbLmFwaS5UaGluZ3NCb2FyZElu",
+            "dGVncmF0aW9uIjoKIEdldFRoaW5nc0JvYXJkSW50ZWdyYXRpb25SZXF1ZXN0",
+            "EhYKDmFwcGxpY2F0aW9uX2lkGAEgASgJIlUKIUdldFRoaW5nc0JvYXJkSW50",
+            "ZWdyYXRpb25SZXNwb25zZRIwCgtpbnRlZ3JhdGlvbhgBIAEoCzIbLmFwaS5U",
+            "aGluZ3NCb2FyZEludGVncmF0aW9uIlcKI1VwZGF0ZVRoaW5nc0JvYXJkSW50",
+            "ZWdyYXRpb25SZXF1ZXN0EjAKC2ludGVncmF0aW9uGAEgASgLMhsuYXBpLlRo",
+            "aW5nc0JvYXJkSW50ZWdyYXRpb24iPQojRGVsZXRlVGhpbmdzQm9hcmRJbnRl",
+            "Z3JhdGlvblJlcXVlc3QSFgoOYXBwbGljYXRpb25faWQYASABKAkiQAoUTXlE",
+            "ZXZpY2VzSW50ZWdyYXRpb24SFgoOYXBwbGljYXRpb25faWQYASABKAkSEAoI",
+            "ZW5kcG9pbnQYAiABKAkiUwohQ3JlYXRlTXlEZXZpY2VzSW50ZWdyYXRpb25S",
+            "ZXF1ZXN0Ei4KC2ludGVncmF0aW9uGAEgASgLMhkuYXBpLk15RGV2aWNlc0lu",
+            "dGVncmF0aW9uIjgKHkdldE15RGV2aWNlc0ludGVncmF0aW9uUmVxdWVzdBIW",
+            "Cg5hcHBsaWNhdGlvbl9pZBgBIAEoCSJRCh9HZXRNeURldmljZXNJbnRlZ3Jh",
+            "dGlvblJlc3BvbnNlEi4KC2ludGVncmF0aW9uGAEgASgLMhkuYXBpLk15RGV2",
+            "aWNlc0ludGVncmF0aW9uIlMKIVVwZGF0ZU15RGV2aWNlc0ludGVncmF0aW9u",
+            "UmVxdWVzdBIuCgtpbnRlZ3JhdGlvbhgBIAEoCzIZLmFwaS5NeURldmljZXNJ",
+            "bnRlZ3JhdGlvbiI7CiFEZWxldGVNeURldmljZXNJbnRlZ3JhdGlvblJlcXVl",
+            "c3QSFgoOYXBwbGljYXRpb25faWQYASABKAkiegoUTG9yYUNsb3VkSW50ZWdy",
+            "YXRpb24SFgoOYXBwbGljYXRpb25faWQYASABKAkSSgoabW9kZW1fZ2VvbG9j",
+            "YXRpb25fc2VydmljZXMYAiABKAsyJi5hcGkuTG9yYUNsb3VkTW9kZW1HZW9s",
+            "b2NhdGlvblNlcnZpY2VzIsADCiFMb3JhQ2xvdWRNb2RlbUdlb2xvY2F0aW9u",
+            "U2VydmljZXMSDQoFdG9rZW4YASABKAkSFQoNbW9kZW1fZW5hYmxlZBgCIAEo",
+            "CBISCgptb2RlbV9wb3J0GAMgASgNEhEKCWduc3NfcG9ydBgEIAEoDRIYChBn",
+            "bnNzX3VzZV9yeF90aW1lGAUgASgIEhEKCXBhcnNlX3RsdhgGIAEoCBIeChZn",
+            "ZW9sb2NhdGlvbl9idWZmZXJfdHRsGAcgASgNEiMKG2dlb2xvY2F0aW9uX21p",
+            "bl9idWZmZXJfc2l6ZRgIIAEoDRIYChBnZW9sb2NhdGlvbl90ZG9hGAkgASgI",
+            "EhgKEGdlb2xvY2F0aW9uX3Jzc2kYCiABKAgSGAoQZ2VvbG9jYXRpb25fZ25z",
+            "cxgLIAEoCBImCh5nZW9sb2NhdGlvbl9nbnNzX3BheWxvYWRfZmllbGQYDCAB",
+            "KAkSJAocZ2VvbG9jYXRpb25fZ25zc191c2VfcnhfdGltZRgNIAEoCBIYChBn",
+            "ZW9sb2NhdGlvbl93aWZpGA4gASgIEiYKHmdlb2xvY2F0aW9uX3dpZmlfcGF5",
+            "bG9hZF9maWVsZBgPIAEoCSJTCiFDcmVhdGVMb3JhQ2xvdWRJbnRlZ3JhdGlv",
+            "blJlcXVlc3QSLgoLaW50ZWdyYXRpb24YASABKAsyGS5hcGkuTG9yYUNsb3Vk",
+            "SW50ZWdyYXRpb24iOAoeR2V0TG9yYUNsb3VkSW50ZWdyYXRpb25SZXF1ZXN0",
+            "EhYKDmFwcGxpY2F0aW9uX2lkGAEgASgJIlEKH0dldExvcmFDbG91ZEludGVn",
+            "cmF0aW9uUmVzcG9uc2USLgoLaW50ZWdyYXRpb24YASABKAsyGS5hcGkuTG9y",
+            "YUNsb3VkSW50ZWdyYXRpb24iUwohVXBkYXRlTG9yYUNsb3VkSW50ZWdyYXRp",
+            "b25SZXF1ZXN0Ei4KC2ludGVncmF0aW9uGAEgASgLMhkuYXBpLkxvcmFDbG91",
+            "ZEludGVncmF0aW9uIjsKIURlbGV0ZUxvcmFDbG91ZEludGVncmF0aW9uUmVx",
+            "dWVzdBIWCg5hcHBsaWNhdGlvbl9pZBgBIAEoCSKRAQoUR2NwUHViU3ViSW50",
+            "ZWdyYXRpb24SFgoOYXBwbGljYXRpb25faWQYASABKAkSHwoIZW5jb2RpbmcY",
+            "AiABKA4yDS5hcGkuRW5jb2RpbmcSGAoQY3JlZGVudGlhbHNfZmlsZRgDIAEo",
+            "CRISCgpwcm9qZWN0X2lkGAQgASgJEhIKCnRvcGljX25hbWUYBSABKAkiUwoh",
+            "Q3JlYXRlR2NwUHViU3ViSW50ZWdyYXRpb25SZXF1ZXN0Ei4KC2ludGVncmF0",
+            "aW9uGAEgASgLMhkuYXBpLkdjcFB1YlN1YkludGVncmF0aW9uIjgKHkdldEdj",
+            "cFB1YlN1YkludGVncmF0aW9uUmVxdWVzdBIWCg5hcHBsaWNhdGlvbl9pZBgB",
+            "IAEoCSJRCh9HZXRHY3BQdWJTdWJJbnRlZ3JhdGlvblJlc3BvbnNlEi4KC2lu",
+            "dGVncmF0aW9uGAEgASgLMhkuYXBpLkdjcFB1YlN1YkludGVncmF0aW9uIlMK",
+            "IVVwZGF0ZUdjcFB1YlN1YkludGVncmF0aW9uUmVxdWVzdBIuCgtpbnRlZ3Jh",
+            "dGlvbhgBIAEoCzIZLmFwaS5HY3BQdWJTdWJJbnRlZ3JhdGlvbiI7CiFEZWxl",
+            "dGVHY3BQdWJTdWJJbnRlZ3JhdGlvblJlcXVlc3QSFgoOYXBwbGljYXRpb25f",
+            "aWQYASABKAkioQEKEUF3c1Nuc0ludGVncmF0aW9uEhYKDmFwcGxpY2F0aW9u",
+            "X2lkGAEgASgJEh8KCGVuY29kaW5nGAIgASgOMg0uYXBpLkVuY29kaW5nEg4K",
+            "BnJlZ2lvbhgDIAEoCRIVCg1hY2Nlc3Nfa2V5X2lkGAQgASgJEhkKEXNlY3Jl",
+            "dF9hY2Nlc3Nfa2V5GAUgASgJEhEKCXRvcGljX2FybhgGIAEoCSJNCh5DcmVh",
+            "dGVBd3NTbnNJbnRlZ3JhdGlvblJlcXVlc3QSKwoLaW50ZWdyYXRpb24YASAB",
+            "KAsyFi5hcGkuQXdzU25zSW50ZWdyYXRpb24iNQobR2V0QXdzU25zSW50ZWdy",
+            "YXRpb25SZXF1ZXN0EhYKDmFwcGxpY2F0aW9uX2lkGAEgASgJIksKHEdldEF3",
+            "c1Nuc0ludGVncmF0aW9uUmVzcG9uc2USKwoLaW50ZWdyYXRpb24YASABKAsy",
+            "Fi5hcGkuQXdzU25zSW50ZWdyYXRpb24iTQoeVXBkYXRlQXdzU25zSW50ZWdy",
+            "YXRpb25SZXF1ZXN0EisKC2ludGVncmF0aW9uGAEgASgLMhYuYXBpLkF3c1Nu",
+            "c0ludGVncmF0aW9uIjgKHkRlbGV0ZUF3c1Nuc0ludGVncmF0aW9uUmVxdWVz",
+            "dBIWCg5hcHBsaWNhdGlvbl9pZBgBIAEoCSKGAQoaQXp1cmVTZXJ2aWNlQnVz",
+            "SW50ZWdyYXRpb24SFgoOYXBwbGljYXRpb25faWQYASABKAkSHwoIZW5jb2Rp",
+            "bmcYAiABKA4yDS5hcGkuRW5jb2RpbmcSGQoRY29ubmVjdGlvbl9zdHJpbmcY",
+            "AyABKAkSFAoMcHVibGlzaF9uYW1lGAQgASgJIl8KJ0NyZWF0ZUF6dXJlU2Vy",
+            "dmljZUJ1c0ludGVncmF0aW9uUmVxdWVzdBI0CgtpbnRlZ3JhdGlvbhgBIAEo",
+            "CzIfLmFwaS5BenVyZVNlcnZpY2VCdXNJbnRlZ3JhdGlvbiI+CiRHZXRBenVy",
+            "ZVNlcnZpY2VCdXNJbnRlZ3JhdGlvblJlcXVlc3QSFgoOYXBwbGljYXRpb25f",
+            "aWQYASABKAkiXQolR2V0QXp1cmVTZXJ2aWNlQnVzSW50ZWdyYXRpb25SZXNw",
+            "b25zZRI0CgtpbnRlZ3JhdGlvbhgBIAEoCzIfLmFwaS5BenVyZVNlcnZpY2VC",
+            "dXNJbnRlZ3JhdGlvbiJfCidVcGRhdGVBenVyZVNlcnZpY2VCdXNJbnRlZ3Jh",
+            "dGlvblJlcXVlc3QSNAoLaW50ZWdyYXRpb24YASABKAsyHy5hcGkuQXp1cmVT",
+            "ZXJ2aWNlQnVzSW50ZWdyYXRpb24iQQonRGVsZXRlQXp1cmVTZXJ2aWNlQnVz",
+            "SW50ZWdyYXRpb25SZXF1ZXN0EhYKDmFwcGxpY2F0aW9uX2lkGAEgASgJIk8K",
+            "FlBpbG90VGhpbmdzSW50ZWdyYXRpb24SFgoOYXBwbGljYXRpb25faWQYASAB",
+            "KAkSDgoGc2VydmVyGAIgASgJEg0KBXRva2VuGAMgASgJIlcKI0NyZWF0ZVBp",
+            "bG90VGhpbmdzSW50ZWdyYXRpb25SZXF1ZXN0EjAKC2ludGVncmF0aW9uGAEg",
+            "ASgLMhsuYXBpLlBpbG90VGhpbmdzSW50ZWdyYXRpb24iOgogR2V0UGlsb3RU",
+            "aGluZ3NJbnRlZ3JhdGlvblJlcXVlc3QSFgoOYXBwbGljYXRpb25faWQYASAB",
+            "KAkiVQohR2V0UGlsb3RUaGluZ3NJbnRlZ3JhdGlvblJlc3BvbnNlEjAKC2lu",
+            "dGVncmF0aW9uGAEgASgLMhsuYXBpLlBpbG90VGhpbmdzSW50ZWdyYXRpb24i",
+            "VwojVXBkYXRlUGlsb3RUaGluZ3NJbnRlZ3JhdGlvblJlcXVlc3QSMAoLaW50",
+            "ZWdyYXRpb24YASABKAsyGy5hcGkuUGlsb3RUaGluZ3NJbnRlZ3JhdGlvbiI9",
+            "CiNEZWxldGVQaWxvdFRoaW5nc0ludGVncmF0aW9uUmVxdWVzdBIWCg5hcHBs",
+            "aWNhdGlvbl9pZBgBIAEoCSJOChBJZnR0dEludGVncmF0aW9uEhYKDmFwcGxp",
+            "Y2F0aW9uX2lkGAEgASgJEgsKA2tleRgCIAEoCRIVCg11cGxpbmtfdmFsdWVz",
+            "GAMgAygJIksKHUNyZWF0ZUlmdHR0SW50ZWdyYXRpb25SZXF1ZXN0EioKC2lu",
+            "dGVncmF0aW9uGAEgASgLMhUuYXBpLklmdHR0SW50ZWdyYXRpb24iNAoaR2V0",
+            "SWZ0dHRJbnRlZ3JhdGlvblJlcXVlc3QSFgoOYXBwbGljYXRpb25faWQYASAB",
+            "KAkiSQobR2V0SWZ0dHRJbnRlZ3JhdGlvblJlc3BvbnNlEioKC2ludGVncmF0",
+            "aW9uGAEgASgLMhUuYXBpLklmdHR0SW50ZWdyYXRpb24iSwodVXBkYXRlSWZ0",
+            "dHRJbnRlZ3JhdGlvblJlcXVlc3QSKgoLaW50ZWdyYXRpb24YASABKAsyFS5h",
+            "cGkuSWZ0dHRJbnRlZ3JhdGlvbiI3Ch1EZWxldGVJZnR0dEludGVncmF0aW9u",
+            "UmVxdWVzdBIWCg5hcHBsaWNhdGlvbl9pZBgBIAEoCSJJCi9HZW5lcmF0ZU1x",
+            "dHRJbnRlZ3JhdGlvbkNsaWVudENlcnRpZmljYXRlUmVxdWVzdBIWCg5hcHBs",
+            "aWNhdGlvbl9pZBgBIAEoCSKWAQowR2VuZXJhdGVNcXR0SW50ZWdyYXRpb25D",
+            "bGllbnRDZXJ0aWZpY2F0ZVJlc3BvbnNlEhAKCHRsc19jZXJ0GAEgASgJEg8K",
+            "B3Rsc19rZXkYAiABKAkSDwoHY2FfY2VydBgDIAEoCRIuCgpleHBpcmVzX2F0",
+            "GAQgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCoiCghFbmNvZGlu",
+            "ZxIICgRKU09OEAASDAoIUFJPVE9CVUYQASq/AQoPSW50ZWdyYXRpb25LaW5k",
+            "EggKBEhUVFAQABINCglJTkZMVVhfREIQARIQCgxUSElOR1NfQk9BUkQQAhIO",
+            "CgpNWV9ERVZJQ0VTEAMSDgoKTE9SQV9DTE9VRBAEEg8KC0dDUF9QVUJfU1VC",
+            "EAUSCwoHQVdTX1NOUxAGEhUKEUFaVVJFX1NFUlZJQ0VfQlVTEAcSEAoMUElM",
+            "T1RfVEhJTkdTEAgSDwoLTVFUVF9HTE9CQUwQCRIJCgVJRlRUVBAKKj8KEUlu",
+            "Zmx1eERiUHJlY2lzaW9uEgYKAk5TEAASBQoBVRABEgYKAk1TEAISBQoBUxAD",
+            "EgUKAU0QBBIFCgFIEAUqMQoPSW5mbHV4RGJWZXJzaW9uEg4KCklORkxVWERC",
+            "XzEQABIOCgpJTkZMVVhEQl8yEAEyuDwKEkFwcGxpY2F0aW9uU2VydmljZRJl",
+            "CgZDcmVhdGUSHS5hcGkuQ3JlYXRlQXBwbGljYXRpb25SZXF1ZXN0Gh4uYXBp",
+            "LkNyZWF0ZUFwcGxpY2F0aW9uUmVzcG9uc2UiHILT5JMCFiIRL2FwaS9hcHBs",
+            "aWNhdGlvbnM6ASoSXgoDR2V0EhouYXBpLkdldEFwcGxpY2F0aW9uUmVxdWVz",
+            "dBobLmFwaS5HZXRBcHBsaWNhdGlvblJlc3BvbnNlIh6C0+STAhgSFi9hcGkv",
+            "YXBwbGljYXRpb25zL3tpZH0SbgoGVXBkYXRlEh0uYXBpLlVwZGF0ZUFwcGxp",
+            "Y2F0aW9uUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSItgtPkkwIn",
+            "GiIvYXBpL2FwcGxpY2F0aW9ucy97YXBwbGljYXRpb24uaWR9OgEqEl8KBkRl",
+            "bGV0ZRIdLmFwaS5EZWxldGVBcHBsaWNhdGlvblJlcXVlc3QaFi5nb29nbGUu",
+            "cHJvdG9idWYuRW1wdHkiHoLT5JMCGCoWL2FwaS9hcHBsaWNhdGlvbnMve2lk",
+            "fRJeCgRMaXN0EhwuYXBpLkxpc3RBcHBsaWNhdGlvbnNSZXF1ZXN0Gh0uYXBp",
+            "Lkxpc3RBcHBsaWNhdGlvbnNSZXNwb25zZSIZgtPkkwITEhEvYXBpL2FwcGxp",
+            "Y2F0aW9ucxKIAQoQTGlzdEludGVncmF0aW9ucxIcLmFwaS5MaXN0SW50ZWdy",
+            "YXRpb25zUmVxdWVzdBodLmFwaS5MaXN0SW50ZWdyYXRpb25zUmVzcG9uc2Ui",
+            "N4LT5JMCMRIvL2FwaS9hcHBsaWNhdGlvbnMve2FwcGxpY2F0aW9uX2lkfS9p",
+            "bnRlZ3JhdGlvbnMSnwEKFUNyZWF0ZUh0dHBJbnRlZ3JhdGlvbhIhLmFwaS5D",
+            "cmVhdGVIdHRwSW50ZWdyYXRpb25SZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVm",
+            "LkVtcHR5IkuC0+STAkUiQC9hcGkvYXBwbGljYXRpb25zL3tpbnRlZ3JhdGlv",
+            "bi5hcHBsaWNhdGlvbl9pZH0vaW50ZWdyYXRpb25zL2h0dHA6ASoSkwEKEkdl",
+            "dEh0dHBJbnRlZ3JhdGlvbhIeLmFwaS5HZXRIdHRwSW50ZWdyYXRpb25SZXF1",
+            "ZXN0Gh8uYXBpLkdldEh0dHBJbnRlZ3JhdGlvblJlc3BvbnNlIjyC0+STAjYS",
+            "NC9hcGkvYXBwbGljYXRpb25zL3thcHBsaWNhdGlvbl9pZH0vaW50ZWdyYXRp",
+            "b25zL2h0dHASnwEKFVVwZGF0ZUh0dHBJbnRlZ3JhdGlvbhIhLmFwaS5VcGRh",
+            "dGVIdHRwSW50ZWdyYXRpb25SZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVt",
+            "cHR5IkuC0+STAkUaQC9hcGkvYXBwbGljYXRpb25zL3tpbnRlZ3JhdGlvbi5h",
+            "cHBsaWNhdGlvbl9pZH0vaW50ZWdyYXRpb25zL2h0dHA6ASoSkAEKFURlbGV0",
+            "ZUh0dHBJbnRlZ3JhdGlvbhIhLmFwaS5EZWxldGVIdHRwSW50ZWdyYXRpb25S",
+            "ZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IjyC0+STAjYqNC9hcGkv",
+            "YXBwbGljYXRpb25zL3thcHBsaWNhdGlvbl9pZH0vaW50ZWdyYXRpb25zL2h0",
+            "dHASqwEKGUNyZWF0ZUluZmx1eERiSW50ZWdyYXRpb24SJS5hcGkuQ3JlYXRl",
+            "SW5mbHV4RGJJbnRlZ3JhdGlvblJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYu",
+            "RW1wdHkiT4LT5JMCSSJEL2FwaS9hcHBsaWNhdGlvbnMve2ludGVncmF0aW9u",
+            "LmFwcGxpY2F0aW9uX2lkfS9pbnRlZ3JhdGlvbnMvaW5mbHV4ZGI6ASoSowEK",
+            "FkdldEluZmx1eERiSW50ZWdyYXRpb24SIi5hcGkuR2V0SW5mbHV4RGJJbnRl",
+            "Z3JhdGlvblJlcXVlc3QaIy5hcGkuR2V0SW5mbHV4RGJJbnRlZ3JhdGlvblJl",
+            "c3BvbnNlIkCC0+STAjoSOC9hcGkvYXBwbGljYXRpb25zL3thcHBsaWNhdGlv",
+            "bl9pZH0vaW50ZWdyYXRpb25zL2luZmx1eGRiEqsBChlVcGRhdGVJbmZsdXhE",
+            "YkludGVncmF0aW9uEiUuYXBpLlVwZGF0ZUluZmx1eERiSW50ZWdyYXRpb25S",
+            "ZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5Ik+C0+STAkkaRC9hcGkv",
+            "YXBwbGljYXRpb25zL3tpbnRlZ3JhdGlvbi5hcHBsaWNhdGlvbl9pZH0vaW50",
+            "ZWdyYXRpb25zL2luZmx1eGRiOgEqEpwBChlEZWxldGVJbmZsdXhEYkludGVn",
+            "cmF0aW9uEiUuYXBpLkRlbGV0ZUluZmx1eERiSW50ZWdyYXRpb25SZXF1ZXN0",
+            "GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IkCC0+STAjoqOC9hcGkvYXBwbGlj",
+            "YXRpb25zL3thcHBsaWNhdGlvbl9pZH0vaW50ZWdyYXRpb25zL2luZmx1eGRi",
+            "ErQBChxDcmVhdGVUaGluZ3NCb2FyZEludGVncmF0aW9uEiguYXBpLkNyZWF0",
+            "ZVRoaW5nc0JvYXJkSW50ZWdyYXRpb25SZXF1ZXN0GhYuZ29vZ2xlLnByb3Rv",
+            "YnVmLkVtcHR5IlKC0+STAkwiRy9hcGkvYXBwbGljYXRpb25zL3tpbnRlZ3Jh",
+            "dGlvbi5hcHBsaWNhdGlvbl9pZH0vaW50ZWdyYXRpb25zL3RoaW5nc2JvYXJk",
+            "OgEqEq8BChlHZXRUaGluZ3NCb2FyZEludGVncmF0aW9uEiUuYXBpLkdldFRo",
+            "aW5nc0JvYXJkSW50ZWdyYXRpb25SZXF1ZXN0GiYuYXBpLkdldFRoaW5nc0Jv",
+            "YXJkSW50ZWdyYXRpb25SZXNwb25zZSJDgtPkkwI9EjsvYXBpL2FwcGxpY2F0",
+            "aW9ucy97YXBwbGljYXRpb25faWR9L2ludGVncmF0aW9ucy90aGluZ3Nib2Fy",
+            "ZBK0AQocVXBkYXRlVGhpbmdzQm9hcmRJbnRlZ3JhdGlvbhIoLmFwaS5VcGRh",
+            "dGVUaGluZ3NCb2FyZEludGVncmF0aW9uUmVxdWVzdBoWLmdvb2dsZS5wcm90",
+            "b2J1Zi5FbXB0eSJSgtPkkwJMGkcvYXBpL2FwcGxpY2F0aW9ucy97aW50ZWdy",
+            "YXRpb24uYXBwbGljYXRpb25faWR9L2ludGVncmF0aW9ucy90aGluZ3Nib2Fy",
+            "ZDoBKhKlAQocRGVsZXRlVGhpbmdzQm9hcmRJbnRlZ3JhdGlvbhIoLmFwaS5E",
+            "ZWxldGVUaGluZ3NCb2FyZEludGVncmF0aW9uUmVxdWVzdBoWLmdvb2dsZS5w",
+            "cm90b2J1Zi5FbXB0eSJDgtPkkwI9KjsvYXBpL2FwcGxpY2F0aW9ucy97YXBw",
+            "bGljYXRpb25faWR9L2ludGVncmF0aW9ucy90aGluZ3Nib2FyZBKuAQoaQ3Jl",
+            "YXRlTXlEZXZpY2VzSW50ZWdyYXRpb24SJi5hcGkuQ3JlYXRlTXlEZXZpY2Vz",
+            "SW50ZWdyYXRpb25SZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IlCC",
+            "0+STAkoiRS9hcGkvYXBwbGljYXRpb25zL3tpbnRlZ3JhdGlvbi5hcHBsaWNh",
+            "dGlvbl9pZH0vaW50ZWdyYXRpb25zL215ZGV2aWNlczoBKhKnAQoXR2V0TXlE",
+            "ZXZpY2VzSW50ZWdyYXRpb24SIy5hcGkuR2V0TXlEZXZpY2VzSW50ZWdyYXRp",
+            "b25SZXF1ZXN0GiQuYXBpLkdldE15RGV2aWNlc0ludGVncmF0aW9uUmVzcG9u",
+            "c2UiQYLT5JMCOxI5L2FwaS9hcHBsaWNhdGlvbnMve2FwcGxpY2F0aW9uX2lk",
+            "fS9pbnRlZ3JhdGlvbnMvbXlkZXZpY2VzEq4BChpVcGRhdGVNeURldmljZXNJ",
+            "bnRlZ3JhdGlvbhImLmFwaS5VcGRhdGVNeURldmljZXNJbnRlZ3JhdGlvblJl",
+            "cXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiUILT5JMCShpFL2FwaS9h",
+            "cHBsaWNhdGlvbnMve2ludGVncmF0aW9uLmFwcGxpY2F0aW9uX2lkfS9pbnRl",
+            "Z3JhdGlvbnMvbXlkZXZpY2VzOgEqEp8BChpEZWxldGVNeURldmljZXNJbnRl",
+            "Z3JhdGlvbhImLmFwaS5EZWxldGVNeURldmljZXNJbnRlZ3JhdGlvblJlcXVl",
+            "c3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiQYLT5JMCOyo5L2FwaS9hcHBs",
+            "aWNhdGlvbnMve2FwcGxpY2F0aW9uX2lkfS9pbnRlZ3JhdGlvbnMvbXlkZXZp",
+            "Y2VzEq4BChpDcmVhdGVMb3JhQ2xvdWRJbnRlZ3JhdGlvbhImLmFwaS5DcmVh",
+            "dGVMb3JhQ2xvdWRJbnRlZ3JhdGlvblJlcXVlc3QaFi5nb29nbGUucHJvdG9i",
+            "dWYuRW1wdHkiUILT5JMCSiJFL2FwaS9hcHBsaWNhdGlvbnMve2ludGVncmF0",
+            "aW9uLmFwcGxpY2F0aW9uX2lkfS9pbnRlZ3JhdGlvbnMvbG9yYWNsb3VkOgEq",
+            "EqcBChdHZXRMb3JhQ2xvdWRJbnRlZ3JhdGlvbhIjLmFwaS5HZXRMb3JhQ2xv",
+            "dWRJbnRlZ3JhdGlvblJlcXVlc3QaJC5hcGkuR2V0TG9yYUNsb3VkSW50ZWdy",
+            "YXRpb25SZXNwb25zZSJBgtPkkwI7EjkvYXBpL2FwcGxpY2F0aW9ucy97YXBw",
+            "bGljYXRpb25faWR9L2ludGVncmF0aW9ucy9sb3JhY2xvdWQSrgEKGlVwZGF0",
+            "ZUxvcmFDbG91ZEludGVncmF0aW9uEiYuYXBpLlVwZGF0ZUxvcmFDbG91ZElu",
+            "dGVncmF0aW9uUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSJQgtPk",
+            "kwJKGkUvYXBpL2FwcGxpY2F0aW9ucy97aW50ZWdyYXRpb24uYXBwbGljYXRp",
+            "b25faWR9L2ludGVncmF0aW9ucy9sb3JhY2xvdWQ6ASoSnwEKGkRlbGV0ZUxv",
+            "cmFDbG91ZEludGVncmF0aW9uEiYuYXBpLkRlbGV0ZUxvcmFDbG91ZEludGVn",
+            "cmF0aW9uUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSJBgtPkkwI7",
+            "KjkvYXBpL2FwcGxpY2F0aW9ucy97YXBwbGljYXRpb25faWR9L2ludGVncmF0",
+            "aW9ucy9sb3JhY2xvdWQSsAEKGkNyZWF0ZUdjcFB1YlN1YkludGVncmF0aW9u",
+            "EiYuYXBpLkNyZWF0ZUdjcFB1YlN1YkludGVncmF0aW9uUmVxdWVzdBoWLmdv",
+            "b2dsZS5wcm90b2J1Zi5FbXB0eSJSgtPkkwJMIkcvYXBpL2FwcGxpY2F0aW9u",
+            "cy97aW50ZWdyYXRpb24uYXBwbGljYXRpb25faWR9L2ludGVncmF0aW9ucy9n",
+            "Y3AtcHViLXN1YjoBKhKpAQoXR2V0R2NwUHViU3ViSW50ZWdyYXRpb24SIy5h",
+            "cGkuR2V0R2NwUHViU3ViSW50ZWdyYXRpb25SZXF1ZXN0GiQuYXBpLkdldEdj",
+            "cFB1YlN1YkludGVncmF0aW9uUmVzcG9uc2UiQ4LT5JMCPRI7L2FwaS9hcHBs",
+            "aWNhdGlvbnMve2FwcGxpY2F0aW9uX2lkfS9pbnRlZ3JhdGlvbnMvZ2NwLXB1",
+            "Yi1zdWISsAEKGlVwZGF0ZUdjcFB1YlN1YkludGVncmF0aW9uEiYuYXBpLlVw",
+            "ZGF0ZUdjcFB1YlN1YkludGVncmF0aW9uUmVxdWVzdBoWLmdvb2dsZS5wcm90",
+            "b2J1Zi5FbXB0eSJSgtPkkwJMGkcvYXBpL2FwcGxpY2F0aW9ucy97aW50ZWdy",
+            "YXRpb24uYXBwbGljYXRpb25faWR9L2ludGVncmF0aW9ucy9nY3AtcHViLXN1",
+            "YjoBKhKhAQoaRGVsZXRlR2NwUHViU3ViSW50ZWdyYXRpb24SJi5hcGkuRGVs",
+            "ZXRlR2NwUHViU3ViSW50ZWdyYXRpb25SZXF1ZXN0GhYuZ29vZ2xlLnByb3Rv",
+            "YnVmLkVtcHR5IkOC0+STAj0qOy9hcGkvYXBwbGljYXRpb25zL3thcHBsaWNh",
+            "dGlvbl9pZH0vaW50ZWdyYXRpb25zL2djcC1wdWItc3ViEqYBChdDcmVhdGVB",
+            "d3NTbnNJbnRlZ3JhdGlvbhIjLmFwaS5DcmVhdGVBd3NTbnNJbnRlZ3JhdGlv",
+            "blJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiToLT5JMCSCJDL2Fw",
+            "aS9hcHBsaWNhdGlvbnMve2ludGVncmF0aW9uLmFwcGxpY2F0aW9uX2lkfS9p",
+            "bnRlZ3JhdGlvbnMvYXdzLXNuczoBKhKcAQoUR2V0QXdzU25zSW50ZWdyYXRp",
+            "b24SIC5hcGkuR2V0QXdzU25zSW50ZWdyYXRpb25SZXF1ZXN0GiEuYXBpLkdl",
+            "dEF3c1Nuc0ludGVncmF0aW9uUmVzcG9uc2UiP4LT5JMCORI3L2FwaS9hcHBs",
+            "aWNhdGlvbnMve2FwcGxpY2F0aW9uX2lkfS9pbnRlZ3JhdGlvbnMvYXdzLXNu",
+            "cxKmAQoXVXBkYXRlQXdzU25zSW50ZWdyYXRpb24SIy5hcGkuVXBkYXRlQXdz",
+            "U25zSW50ZWdyYXRpb25SZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5",
+            "Ik6C0+STAkgaQy9hcGkvYXBwbGljYXRpb25zL3tpbnRlZ3JhdGlvbi5hcHBs",
+            "aWNhdGlvbl9pZH0vaW50ZWdyYXRpb25zL2F3cy1zbnM6ASoSlwEKF0RlbGV0",
+            "ZUF3c1Nuc0ludGVncmF0aW9uEiMuYXBpLkRlbGV0ZUF3c1Nuc0ludGVncmF0",
+            "aW9uUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSI/gtPkkwI5Kjcv",
+            "YXBpL2FwcGxpY2F0aW9ucy97YXBwbGljYXRpb25faWR9L2ludGVncmF0aW9u",
+            "cy9hd3Mtc25zEsIBCiBDcmVhdGVBenVyZVNlcnZpY2VCdXNJbnRlZ3JhdGlv",
+            "bhIsLmFwaS5DcmVhdGVBenVyZVNlcnZpY2VCdXNJbnRlZ3JhdGlvblJlcXVl",
+            "c3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiWILT5JMCUiJNL2FwaS9hcHBs",
+            "aWNhdGlvbnMve2ludGVncmF0aW9uLmFwcGxpY2F0aW9uX2lkfS9pbnRlZ3Jh",
+            "dGlvbnMvYXp1cmUtc2VydmljZS1idXM6ASoSwQEKHUdldEF6dXJlU2Vydmlj",
+            "ZUJ1c0ludGVncmF0aW9uEikuYXBpLkdldEF6dXJlU2VydmljZUJ1c0ludGVn",
+            "cmF0aW9uUmVxdWVzdBoqLmFwaS5HZXRBenVyZVNlcnZpY2VCdXNJbnRlZ3Jh",
+            "dGlvblJlc3BvbnNlIkmC0+STAkMSQS9hcGkvYXBwbGljYXRpb25zL3thcHBs",
+            "aWNhdGlvbl9pZH0vaW50ZWdyYXRpb25zL2F6dXJlLXNlcnZpY2UtYnVzEsIB",
+            "CiBVcGRhdGVBenVyZVNlcnZpY2VCdXNJbnRlZ3JhdGlvbhIsLmFwaS5VcGRh",
+            "dGVBenVyZVNlcnZpY2VCdXNJbnRlZ3JhdGlvblJlcXVlc3QaFi5nb29nbGUu",
+            "cHJvdG9idWYuRW1wdHkiWILT5JMCUhpNL2FwaS9hcHBsaWNhdGlvbnMve2lu",
+            "dGVncmF0aW9uLmFwcGxpY2F0aW9uX2lkfS9pbnRlZ3JhdGlvbnMvYXp1cmUt",
+            "c2VydmljZS1idXM6ASoSswEKIERlbGV0ZUF6dXJlU2VydmljZUJ1c0ludGVn",
+            "cmF0aW9uEiwuYXBpLkRlbGV0ZUF6dXJlU2VydmljZUJ1c0ludGVncmF0aW9u",
+            "UmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSJJgtPkkwJDKkEvYXBp",
+            "L2FwcGxpY2F0aW9ucy97YXBwbGljYXRpb25faWR9L2ludGVncmF0aW9ucy9h",
+            "enVyZS1zZXJ2aWNlLWJ1cxK1AQocQ3JlYXRlUGlsb3RUaGluZ3NJbnRlZ3Jh",
+            "dGlvbhIoLmFwaS5DcmVhdGVQaWxvdFRoaW5nc0ludGVncmF0aW9uUmVxdWVz",
+            "dBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSJTgtPkkwJNIkgvYXBpL2FwcGxp",
+            "Y2F0aW9ucy97aW50ZWdyYXRpb24uYXBwbGljYXRpb25faWR9L2ludGVncmF0",
+            "aW9ucy9waWxvdC10aGluZ3M6ASoSsAEKGUdldFBpbG90VGhpbmdzSW50ZWdy",
+            "YXRpb24SJS5hcGkuR2V0UGlsb3RUaGluZ3NJbnRlZ3JhdGlvblJlcXVlc3Qa",
+            "Ji5hcGkuR2V0UGlsb3RUaGluZ3NJbnRlZ3JhdGlvblJlc3BvbnNlIkSC0+ST",
+            "Aj4SPC9hcGkvYXBwbGljYXRpb25zL3thcHBsaWNhdGlvbl9pZH0vaW50ZWdy",
+            "YXRpb25zL3BpbG90LXRoaW5ncxK1AQocVXBkYXRlUGlsb3RUaGluZ3NJbnRl",
+            "Z3JhdGlvbhIoLmFwaS5VcGRhdGVQaWxvdFRoaW5nc0ludGVncmF0aW9uUmVx",
+            "dWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSJTgtPkkwJNGkgvYXBpL2Fw",
+            "cGxpY2F0aW9ucy97aW50ZWdyYXRpb24uYXBwbGljYXRpb25faWR9L2ludGVn",
+            "cmF0aW9ucy9waWxvdC10aGluZ3M6ASoSpgEKHERlbGV0ZVBpbG90VGhpbmdz",
+            "SW50ZWdyYXRpb24SKC5hcGkuRGVsZXRlUGlsb3RUaGluZ3NJbnRlZ3JhdGlv",
+            "blJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiRILT5JMCPio8L2Fw",
+            "aS9hcHBsaWNhdGlvbnMve2FwcGxpY2F0aW9uX2lkfS9pbnRlZ3JhdGlvbnMv",
+            "cGlsb3QtdGhpbmdzEqIBChZDcmVhdGVJZnR0dEludGVncmF0aW9uEiIuYXBp",
+            "LkNyZWF0ZUlmdHR0SW50ZWdyYXRpb25SZXF1ZXN0GhYuZ29vZ2xlLnByb3Rv",
+            "YnVmLkVtcHR5IkyC0+STAkYiQS9hcGkvYXBwbGljYXRpb25zL3tpbnRlZ3Jh",
+            "dGlvbi5hcHBsaWNhdGlvbl9pZH0vaW50ZWdyYXRpb25zL2lmdHR0OgEqEpcB",
+            "ChNHZXRJZnR0dEludGVncmF0aW9uEh8uYXBpLkdldElmdHR0SW50ZWdyYXRp",
+            "b25SZXF1ZXN0GiAuYXBpLkdldElmdHR0SW50ZWdyYXRpb25SZXNwb25zZSI9",
+            "gtPkkwI3EjUvYXBpL2FwcGxpY2F0aW9ucy97YXBwbGljYXRpb25faWR9L2lu",
+            "dGVncmF0aW9ucy9pZnR0dBKiAQoWVXBkYXRlSWZ0dHRJbnRlZ3JhdGlvbhIi",
+            "LmFwaS5VcGRhdGVJZnR0dEludGVncmF0aW9uUmVxdWVzdBoWLmdvb2dsZS5w",
+            "cm90b2J1Zi5FbXB0eSJMgtPkkwJGGkEvYXBpL2FwcGxpY2F0aW9ucy97aW50",
+            "ZWdyYXRpb24uYXBwbGljYXRpb25faWR9L2ludGVncmF0aW9ucy9pZnR0dDoB",
+            "KhKTAQoWRGVsZXRlSWZ0dHRJbnRlZ3JhdGlvbhIiLmFwaS5EZWxldGVJZnR0",
+            "dEludGVncmF0aW9uUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSI9",
+            "gtPkkwI3KjUvYXBpL2FwcGxpY2F0aW9ucy97YXBwbGljYXRpb25faWR9L2lu",
+            "dGVncmF0aW9ucy9pZnR0dBLhAQooR2VuZXJhdGVNcXR0SW50ZWdyYXRpb25D",
+            "bGllbnRDZXJ0aWZpY2F0ZRI0LmFwaS5HZW5lcmF0ZU1xdHRJbnRlZ3JhdGlv",
+            "bkNsaWVudENlcnRpZmljYXRlUmVxdWVzdBo1LmFwaS5HZW5lcmF0ZU1xdHRJ",
+            "bnRlZ3JhdGlvbkNsaWVudENlcnRpZmljYXRlUmVzcG9uc2UiSILT5JMCQiJA",
+            "L2FwaS9hcHBsaWNhdGlvbnMve2FwcGxpY2F0aW9uX2lkfS9pbnRlZ3JhdGlv",
+            "bnMvbXF0dC9jZXJ0aWZpY2F0ZUJoChFpby5jaGlycHN0YWNrLmFwaUIQQXBw",
+            "bGljYXRpb25Qcm90b1ABWi5naXRodWIuY29tL2NoaXJwc3RhY2svY2hpcnBz",
+            "dGFjay9hcGkvZ28vdjQvYXBpqgIOQ2hpcnBzdGFjay5BcGliBnByb3RvMw=="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Api.AnnotationsReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Chirpstack.Api.Encoding), typeof(global::Chirpstack.Api.IntegrationKind), typeof(global::Chirpstack.Api.InfluxDbPrecision), typeof(global::Chirpstack.Api.InfluxDbVersion), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.Application), global::Chirpstack.Api.Application.Parser, new[]{ "Id", "Name", "Description", "TenantId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ApplicationListItem), global::Chirpstack.Api.ApplicationListItem.Parser, new[]{ "Id", "CreatedAt", "UpdatedAt", "Name", "Description" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateApplicationRequest), global::Chirpstack.Api.CreateApplicationRequest.Parser, new[]{ "Application" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateApplicationResponse), global::Chirpstack.Api.CreateApplicationResponse.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetApplicationRequest), global::Chirpstack.Api.GetApplicationRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetApplicationResponse), global::Chirpstack.Api.GetApplicationResponse.Parser, new[]{ "Application", "CreatedAt", "UpdatedAt", "MeasurementKeys" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateApplicationRequest), global::Chirpstack.Api.UpdateApplicationRequest.Parser, new[]{ "Application" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteApplicationRequest), global::Chirpstack.Api.DeleteApplicationRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListApplicationsRequest), global::Chirpstack.Api.ListApplicationsRequest.Parser, new[]{ "Limit", "Offset", "Search", "TenantId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListApplicationsResponse), global::Chirpstack.Api.ListApplicationsResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListIntegrationsRequest), global::Chirpstack.Api.ListIntegrationsRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.IntegrationListItem), global::Chirpstack.Api.IntegrationListItem.Parser, new[]{ "Kind" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListIntegrationsResponse), global::Chirpstack.Api.ListIntegrationsResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.HttpIntegration), global::Chirpstack.Api.HttpIntegration.Parser, new[]{ "ApplicationId", "Headers", "Encoding", "EventEndpointUrl" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateHttpIntegrationRequest), global::Chirpstack.Api.CreateHttpIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetHttpIntegrationRequest), global::Chirpstack.Api.GetHttpIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetHttpIntegrationResponse), global::Chirpstack.Api.GetHttpIntegrationResponse.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateHttpIntegrationRequest), global::Chirpstack.Api.UpdateHttpIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteHttpIntegrationRequest), global::Chirpstack.Api.DeleteHttpIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.InfluxDbIntegration), global::Chirpstack.Api.InfluxDbIntegration.Parser, new[]{ "ApplicationId", "Endpoint", "Db", "Username", "Password", "RetentionPolicyName", "Precision", "Version", "Token", "Organization", "Bucket" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateInfluxDbIntegrationRequest), global::Chirpstack.Api.CreateInfluxDbIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetInfluxDbIntegrationRequest), global::Chirpstack.Api.GetInfluxDbIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetInfluxDbIntegrationResponse), global::Chirpstack.Api.GetInfluxDbIntegrationResponse.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest), global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest), global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ThingsBoardIntegration), global::Chirpstack.Api.ThingsBoardIntegration.Parser, new[]{ "ApplicationId", "Server" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateThingsBoardIntegrationRequest), global::Chirpstack.Api.CreateThingsBoardIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetThingsBoardIntegrationRequest), global::Chirpstack.Api.GetThingsBoardIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetThingsBoardIntegrationResponse), global::Chirpstack.Api.GetThingsBoardIntegrationResponse.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest), global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest), global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.MyDevicesIntegration), global::Chirpstack.Api.MyDevicesIntegration.Parser, new[]{ "ApplicationId", "Endpoint" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateMyDevicesIntegrationRequest), global::Chirpstack.Api.CreateMyDevicesIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetMyDevicesIntegrationRequest), global::Chirpstack.Api.GetMyDevicesIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetMyDevicesIntegrationResponse), global::Chirpstack.Api.GetMyDevicesIntegrationResponse.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest), global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest), global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.LoraCloudIntegration), global::Chirpstack.Api.LoraCloudIntegration.Parser, new[]{ "ApplicationId", "ModemGeolocationServices" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.LoraCloudModemGeolocationServices), global::Chirpstack.Api.LoraCloudModemGeolocationServices.Parser, new[]{ "Token", "ModemEnabled", "ModemPort", "GnssPort", "GnssUseRxTime", "ParseTlv", "GeolocationBufferTtl", "GeolocationMinBufferSize", "GeolocationTdoa", "GeolocationRssi", "GeolocationGnss", "GeolocationGnssPayloadField", "GeolocationGnssUseRxTime", "GeolocationWifi", "GeolocationWifiPayloadField" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateLoraCloudIntegrationRequest), global::Chirpstack.Api.CreateLoraCloudIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetLoraCloudIntegrationRequest), global::Chirpstack.Api.GetLoraCloudIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetLoraCloudIntegrationResponse), global::Chirpstack.Api.GetLoraCloudIntegrationResponse.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest), global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest), global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GcpPubSubIntegration), global::Chirpstack.Api.GcpPubSubIntegration.Parser, new[]{ "ApplicationId", "Encoding", "CredentialsFile", "ProjectId", "TopicName" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest), global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetGcpPubSubIntegrationRequest), global::Chirpstack.Api.GetGcpPubSubIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetGcpPubSubIntegrationResponse), global::Chirpstack.Api.GetGcpPubSubIntegrationResponse.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest), global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest), global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.AwsSnsIntegration), global::Chirpstack.Api.AwsSnsIntegration.Parser, new[]{ "ApplicationId", "Encoding", "Region", "AccessKeyId", "SecretAccessKey", "TopicArn" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateAwsSnsIntegrationRequest), global::Chirpstack.Api.CreateAwsSnsIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetAwsSnsIntegrationRequest), global::Chirpstack.Api.GetAwsSnsIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetAwsSnsIntegrationResponse), global::Chirpstack.Api.GetAwsSnsIntegrationResponse.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest), global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest), global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.AzureServiceBusIntegration), global::Chirpstack.Api.AzureServiceBusIntegration.Parser, new[]{ "ApplicationId", "Encoding", "ConnectionString", "PublishName" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest), global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest), global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse), global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest), global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest), global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.PilotThingsIntegration), global::Chirpstack.Api.PilotThingsIntegration.Parser, new[]{ "ApplicationId", "Server", "Token" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreatePilotThingsIntegrationRequest), global::Chirpstack.Api.CreatePilotThingsIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetPilotThingsIntegrationRequest), global::Chirpstack.Api.GetPilotThingsIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetPilotThingsIntegrationResponse), global::Chirpstack.Api.GetPilotThingsIntegrationResponse.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest), global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeletePilotThingsIntegrationRequest), global::Chirpstack.Api.DeletePilotThingsIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.IftttIntegration), global::Chirpstack.Api.IftttIntegration.Parser, new[]{ "ApplicationId", "Key", "UplinkValues" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateIftttIntegrationRequest), global::Chirpstack.Api.CreateIftttIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetIftttIntegrationRequest), global::Chirpstack.Api.GetIftttIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetIftttIntegrationResponse), global::Chirpstack.Api.GetIftttIntegrationResponse.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateIftttIntegrationRequest), global::Chirpstack.Api.UpdateIftttIntegrationRequest.Parser, new[]{ "Integration" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteIftttIntegrationRequest), global::Chirpstack.Api.DeleteIftttIntegrationRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest), global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest.Parser, new[]{ "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse), global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse.Parser, new[]{ "TlsCert", "TlsKey", "CaCert", "ExpiresAt" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Enums
+  public enum Encoding {
+    [pbr::OriginalName("JSON")] Json = 0,
+    [pbr::OriginalName("PROTOBUF")] Protobuf = 1,
+  }
+
+  public enum IntegrationKind {
+    [pbr::OriginalName("HTTP")] Http = 0,
+    [pbr::OriginalName("INFLUX_DB")] InfluxDb = 1,
+    [pbr::OriginalName("THINGS_BOARD")] ThingsBoard = 2,
+    [pbr::OriginalName("MY_DEVICES")] MyDevices = 3,
+    [pbr::OriginalName("LORA_CLOUD")] LoraCloud = 4,
+    [pbr::OriginalName("GCP_PUB_SUB")] GcpPubSub = 5,
+    [pbr::OriginalName("AWS_SNS")] AwsSns = 6,
+    [pbr::OriginalName("AZURE_SERVICE_BUS")] AzureServiceBus = 7,
+    [pbr::OriginalName("PILOT_THINGS")] PilotThings = 8,
+    [pbr::OriginalName("MQTT_GLOBAL")] MqttGlobal = 9,
+    [pbr::OriginalName("IFTTT")] Ifttt = 10,
+  }
+
+  public enum InfluxDbPrecision {
+    [pbr::OriginalName("NS")] Ns = 0,
+    [pbr::OriginalName("U")] U = 1,
+    [pbr::OriginalName("MS")] Ms = 2,
+    [pbr::OriginalName("S")] S = 3,
+    [pbr::OriginalName("M")] M = 4,
+    [pbr::OriginalName("H")] H = 5,
+  }
+
+  public enum InfluxDbVersion {
+    [pbr::OriginalName("INFLUXDB_1")] Influxdb1 = 0,
+    [pbr::OriginalName("INFLUXDB_2")] Influxdb2 = 1,
+  }
+
+  #endregion
+
+  #region Messages
+  public sealed partial class Application : pb::IMessage<Application>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<Application> _parser = new pb::MessageParser<Application>(() => new Application());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<Application> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Application() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Application(Application other) : this() {
+      id_ = other.id_;
+      name_ = other.name_;
+      description_ = other.description_;
+      tenantId_ = other.tenantId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Application Clone() {
+      return new Application(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// Note: on create this will be automatically generated.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 2;
+    private string name_ = "";
+    /// <summary>
+    /// Application name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 3;
+    private string description_ = "";
+    /// <summary>
+    /// Application description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 4;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as Application);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(Application other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (Name != other.Name) return false;
+      if (Description != other.Description) return false;
+      if (TenantId != other.TenantId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(Application other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ApplicationListItem : pb::IMessage<ApplicationListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ApplicationListItem> _parser = new pb::MessageParser<ApplicationListItem>(() => new ApplicationListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ApplicationListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ApplicationListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ApplicationListItem(ApplicationListItem other) : this() {
+      id_ = other.id_;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      name_ = other.name_;
+      description_ = other.description_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ApplicationListItem Clone() {
+      return new ApplicationListItem(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 4;
+    private string name_ = "";
+    /// <summary>
+    /// Application name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 5;
+    private string description_ = "";
+    /// <summary>
+    /// Application description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ApplicationListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ApplicationListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (Name != other.Name) return false;
+      if (Description != other.Description) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Description);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Description);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ApplicationListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Name = input.ReadString();
+            break;
+          }
+          case 42: {
+            Description = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Name = input.ReadString();
+            break;
+          }
+          case 42: {
+            Description = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateApplicationRequest : pb::IMessage<CreateApplicationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateApplicationRequest> _parser = new pb::MessageParser<CreateApplicationRequest>(() => new CreateApplicationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateApplicationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApplicationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApplicationRequest(CreateApplicationRequest other) : this() {
+      application_ = other.application_ != null ? other.application_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApplicationRequest Clone() {
+      return new CreateApplicationRequest(this);
+    }
+
+    /// <summary>Field number for the "application" field.</summary>
+    public const int ApplicationFieldNumber = 1;
+    private global::Chirpstack.Api.Application application_;
+    /// <summary>
+    /// Application object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Application Application {
+      get { return application_; }
+      set {
+        application_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateApplicationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateApplicationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Application, other.Application)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (application_ != null) hash ^= Application.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (application_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Application);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (application_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Application);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (application_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Application);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateApplicationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.application_ != null) {
+        if (application_ == null) {
+          Application = new global::Chirpstack.Api.Application();
+        }
+        Application.MergeFrom(other.Application);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (application_ == null) {
+              Application = new global::Chirpstack.Api.Application();
+            }
+            input.ReadMessage(Application);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (application_ == null) {
+              Application = new global::Chirpstack.Api.Application();
+            }
+            input.ReadMessage(Application);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateApplicationResponse : pb::IMessage<CreateApplicationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateApplicationResponse> _parser = new pb::MessageParser<CreateApplicationResponse>(() => new CreateApplicationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateApplicationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApplicationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApplicationResponse(CreateApplicationResponse other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApplicationResponse Clone() {
+      return new CreateApplicationResponse(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateApplicationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateApplicationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateApplicationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetApplicationRequest : pb::IMessage<GetApplicationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetApplicationRequest> _parser = new pb::MessageParser<GetApplicationRequest>(() => new GetApplicationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetApplicationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetApplicationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetApplicationRequest(GetApplicationRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetApplicationRequest Clone() {
+      return new GetApplicationRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetApplicationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetApplicationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetApplicationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetApplicationResponse : pb::IMessage<GetApplicationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetApplicationResponse> _parser = new pb::MessageParser<GetApplicationResponse>(() => new GetApplicationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetApplicationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetApplicationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetApplicationResponse(GetApplicationResponse other) : this() {
+      application_ = other.application_ != null ? other.application_.Clone() : null;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      measurementKeys_ = other.measurementKeys_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetApplicationResponse Clone() {
+      return new GetApplicationResponse(this);
+    }
+
+    /// <summary>Field number for the "application" field.</summary>
+    public const int ApplicationFieldNumber = 1;
+    private global::Chirpstack.Api.Application application_;
+    /// <summary>
+    /// Application object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Application Application {
+      get { return application_; }
+      set {
+        application_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "measurement_keys" field.</summary>
+    public const int MeasurementKeysFieldNumber = 4;
+    private static readonly pb::FieldCodec<string> _repeated_measurementKeys_codec
+        = pb::FieldCodec.ForString(34);
+    private readonly pbc::RepeatedField<string> measurementKeys_ = new pbc::RepeatedField<string>();
+    /// <summary>
+    /// Measurement keys.
+    /// This contains the measurement keys from all the device-profiles that
+    /// are used by the devices under this application.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<string> MeasurementKeys {
+      get { return measurementKeys_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetApplicationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetApplicationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Application, other.Application)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if(!measurementKeys_.Equals(other.measurementKeys_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (application_ != null) hash ^= Application.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      hash ^= measurementKeys_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (application_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Application);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      measurementKeys_.WriteTo(output, _repeated_measurementKeys_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (application_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Application);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      measurementKeys_.WriteTo(ref output, _repeated_measurementKeys_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (application_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Application);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      size += measurementKeys_.CalculateSize(_repeated_measurementKeys_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetApplicationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.application_ != null) {
+        if (application_ == null) {
+          Application = new global::Chirpstack.Api.Application();
+        }
+        Application.MergeFrom(other.Application);
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      measurementKeys_.Add(other.measurementKeys_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (application_ == null) {
+              Application = new global::Chirpstack.Api.Application();
+            }
+            input.ReadMessage(Application);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            measurementKeys_.AddEntriesFrom(input, _repeated_measurementKeys_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (application_ == null) {
+              Application = new global::Chirpstack.Api.Application();
+            }
+            input.ReadMessage(Application);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            measurementKeys_.AddEntriesFrom(ref input, _repeated_measurementKeys_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateApplicationRequest : pb::IMessage<UpdateApplicationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateApplicationRequest> _parser = new pb::MessageParser<UpdateApplicationRequest>(() => new UpdateApplicationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateApplicationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateApplicationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateApplicationRequest(UpdateApplicationRequest other) : this() {
+      application_ = other.application_ != null ? other.application_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateApplicationRequest Clone() {
+      return new UpdateApplicationRequest(this);
+    }
+
+    /// <summary>Field number for the "application" field.</summary>
+    public const int ApplicationFieldNumber = 1;
+    private global::Chirpstack.Api.Application application_;
+    /// <summary>
+    /// Application object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Application Application {
+      get { return application_; }
+      set {
+        application_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateApplicationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateApplicationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Application, other.Application)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (application_ != null) hash ^= Application.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (application_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Application);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (application_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Application);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (application_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Application);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateApplicationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.application_ != null) {
+        if (application_ == null) {
+          Application = new global::Chirpstack.Api.Application();
+        }
+        Application.MergeFrom(other.Application);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (application_ == null) {
+              Application = new global::Chirpstack.Api.Application();
+            }
+            input.ReadMessage(Application);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (application_ == null) {
+              Application = new global::Chirpstack.Api.Application();
+            }
+            input.ReadMessage(Application);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteApplicationRequest : pb::IMessage<DeleteApplicationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteApplicationRequest> _parser = new pb::MessageParser<DeleteApplicationRequest>(() => new DeleteApplicationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteApplicationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteApplicationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteApplicationRequest(DeleteApplicationRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteApplicationRequest Clone() {
+      return new DeleteApplicationRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteApplicationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteApplicationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteApplicationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListApplicationsRequest : pb::IMessage<ListApplicationsRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListApplicationsRequest> _parser = new pb::MessageParser<ListApplicationsRequest>(() => new ListApplicationsRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListApplicationsRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApplicationsRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApplicationsRequest(ListApplicationsRequest other) : this() {
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      search_ = other.search_;
+      tenantId_ = other.tenantId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApplicationsRequest Clone() {
+      return new ListApplicationsRequest(this);
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 1;
+    private uint limit_;
+    /// <summary>
+    /// Max number of applications to return in the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 2;
+    private uint offset_;
+    /// <summary>
+    /// Offset in the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "search" field.</summary>
+    public const int SearchFieldNumber = 3;
+    private string search_ = "";
+    /// <summary>
+    /// If set, the given string will be used to search on name (optional).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Search {
+      get { return search_; }
+      set {
+        search_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 4;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID to list the applications for.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListApplicationsRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListApplicationsRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      if (Search != other.Search) return false;
+      if (TenantId != other.TenantId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Limit != 0) hash ^= Limit.GetHashCode();
+      if (Offset != 0) hash ^= Offset.GetHashCode();
+      if (Search.Length != 0) hash ^= Search.GetHashCode();
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Limit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Limit);
+      }
+      if (Offset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Offset);
+      }
+      if (Search.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Search);
+      }
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListApplicationsRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Limit != 0) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0) {
+        Offset = other.Offset;
+      }
+      if (other.Search.Length != 0) {
+        Search = other.Search;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListApplicationsResponse : pb::IMessage<ListApplicationsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListApplicationsResponse> _parser = new pb::MessageParser<ListApplicationsResponse>(() => new ListApplicationsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListApplicationsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApplicationsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApplicationsResponse(ListApplicationsResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApplicationsResponse Clone() {
+      return new ListApplicationsResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of applications.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.ApplicationListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.ApplicationListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.ApplicationListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.ApplicationListItem>();
+    /// <summary>
+    /// Result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.ApplicationListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListApplicationsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListApplicationsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListApplicationsResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListIntegrationsRequest : pb::IMessage<ListIntegrationsRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListIntegrationsRequest> _parser = new pb::MessageParser<ListIntegrationsRequest>(() => new ListIntegrationsRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListIntegrationsRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[10]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListIntegrationsRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListIntegrationsRequest(ListIntegrationsRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListIntegrationsRequest Clone() {
+      return new ListIntegrationsRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListIntegrationsRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListIntegrationsRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListIntegrationsRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class IntegrationListItem : pb::IMessage<IntegrationListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<IntegrationListItem> _parser = new pb::MessageParser<IntegrationListItem>(() => new IntegrationListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<IntegrationListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[11]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public IntegrationListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public IntegrationListItem(IntegrationListItem other) : this() {
+      kind_ = other.kind_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public IntegrationListItem Clone() {
+      return new IntegrationListItem(this);
+    }
+
+    /// <summary>Field number for the "kind" field.</summary>
+    public const int KindFieldNumber = 1;
+    private global::Chirpstack.Api.IntegrationKind kind_ = global::Chirpstack.Api.IntegrationKind.Http;
+    /// <summary>
+    /// Integration kind.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.IntegrationKind Kind {
+      get { return kind_; }
+      set {
+        kind_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as IntegrationListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(IntegrationListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Kind != other.Kind) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Kind != global::Chirpstack.Api.IntegrationKind.Http) hash ^= Kind.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Kind != global::Chirpstack.Api.IntegrationKind.Http) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) Kind);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Kind != global::Chirpstack.Api.IntegrationKind.Http) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) Kind);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Kind != global::Chirpstack.Api.IntegrationKind.Http) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Kind);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(IntegrationListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Kind != global::Chirpstack.Api.IntegrationKind.Http) {
+        Kind = other.Kind;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Kind = (global::Chirpstack.Api.IntegrationKind) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Kind = (global::Chirpstack.Api.IntegrationKind) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListIntegrationsResponse : pb::IMessage<ListIntegrationsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListIntegrationsResponse> _parser = new pb::MessageParser<ListIntegrationsResponse>(() => new ListIntegrationsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListIntegrationsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[12]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListIntegrationsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListIntegrationsResponse(ListIntegrationsResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListIntegrationsResponse Clone() {
+      return new ListIntegrationsResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of integrations available within the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.IntegrationListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.IntegrationListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.IntegrationListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.IntegrationListItem>();
+    /// <summary>
+    /// Integrations within result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.IntegrationListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListIntegrationsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListIntegrationsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListIntegrationsResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class HttpIntegration : pb::IMessage<HttpIntegration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<HttpIntegration> _parser = new pb::MessageParser<HttpIntegration>(() => new HttpIntegration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<HttpIntegration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[13]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public HttpIntegration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public HttpIntegration(HttpIntegration other) : this() {
+      applicationId_ = other.applicationId_;
+      headers_ = other.headers_.Clone();
+      encoding_ = other.encoding_;
+      eventEndpointUrl_ = other.eventEndpointUrl_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public HttpIntegration Clone() {
+      return new HttpIntegration(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "headers" field.</summary>
+    public const int HeadersFieldNumber = 2;
+    private static readonly pbc::MapField<string, string>.Codec _map_headers_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 18);
+    private readonly pbc::MapField<string, string> headers_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// HTTP headers to set when making requests.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Headers {
+      get { return headers_; }
+    }
+
+    /// <summary>Field number for the "encoding" field.</summary>
+    public const int EncodingFieldNumber = 3;
+    private global::Chirpstack.Api.Encoding encoding_ = global::Chirpstack.Api.Encoding.Json;
+    /// <summary>
+    /// Payload encoding.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Encoding Encoding {
+      get { return encoding_; }
+      set {
+        encoding_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "event_endpoint_url" field.</summary>
+    public const int EventEndpointUrlFieldNumber = 4;
+    private string eventEndpointUrl_ = "";
+    /// <summary>
+    /// Event endpoint URL.
+    /// The HTTP integration will POST all events to this enpoint. The request
+    /// will contain a query parameters "event" containing the type of the
+    /// event.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string EventEndpointUrl {
+      get { return eventEndpointUrl_; }
+      set {
+        eventEndpointUrl_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as HttpIntegration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(HttpIntegration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      if (!Headers.Equals(other.Headers)) return false;
+      if (Encoding != other.Encoding) return false;
+      if (EventEndpointUrl != other.EventEndpointUrl) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      hash ^= Headers.GetHashCode();
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) hash ^= Encoding.GetHashCode();
+      if (EventEndpointUrl.Length != 0) hash ^= EventEndpointUrl.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      headers_.WriteTo(output, _map_headers_codec);
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        output.WriteRawTag(24);
+        output.WriteEnum((int) Encoding);
+      }
+      if (EventEndpointUrl.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(EventEndpointUrl);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      headers_.WriteTo(ref output, _map_headers_codec);
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        output.WriteRawTag(24);
+        output.WriteEnum((int) Encoding);
+      }
+      if (EventEndpointUrl.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(EventEndpointUrl);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      size += headers_.CalculateSize(_map_headers_codec);
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Encoding);
+      }
+      if (EventEndpointUrl.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(EventEndpointUrl);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(HttpIntegration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      headers_.Add(other.headers_);
+      if (other.Encoding != global::Chirpstack.Api.Encoding.Json) {
+        Encoding = other.Encoding;
+      }
+      if (other.EventEndpointUrl.Length != 0) {
+        EventEndpointUrl = other.EventEndpointUrl;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            headers_.AddEntriesFrom(input, _map_headers_codec);
+            break;
+          }
+          case 24: {
+            Encoding = (global::Chirpstack.Api.Encoding) input.ReadEnum();
+            break;
+          }
+          case 34: {
+            EventEndpointUrl = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            headers_.AddEntriesFrom(ref input, _map_headers_codec);
+            break;
+          }
+          case 24: {
+            Encoding = (global::Chirpstack.Api.Encoding) input.ReadEnum();
+            break;
+          }
+          case 34: {
+            EventEndpointUrl = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateHttpIntegrationRequest : pb::IMessage<CreateHttpIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateHttpIntegrationRequest> _parser = new pb::MessageParser<CreateHttpIntegrationRequest>(() => new CreateHttpIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateHttpIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[14]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateHttpIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateHttpIntegrationRequest(CreateHttpIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateHttpIntegrationRequest Clone() {
+      return new CreateHttpIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.HttpIntegration integration_;
+    /// <summary>
+    /// Integration object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.HttpIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateHttpIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateHttpIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateHttpIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.HttpIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.HttpIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.HttpIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetHttpIntegrationRequest : pb::IMessage<GetHttpIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetHttpIntegrationRequest> _parser = new pb::MessageParser<GetHttpIntegrationRequest>(() => new GetHttpIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetHttpIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[15]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetHttpIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetHttpIntegrationRequest(GetHttpIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetHttpIntegrationRequest Clone() {
+      return new GetHttpIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetHttpIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetHttpIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetHttpIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetHttpIntegrationResponse : pb::IMessage<GetHttpIntegrationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetHttpIntegrationResponse> _parser = new pb::MessageParser<GetHttpIntegrationResponse>(() => new GetHttpIntegrationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetHttpIntegrationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[16]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetHttpIntegrationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetHttpIntegrationResponse(GetHttpIntegrationResponse other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetHttpIntegrationResponse Clone() {
+      return new GetHttpIntegrationResponse(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.HttpIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.HttpIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetHttpIntegrationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetHttpIntegrationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetHttpIntegrationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.HttpIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.HttpIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.HttpIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateHttpIntegrationRequest : pb::IMessage<UpdateHttpIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateHttpIntegrationRequest> _parser = new pb::MessageParser<UpdateHttpIntegrationRequest>(() => new UpdateHttpIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateHttpIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[17]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateHttpIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateHttpIntegrationRequest(UpdateHttpIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateHttpIntegrationRequest Clone() {
+      return new UpdateHttpIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.HttpIntegration integration_;
+    /// <summary>
+    /// Integration object to update.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.HttpIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateHttpIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateHttpIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateHttpIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.HttpIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.HttpIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.HttpIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteHttpIntegrationRequest : pb::IMessage<DeleteHttpIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteHttpIntegrationRequest> _parser = new pb::MessageParser<DeleteHttpIntegrationRequest>(() => new DeleteHttpIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteHttpIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[18]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteHttpIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteHttpIntegrationRequest(DeleteHttpIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteHttpIntegrationRequest Clone() {
+      return new DeleteHttpIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteHttpIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteHttpIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteHttpIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class InfluxDbIntegration : pb::IMessage<InfluxDbIntegration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<InfluxDbIntegration> _parser = new pb::MessageParser<InfluxDbIntegration>(() => new InfluxDbIntegration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<InfluxDbIntegration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[19]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public InfluxDbIntegration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public InfluxDbIntegration(InfluxDbIntegration other) : this() {
+      applicationId_ = other.applicationId_;
+      endpoint_ = other.endpoint_;
+      db_ = other.db_;
+      username_ = other.username_;
+      password_ = other.password_;
+      retentionPolicyName_ = other.retentionPolicyName_;
+      precision_ = other.precision_;
+      version_ = other.version_;
+      token_ = other.token_;
+      organization_ = other.organization_;
+      bucket_ = other.bucket_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public InfluxDbIntegration Clone() {
+      return new InfluxDbIntegration(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "endpoint" field.</summary>
+    public const int EndpointFieldNumber = 2;
+    private string endpoint_ = "";
+    /// <summary>
+    /// InfluxDb API write endpoint (e.g. http://localhost:8086/write).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Endpoint {
+      get { return endpoint_; }
+      set {
+        endpoint_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "db" field.</summary>
+    public const int DbFieldNumber = 3;
+    private string db_ = "";
+    /// <summary>
+    /// InfluxDb database name. (InfluxDb v1)
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Db {
+      get { return db_; }
+      set {
+        db_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "username" field.</summary>
+    public const int UsernameFieldNumber = 4;
+    private string username_ = "";
+    /// <summary>
+    /// InfluxDb username. (InfluxDb v1)
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Username {
+      get { return username_; }
+      set {
+        username_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "password" field.</summary>
+    public const int PasswordFieldNumber = 5;
+    private string password_ = "";
+    /// <summary>
+    /// InfluxDb password. (InfluxDb v1)
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Password {
+      get { return password_; }
+      set {
+        password_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "retention_policy_name" field.</summary>
+    public const int RetentionPolicyNameFieldNumber = 6;
+    private string retentionPolicyName_ = "";
+    /// <summary>
+    /// InfluxDb retention policy name. (InfluxDb v1)
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string RetentionPolicyName {
+      get { return retentionPolicyName_; }
+      set {
+        retentionPolicyName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "precision" field.</summary>
+    public const int PrecisionFieldNumber = 7;
+    private global::Chirpstack.Api.InfluxDbPrecision precision_ = global::Chirpstack.Api.InfluxDbPrecision.Ns;
+    /// <summary>
+    /// InfluxDb timestamp precision (InfluxDb v1).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.InfluxDbPrecision Precision {
+      get { return precision_; }
+      set {
+        precision_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "version" field.</summary>
+    public const int VersionFieldNumber = 8;
+    private global::Chirpstack.Api.InfluxDbVersion version_ = global::Chirpstack.Api.InfluxDbVersion.Influxdb1;
+    /// <summary>
+    /// InfluxDb version.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.InfluxDbVersion Version {
+      get { return version_; }
+      set {
+        version_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "token" field.</summary>
+    public const int TokenFieldNumber = 9;
+    private string token_ = "";
+    /// <summary>
+    /// Token. (InfluxDb v2)
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Token {
+      get { return token_; }
+      set {
+        token_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "organization" field.</summary>
+    public const int OrganizationFieldNumber = 10;
+    private string organization_ = "";
+    /// <summary>
+    /// Organization. (InfluxDb v2)
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Organization {
+      get { return organization_; }
+      set {
+        organization_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "bucket" field.</summary>
+    public const int BucketFieldNumber = 11;
+    private string bucket_ = "";
+    /// <summary>
+    /// Bucket. (InfluxDb v2)
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Bucket {
+      get { return bucket_; }
+      set {
+        bucket_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as InfluxDbIntegration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(InfluxDbIntegration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      if (Endpoint != other.Endpoint) return false;
+      if (Db != other.Db) return false;
+      if (Username != other.Username) return false;
+      if (Password != other.Password) return false;
+      if (RetentionPolicyName != other.RetentionPolicyName) return false;
+      if (Precision != other.Precision) return false;
+      if (Version != other.Version) return false;
+      if (Token != other.Token) return false;
+      if (Organization != other.Organization) return false;
+      if (Bucket != other.Bucket) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (Endpoint.Length != 0) hash ^= Endpoint.GetHashCode();
+      if (Db.Length != 0) hash ^= Db.GetHashCode();
+      if (Username.Length != 0) hash ^= Username.GetHashCode();
+      if (Password.Length != 0) hash ^= Password.GetHashCode();
+      if (RetentionPolicyName.Length != 0) hash ^= RetentionPolicyName.GetHashCode();
+      if (Precision != global::Chirpstack.Api.InfluxDbPrecision.Ns) hash ^= Precision.GetHashCode();
+      if (Version != global::Chirpstack.Api.InfluxDbVersion.Influxdb1) hash ^= Version.GetHashCode();
+      if (Token.Length != 0) hash ^= Token.GetHashCode();
+      if (Organization.Length != 0) hash ^= Organization.GetHashCode();
+      if (Bucket.Length != 0) hash ^= Bucket.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Endpoint.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Endpoint);
+      }
+      if (Db.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Db);
+      }
+      if (Username.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Username);
+      }
+      if (Password.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Password);
+      }
+      if (RetentionPolicyName.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(RetentionPolicyName);
+      }
+      if (Precision != global::Chirpstack.Api.InfluxDbPrecision.Ns) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) Precision);
+      }
+      if (Version != global::Chirpstack.Api.InfluxDbVersion.Influxdb1) {
+        output.WriteRawTag(64);
+        output.WriteEnum((int) Version);
+      }
+      if (Token.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(Token);
+      }
+      if (Organization.Length != 0) {
+        output.WriteRawTag(82);
+        output.WriteString(Organization);
+      }
+      if (Bucket.Length != 0) {
+        output.WriteRawTag(90);
+        output.WriteString(Bucket);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Endpoint.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Endpoint);
+      }
+      if (Db.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Db);
+      }
+      if (Username.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Username);
+      }
+      if (Password.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Password);
+      }
+      if (RetentionPolicyName.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(RetentionPolicyName);
+      }
+      if (Precision != global::Chirpstack.Api.InfluxDbPrecision.Ns) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) Precision);
+      }
+      if (Version != global::Chirpstack.Api.InfluxDbVersion.Influxdb1) {
+        output.WriteRawTag(64);
+        output.WriteEnum((int) Version);
+      }
+      if (Token.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(Token);
+      }
+      if (Organization.Length != 0) {
+        output.WriteRawTag(82);
+        output.WriteString(Organization);
+      }
+      if (Bucket.Length != 0) {
+        output.WriteRawTag(90);
+        output.WriteString(Bucket);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (Endpoint.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Endpoint);
+      }
+      if (Db.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Db);
+      }
+      if (Username.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Username);
+      }
+      if (Password.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Password);
+      }
+      if (RetentionPolicyName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(RetentionPolicyName);
+      }
+      if (Precision != global::Chirpstack.Api.InfluxDbPrecision.Ns) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Precision);
+      }
+      if (Version != global::Chirpstack.Api.InfluxDbVersion.Influxdb1) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Version);
+      }
+      if (Token.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Token);
+      }
+      if (Organization.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Organization);
+      }
+      if (Bucket.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Bucket);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(InfluxDbIntegration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.Endpoint.Length != 0) {
+        Endpoint = other.Endpoint;
+      }
+      if (other.Db.Length != 0) {
+        Db = other.Db;
+      }
+      if (other.Username.Length != 0) {
+        Username = other.Username;
+      }
+      if (other.Password.Length != 0) {
+        Password = other.Password;
+      }
+      if (other.RetentionPolicyName.Length != 0) {
+        RetentionPolicyName = other.RetentionPolicyName;
+      }
+      if (other.Precision != global::Chirpstack.Api.InfluxDbPrecision.Ns) {
+        Precision = other.Precision;
+      }
+      if (other.Version != global::Chirpstack.Api.InfluxDbVersion.Influxdb1) {
+        Version = other.Version;
+      }
+      if (other.Token.Length != 0) {
+        Token = other.Token;
+      }
+      if (other.Organization.Length != 0) {
+        Organization = other.Organization;
+      }
+      if (other.Bucket.Length != 0) {
+        Bucket = other.Bucket;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Endpoint = input.ReadString();
+            break;
+          }
+          case 26: {
+            Db = input.ReadString();
+            break;
+          }
+          case 34: {
+            Username = input.ReadString();
+            break;
+          }
+          case 42: {
+            Password = input.ReadString();
+            break;
+          }
+          case 50: {
+            RetentionPolicyName = input.ReadString();
+            break;
+          }
+          case 56: {
+            Precision = (global::Chirpstack.Api.InfluxDbPrecision) input.ReadEnum();
+            break;
+          }
+          case 64: {
+            Version = (global::Chirpstack.Api.InfluxDbVersion) input.ReadEnum();
+            break;
+          }
+          case 74: {
+            Token = input.ReadString();
+            break;
+          }
+          case 82: {
+            Organization = input.ReadString();
+            break;
+          }
+          case 90: {
+            Bucket = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Endpoint = input.ReadString();
+            break;
+          }
+          case 26: {
+            Db = input.ReadString();
+            break;
+          }
+          case 34: {
+            Username = input.ReadString();
+            break;
+          }
+          case 42: {
+            Password = input.ReadString();
+            break;
+          }
+          case 50: {
+            RetentionPolicyName = input.ReadString();
+            break;
+          }
+          case 56: {
+            Precision = (global::Chirpstack.Api.InfluxDbPrecision) input.ReadEnum();
+            break;
+          }
+          case 64: {
+            Version = (global::Chirpstack.Api.InfluxDbVersion) input.ReadEnum();
+            break;
+          }
+          case 74: {
+            Token = input.ReadString();
+            break;
+          }
+          case 82: {
+            Organization = input.ReadString();
+            break;
+          }
+          case 90: {
+            Bucket = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateInfluxDbIntegrationRequest : pb::IMessage<CreateInfluxDbIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateInfluxDbIntegrationRequest> _parser = new pb::MessageParser<CreateInfluxDbIntegrationRequest>(() => new CreateInfluxDbIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateInfluxDbIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[20]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateInfluxDbIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateInfluxDbIntegrationRequest(CreateInfluxDbIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateInfluxDbIntegrationRequest Clone() {
+      return new CreateInfluxDbIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.InfluxDbIntegration integration_;
+    /// <summary>
+    /// Integration object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.InfluxDbIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateInfluxDbIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateInfluxDbIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateInfluxDbIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.InfluxDbIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.InfluxDbIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.InfluxDbIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetInfluxDbIntegrationRequest : pb::IMessage<GetInfluxDbIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetInfluxDbIntegrationRequest> _parser = new pb::MessageParser<GetInfluxDbIntegrationRequest>(() => new GetInfluxDbIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetInfluxDbIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[21]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetInfluxDbIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetInfluxDbIntegrationRequest(GetInfluxDbIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetInfluxDbIntegrationRequest Clone() {
+      return new GetInfluxDbIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetInfluxDbIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetInfluxDbIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetInfluxDbIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetInfluxDbIntegrationResponse : pb::IMessage<GetInfluxDbIntegrationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetInfluxDbIntegrationResponse> _parser = new pb::MessageParser<GetInfluxDbIntegrationResponse>(() => new GetInfluxDbIntegrationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetInfluxDbIntegrationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[22]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetInfluxDbIntegrationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetInfluxDbIntegrationResponse(GetInfluxDbIntegrationResponse other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetInfluxDbIntegrationResponse Clone() {
+      return new GetInfluxDbIntegrationResponse(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.InfluxDbIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.InfluxDbIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetInfluxDbIntegrationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetInfluxDbIntegrationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetInfluxDbIntegrationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.InfluxDbIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.InfluxDbIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.InfluxDbIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateInfluxDbIntegrationRequest : pb::IMessage<UpdateInfluxDbIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateInfluxDbIntegrationRequest> _parser = new pb::MessageParser<UpdateInfluxDbIntegrationRequest>(() => new UpdateInfluxDbIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateInfluxDbIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[23]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateInfluxDbIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateInfluxDbIntegrationRequest(UpdateInfluxDbIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateInfluxDbIntegrationRequest Clone() {
+      return new UpdateInfluxDbIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.InfluxDbIntegration integration_;
+    /// <summary>
+    /// Integration object to update. 
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.InfluxDbIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateInfluxDbIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateInfluxDbIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateInfluxDbIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.InfluxDbIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.InfluxDbIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.InfluxDbIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteInfluxDbIntegrationRequest : pb::IMessage<DeleteInfluxDbIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteInfluxDbIntegrationRequest> _parser = new pb::MessageParser<DeleteInfluxDbIntegrationRequest>(() => new DeleteInfluxDbIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteInfluxDbIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[24]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteInfluxDbIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteInfluxDbIntegrationRequest(DeleteInfluxDbIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteInfluxDbIntegrationRequest Clone() {
+      return new DeleteInfluxDbIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteInfluxDbIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteInfluxDbIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteInfluxDbIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ThingsBoardIntegration : pb::IMessage<ThingsBoardIntegration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ThingsBoardIntegration> _parser = new pb::MessageParser<ThingsBoardIntegration>(() => new ThingsBoardIntegration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ThingsBoardIntegration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[25]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ThingsBoardIntegration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ThingsBoardIntegration(ThingsBoardIntegration other) : this() {
+      applicationId_ = other.applicationId_;
+      server_ = other.server_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ThingsBoardIntegration Clone() {
+      return new ThingsBoardIntegration(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "server" field.</summary>
+    public const int ServerFieldNumber = 2;
+    private string server_ = "";
+    /// <summary>
+    /// ThingsBoard server endpoint, e.g. https://example.com
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Server {
+      get { return server_; }
+      set {
+        server_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ThingsBoardIntegration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ThingsBoardIntegration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      if (Server != other.Server) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (Server.Length != 0) hash ^= Server.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Server.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Server);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Server.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Server);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (Server.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Server);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ThingsBoardIntegration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.Server.Length != 0) {
+        Server = other.Server;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Server = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Server = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateThingsBoardIntegrationRequest : pb::IMessage<CreateThingsBoardIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateThingsBoardIntegrationRequest> _parser = new pb::MessageParser<CreateThingsBoardIntegrationRequest>(() => new CreateThingsBoardIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateThingsBoardIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[26]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateThingsBoardIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateThingsBoardIntegrationRequest(CreateThingsBoardIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateThingsBoardIntegrationRequest Clone() {
+      return new CreateThingsBoardIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.ThingsBoardIntegration integration_;
+    /// <summary>
+    /// Integration object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.ThingsBoardIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateThingsBoardIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateThingsBoardIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateThingsBoardIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.ThingsBoardIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.ThingsBoardIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.ThingsBoardIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetThingsBoardIntegrationRequest : pb::IMessage<GetThingsBoardIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetThingsBoardIntegrationRequest> _parser = new pb::MessageParser<GetThingsBoardIntegrationRequest>(() => new GetThingsBoardIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetThingsBoardIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[27]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetThingsBoardIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetThingsBoardIntegrationRequest(GetThingsBoardIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetThingsBoardIntegrationRequest Clone() {
+      return new GetThingsBoardIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetThingsBoardIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetThingsBoardIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetThingsBoardIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetThingsBoardIntegrationResponse : pb::IMessage<GetThingsBoardIntegrationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetThingsBoardIntegrationResponse> _parser = new pb::MessageParser<GetThingsBoardIntegrationResponse>(() => new GetThingsBoardIntegrationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetThingsBoardIntegrationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[28]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetThingsBoardIntegrationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetThingsBoardIntegrationResponse(GetThingsBoardIntegrationResponse other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetThingsBoardIntegrationResponse Clone() {
+      return new GetThingsBoardIntegrationResponse(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.ThingsBoardIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.ThingsBoardIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetThingsBoardIntegrationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetThingsBoardIntegrationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetThingsBoardIntegrationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.ThingsBoardIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.ThingsBoardIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.ThingsBoardIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateThingsBoardIntegrationRequest : pb::IMessage<UpdateThingsBoardIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateThingsBoardIntegrationRequest> _parser = new pb::MessageParser<UpdateThingsBoardIntegrationRequest>(() => new UpdateThingsBoardIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateThingsBoardIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[29]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateThingsBoardIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateThingsBoardIntegrationRequest(UpdateThingsBoardIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateThingsBoardIntegrationRequest Clone() {
+      return new UpdateThingsBoardIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.ThingsBoardIntegration integration_;
+    /// <summary>
+    /// Integration object to update.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.ThingsBoardIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateThingsBoardIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateThingsBoardIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateThingsBoardIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.ThingsBoardIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.ThingsBoardIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.ThingsBoardIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteThingsBoardIntegrationRequest : pb::IMessage<DeleteThingsBoardIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteThingsBoardIntegrationRequest> _parser = new pb::MessageParser<DeleteThingsBoardIntegrationRequest>(() => new DeleteThingsBoardIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteThingsBoardIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[30]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteThingsBoardIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteThingsBoardIntegrationRequest(DeleteThingsBoardIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteThingsBoardIntegrationRequest Clone() {
+      return new DeleteThingsBoardIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteThingsBoardIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteThingsBoardIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteThingsBoardIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class MyDevicesIntegration : pb::IMessage<MyDevicesIntegration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<MyDevicesIntegration> _parser = new pb::MessageParser<MyDevicesIntegration>(() => new MyDevicesIntegration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<MyDevicesIntegration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[31]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MyDevicesIntegration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MyDevicesIntegration(MyDevicesIntegration other) : this() {
+      applicationId_ = other.applicationId_;
+      endpoint_ = other.endpoint_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MyDevicesIntegration Clone() {
+      return new MyDevicesIntegration(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "endpoint" field.</summary>
+    public const int EndpointFieldNumber = 2;
+    private string endpoint_ = "";
+    /// <summary>
+    /// myDevices API endpoint.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Endpoint {
+      get { return endpoint_; }
+      set {
+        endpoint_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as MyDevicesIntegration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(MyDevicesIntegration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      if (Endpoint != other.Endpoint) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (Endpoint.Length != 0) hash ^= Endpoint.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Endpoint.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Endpoint);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Endpoint.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Endpoint);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (Endpoint.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Endpoint);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(MyDevicesIntegration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.Endpoint.Length != 0) {
+        Endpoint = other.Endpoint;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Endpoint = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Endpoint = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateMyDevicesIntegrationRequest : pb::IMessage<CreateMyDevicesIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateMyDevicesIntegrationRequest> _parser = new pb::MessageParser<CreateMyDevicesIntegrationRequest>(() => new CreateMyDevicesIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateMyDevicesIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[32]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateMyDevicesIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateMyDevicesIntegrationRequest(CreateMyDevicesIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateMyDevicesIntegrationRequest Clone() {
+      return new CreateMyDevicesIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.MyDevicesIntegration integration_;
+    /// <summary>
+    /// Integration object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MyDevicesIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateMyDevicesIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateMyDevicesIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateMyDevicesIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.MyDevicesIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.MyDevicesIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.MyDevicesIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetMyDevicesIntegrationRequest : pb::IMessage<GetMyDevicesIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetMyDevicesIntegrationRequest> _parser = new pb::MessageParser<GetMyDevicesIntegrationRequest>(() => new GetMyDevicesIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetMyDevicesIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[33]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMyDevicesIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMyDevicesIntegrationRequest(GetMyDevicesIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMyDevicesIntegrationRequest Clone() {
+      return new GetMyDevicesIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetMyDevicesIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetMyDevicesIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetMyDevicesIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetMyDevicesIntegrationResponse : pb::IMessage<GetMyDevicesIntegrationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetMyDevicesIntegrationResponse> _parser = new pb::MessageParser<GetMyDevicesIntegrationResponse>(() => new GetMyDevicesIntegrationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetMyDevicesIntegrationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[34]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMyDevicesIntegrationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMyDevicesIntegrationResponse(GetMyDevicesIntegrationResponse other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMyDevicesIntegrationResponse Clone() {
+      return new GetMyDevicesIntegrationResponse(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.MyDevicesIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MyDevicesIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetMyDevicesIntegrationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetMyDevicesIntegrationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetMyDevicesIntegrationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.MyDevicesIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.MyDevicesIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.MyDevicesIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateMyDevicesIntegrationRequest : pb::IMessage<UpdateMyDevicesIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateMyDevicesIntegrationRequest> _parser = new pb::MessageParser<UpdateMyDevicesIntegrationRequest>(() => new UpdateMyDevicesIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateMyDevicesIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[35]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateMyDevicesIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateMyDevicesIntegrationRequest(UpdateMyDevicesIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateMyDevicesIntegrationRequest Clone() {
+      return new UpdateMyDevicesIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.MyDevicesIntegration integration_;
+    /// <summary>
+    /// Integration object to update.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MyDevicesIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateMyDevicesIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateMyDevicesIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateMyDevicesIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.MyDevicesIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.MyDevicesIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.MyDevicesIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteMyDevicesIntegrationRequest : pb::IMessage<DeleteMyDevicesIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteMyDevicesIntegrationRequest> _parser = new pb::MessageParser<DeleteMyDevicesIntegrationRequest>(() => new DeleteMyDevicesIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteMyDevicesIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[36]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteMyDevicesIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteMyDevicesIntegrationRequest(DeleteMyDevicesIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteMyDevicesIntegrationRequest Clone() {
+      return new DeleteMyDevicesIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUIO).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteMyDevicesIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteMyDevicesIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteMyDevicesIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class LoraCloudIntegration : pb::IMessage<LoraCloudIntegration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<LoraCloudIntegration> _parser = new pb::MessageParser<LoraCloudIntegration>(() => new LoraCloudIntegration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<LoraCloudIntegration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[37]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraCloudIntegration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraCloudIntegration(LoraCloudIntegration other) : this() {
+      applicationId_ = other.applicationId_;
+      modemGeolocationServices_ = other.modemGeolocationServices_ != null ? other.modemGeolocationServices_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraCloudIntegration Clone() {
+      return new LoraCloudIntegration(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "modem_geolocation_services" field.</summary>
+    public const int ModemGeolocationServicesFieldNumber = 2;
+    private global::Chirpstack.Api.LoraCloudModemGeolocationServices modemGeolocationServices_;
+    /// <summary>
+    /// Modem &amp; Geolocation Services configuration.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.LoraCloudModemGeolocationServices ModemGeolocationServices {
+      get { return modemGeolocationServices_; }
+      set {
+        modemGeolocationServices_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as LoraCloudIntegration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(LoraCloudIntegration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      if (!object.Equals(ModemGeolocationServices, other.ModemGeolocationServices)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (modemGeolocationServices_ != null) hash ^= ModemGeolocationServices.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (modemGeolocationServices_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(ModemGeolocationServices);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (modemGeolocationServices_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(ModemGeolocationServices);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (modemGeolocationServices_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ModemGeolocationServices);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(LoraCloudIntegration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.modemGeolocationServices_ != null) {
+        if (modemGeolocationServices_ == null) {
+          ModemGeolocationServices = new global::Chirpstack.Api.LoraCloudModemGeolocationServices();
+        }
+        ModemGeolocationServices.MergeFrom(other.ModemGeolocationServices);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (modemGeolocationServices_ == null) {
+              ModemGeolocationServices = new global::Chirpstack.Api.LoraCloudModemGeolocationServices();
+            }
+            input.ReadMessage(ModemGeolocationServices);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (modemGeolocationServices_ == null) {
+              ModemGeolocationServices = new global::Chirpstack.Api.LoraCloudModemGeolocationServices();
+            }
+            input.ReadMessage(ModemGeolocationServices);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class LoraCloudModemGeolocationServices : pb::IMessage<LoraCloudModemGeolocationServices>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<LoraCloudModemGeolocationServices> _parser = new pb::MessageParser<LoraCloudModemGeolocationServices>(() => new LoraCloudModemGeolocationServices());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<LoraCloudModemGeolocationServices> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[38]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraCloudModemGeolocationServices() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraCloudModemGeolocationServices(LoraCloudModemGeolocationServices other) : this() {
+      token_ = other.token_;
+      modemEnabled_ = other.modemEnabled_;
+      modemPort_ = other.modemPort_;
+      gnssPort_ = other.gnssPort_;
+      gnssUseRxTime_ = other.gnssUseRxTime_;
+      parseTlv_ = other.parseTlv_;
+      geolocationBufferTtl_ = other.geolocationBufferTtl_;
+      geolocationMinBufferSize_ = other.geolocationMinBufferSize_;
+      geolocationTdoa_ = other.geolocationTdoa_;
+      geolocationRssi_ = other.geolocationRssi_;
+      geolocationGnss_ = other.geolocationGnss_;
+      geolocationGnssPayloadField_ = other.geolocationGnssPayloadField_;
+      geolocationGnssUseRxTime_ = other.geolocationGnssUseRxTime_;
+      geolocationWifi_ = other.geolocationWifi_;
+      geolocationWifiPayloadField_ = other.geolocationWifiPayloadField_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraCloudModemGeolocationServices Clone() {
+      return new LoraCloudModemGeolocationServices(this);
+    }
+
+    /// <summary>Field number for the "token" field.</summary>
+    public const int TokenFieldNumber = 1;
+    private string token_ = "";
+    /// <summary>
+    /// API token.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Token {
+      get { return token_; }
+      set {
+        token_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "modem_enabled" field.</summary>
+    public const int ModemEnabledFieldNumber = 2;
+    private bool modemEnabled_;
+    /// <summary>
+    /// Device implements Modem / Modem-E stack.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool ModemEnabled {
+      get { return modemEnabled_; }
+      set {
+        modemEnabled_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "modem_port" field.</summary>
+    public const int ModemPortFieldNumber = 3;
+    private uint modemPort_;
+    /// <summary>
+    /// Modem port (fPort).
+    /// ChirpStack will only forward the FrmPayload to the MGS if the port
+    /// is equal to the configured value.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ModemPort {
+      get { return modemPort_; }
+      set {
+        modemPort_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "gnss_port" field.</summary>
+    public const int GnssPortFieldNumber = 4;
+    private uint gnssPort_;
+    /// <summary>
+    /// GNSS port (fPort).
+    /// ChirpStack will forward the FrmPayload to MGS as GNSS payload if the
+    /// port is equal to the configured value.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint GnssPort {
+      get { return gnssPort_; }
+      set {
+        gnssPort_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "gnss_use_rx_time" field.</summary>
+    public const int GnssUseRxTimeFieldNumber = 5;
+    private bool gnssUseRxTime_;
+    /// <summary>
+    /// Use rx time for GNSS resolving.
+    /// In case this is set to true, the MGS resolver will use the RX time of the
+    /// network instead of the timestamp included in the LR1110 payload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool GnssUseRxTime {
+      get { return gnssUseRxTime_; }
+      set {
+        gnssUseRxTime_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "parse_tlv" field.</summary>
+    public const int ParseTlvFieldNumber = 6;
+    private bool parseTlv_;
+    /// <summary>
+    /// Parse TLV records.
+    /// If enabled, stream records (expected in TLV format) are scanned for GNSS
+    /// data (0x06 or 0x07). If found, ChirpStack will make an additional
+    /// geolocation call to the MGS API for resolving the location of the detected
+    /// payload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool ParseTlv {
+      get { return parseTlv_; }
+      set {
+        parseTlv_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "geolocation_buffer_ttl" field.</summary>
+    public const int GeolocationBufferTtlFieldNumber = 7;
+    private uint geolocationBufferTtl_;
+    /// <summary>
+    /// Geolocation buffer TTL (in seconds).
+    /// If > 0, uplink RX meta-data will be stored in a buffer so that
+    /// the meta-data of multiple uplinks can be used for geolocation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint GeolocationBufferTtl {
+      get { return geolocationBufferTtl_; }
+      set {
+        geolocationBufferTtl_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "geolocation_min_buffer_size" field.</summary>
+    public const int GeolocationMinBufferSizeFieldNumber = 8;
+    private uint geolocationMinBufferSize_;
+    /// <summary>
+    /// Geolocation minimum buffer size.
+    /// If > 0, geolocation will only be performed when the buffer has
+    /// at least the given size.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint GeolocationMinBufferSize {
+      get { return geolocationMinBufferSize_; }
+      set {
+        geolocationMinBufferSize_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "geolocation_tdoa" field.</summary>
+    public const int GeolocationTdoaFieldNumber = 9;
+    private bool geolocationTdoa_;
+    /// <summary>
+    /// TDOA based geolocation is enabled.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool GeolocationTdoa {
+      get { return geolocationTdoa_; }
+      set {
+        geolocationTdoa_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "geolocation_rssi" field.</summary>
+    public const int GeolocationRssiFieldNumber = 10;
+    private bool geolocationRssi_;
+    /// <summary>
+    /// RSSI based geolocation is enabled.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool GeolocationRssi {
+      get { return geolocationRssi_; }
+      set {
+        geolocationRssi_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "geolocation_gnss" field.</summary>
+    public const int GeolocationGnssFieldNumber = 11;
+    private bool geolocationGnss_;
+    /// <summary>
+    /// GNSS based geolocation is enabled (LR1110).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool GeolocationGnss {
+      get { return geolocationGnss_; }
+      set {
+        geolocationGnss_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "geolocation_gnss_payload_field" field.</summary>
+    public const int GeolocationGnssPayloadFieldFieldNumber = 12;
+    private string geolocationGnssPayloadField_ = "";
+    /// <summary>
+    /// GNSS payload field.
+    /// This holds the name of the field in the decoded payload object which
+    /// contains the GNSS payload bytes (as HEX string).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GeolocationGnssPayloadField {
+      get { return geolocationGnssPayloadField_; }
+      set {
+        geolocationGnssPayloadField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "geolocation_gnss_use_rx_time" field.</summary>
+    public const int GeolocationGnssUseRxTimeFieldNumber = 13;
+    private bool geolocationGnssUseRxTime_;
+    /// <summary>
+    /// GNSS use RX time.
+    /// In case this is set to true, the resolver will use the RX time of the
+    /// network instead of the timestamp included in the LR1110 payload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool GeolocationGnssUseRxTime {
+      get { return geolocationGnssUseRxTime_; }
+      set {
+        geolocationGnssUseRxTime_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "geolocation_wifi" field.</summary>
+    public const int GeolocationWifiFieldNumber = 14;
+    private bool geolocationWifi_;
+    /// <summary>
+    /// Wifi based geolocation is enabled.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool GeolocationWifi {
+      get { return geolocationWifi_; }
+      set {
+        geolocationWifi_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "geolocation_wifi_payload_field" field.</summary>
+    public const int GeolocationWifiPayloadFieldFieldNumber = 15;
+    private string geolocationWifiPayloadField_ = "";
+    /// <summary>
+    /// Wifi payload field.
+    /// This holds the name of the field in the decoded payload object which
+    /// contains an array of objects with the following fields:
+    /// * macAddress - e.g. 01:23:45:67:89:ab
+    /// * signalStrength - e.g. -51 (optional)
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GeolocationWifiPayloadField {
+      get { return geolocationWifiPayloadField_; }
+      set {
+        geolocationWifiPayloadField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as LoraCloudModemGeolocationServices);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(LoraCloudModemGeolocationServices other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Token != other.Token) return false;
+      if (ModemEnabled != other.ModemEnabled) return false;
+      if (ModemPort != other.ModemPort) return false;
+      if (GnssPort != other.GnssPort) return false;
+      if (GnssUseRxTime != other.GnssUseRxTime) return false;
+      if (ParseTlv != other.ParseTlv) return false;
+      if (GeolocationBufferTtl != other.GeolocationBufferTtl) return false;
+      if (GeolocationMinBufferSize != other.GeolocationMinBufferSize) return false;
+      if (GeolocationTdoa != other.GeolocationTdoa) return false;
+      if (GeolocationRssi != other.GeolocationRssi) return false;
+      if (GeolocationGnss != other.GeolocationGnss) return false;
+      if (GeolocationGnssPayloadField != other.GeolocationGnssPayloadField) return false;
+      if (GeolocationGnssUseRxTime != other.GeolocationGnssUseRxTime) return false;
+      if (GeolocationWifi != other.GeolocationWifi) return false;
+      if (GeolocationWifiPayloadField != other.GeolocationWifiPayloadField) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Token.Length != 0) hash ^= Token.GetHashCode();
+      if (ModemEnabled != false) hash ^= ModemEnabled.GetHashCode();
+      if (ModemPort != 0) hash ^= ModemPort.GetHashCode();
+      if (GnssPort != 0) hash ^= GnssPort.GetHashCode();
+      if (GnssUseRxTime != false) hash ^= GnssUseRxTime.GetHashCode();
+      if (ParseTlv != false) hash ^= ParseTlv.GetHashCode();
+      if (GeolocationBufferTtl != 0) hash ^= GeolocationBufferTtl.GetHashCode();
+      if (GeolocationMinBufferSize != 0) hash ^= GeolocationMinBufferSize.GetHashCode();
+      if (GeolocationTdoa != false) hash ^= GeolocationTdoa.GetHashCode();
+      if (GeolocationRssi != false) hash ^= GeolocationRssi.GetHashCode();
+      if (GeolocationGnss != false) hash ^= GeolocationGnss.GetHashCode();
+      if (GeolocationGnssPayloadField.Length != 0) hash ^= GeolocationGnssPayloadField.GetHashCode();
+      if (GeolocationGnssUseRxTime != false) hash ^= GeolocationGnssUseRxTime.GetHashCode();
+      if (GeolocationWifi != false) hash ^= GeolocationWifi.GetHashCode();
+      if (GeolocationWifiPayloadField.Length != 0) hash ^= GeolocationWifiPayloadField.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Token.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Token);
+      }
+      if (ModemEnabled != false) {
+        output.WriteRawTag(16);
+        output.WriteBool(ModemEnabled);
+      }
+      if (ModemPort != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(ModemPort);
+      }
+      if (GnssPort != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(GnssPort);
+      }
+      if (GnssUseRxTime != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(GnssUseRxTime);
+      }
+      if (ParseTlv != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(ParseTlv);
+      }
+      if (GeolocationBufferTtl != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(GeolocationBufferTtl);
+      }
+      if (GeolocationMinBufferSize != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(GeolocationMinBufferSize);
+      }
+      if (GeolocationTdoa != false) {
+        output.WriteRawTag(72);
+        output.WriteBool(GeolocationTdoa);
+      }
+      if (GeolocationRssi != false) {
+        output.WriteRawTag(80);
+        output.WriteBool(GeolocationRssi);
+      }
+      if (GeolocationGnss != false) {
+        output.WriteRawTag(88);
+        output.WriteBool(GeolocationGnss);
+      }
+      if (GeolocationGnssPayloadField.Length != 0) {
+        output.WriteRawTag(98);
+        output.WriteString(GeolocationGnssPayloadField);
+      }
+      if (GeolocationGnssUseRxTime != false) {
+        output.WriteRawTag(104);
+        output.WriteBool(GeolocationGnssUseRxTime);
+      }
+      if (GeolocationWifi != false) {
+        output.WriteRawTag(112);
+        output.WriteBool(GeolocationWifi);
+      }
+      if (GeolocationWifiPayloadField.Length != 0) {
+        output.WriteRawTag(122);
+        output.WriteString(GeolocationWifiPayloadField);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Token.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Token);
+      }
+      if (ModemEnabled != false) {
+        output.WriteRawTag(16);
+        output.WriteBool(ModemEnabled);
+      }
+      if (ModemPort != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(ModemPort);
+      }
+      if (GnssPort != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(GnssPort);
+      }
+      if (GnssUseRxTime != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(GnssUseRxTime);
+      }
+      if (ParseTlv != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(ParseTlv);
+      }
+      if (GeolocationBufferTtl != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(GeolocationBufferTtl);
+      }
+      if (GeolocationMinBufferSize != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(GeolocationMinBufferSize);
+      }
+      if (GeolocationTdoa != false) {
+        output.WriteRawTag(72);
+        output.WriteBool(GeolocationTdoa);
+      }
+      if (GeolocationRssi != false) {
+        output.WriteRawTag(80);
+        output.WriteBool(GeolocationRssi);
+      }
+      if (GeolocationGnss != false) {
+        output.WriteRawTag(88);
+        output.WriteBool(GeolocationGnss);
+      }
+      if (GeolocationGnssPayloadField.Length != 0) {
+        output.WriteRawTag(98);
+        output.WriteString(GeolocationGnssPayloadField);
+      }
+      if (GeolocationGnssUseRxTime != false) {
+        output.WriteRawTag(104);
+        output.WriteBool(GeolocationGnssUseRxTime);
+      }
+      if (GeolocationWifi != false) {
+        output.WriteRawTag(112);
+        output.WriteBool(GeolocationWifi);
+      }
+      if (GeolocationWifiPayloadField.Length != 0) {
+        output.WriteRawTag(122);
+        output.WriteString(GeolocationWifiPayloadField);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Token.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Token);
+      }
+      if (ModemEnabled != false) {
+        size += 1 + 1;
+      }
+      if (ModemPort != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ModemPort);
+      }
+      if (GnssPort != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(GnssPort);
+      }
+      if (GnssUseRxTime != false) {
+        size += 1 + 1;
+      }
+      if (ParseTlv != false) {
+        size += 1 + 1;
+      }
+      if (GeolocationBufferTtl != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(GeolocationBufferTtl);
+      }
+      if (GeolocationMinBufferSize != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(GeolocationMinBufferSize);
+      }
+      if (GeolocationTdoa != false) {
+        size += 1 + 1;
+      }
+      if (GeolocationRssi != false) {
+        size += 1 + 1;
+      }
+      if (GeolocationGnss != false) {
+        size += 1 + 1;
+      }
+      if (GeolocationGnssPayloadField.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GeolocationGnssPayloadField);
+      }
+      if (GeolocationGnssUseRxTime != false) {
+        size += 1 + 1;
+      }
+      if (GeolocationWifi != false) {
+        size += 1 + 1;
+      }
+      if (GeolocationWifiPayloadField.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GeolocationWifiPayloadField);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(LoraCloudModemGeolocationServices other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Token.Length != 0) {
+        Token = other.Token;
+      }
+      if (other.ModemEnabled != false) {
+        ModemEnabled = other.ModemEnabled;
+      }
+      if (other.ModemPort != 0) {
+        ModemPort = other.ModemPort;
+      }
+      if (other.GnssPort != 0) {
+        GnssPort = other.GnssPort;
+      }
+      if (other.GnssUseRxTime != false) {
+        GnssUseRxTime = other.GnssUseRxTime;
+      }
+      if (other.ParseTlv != false) {
+        ParseTlv = other.ParseTlv;
+      }
+      if (other.GeolocationBufferTtl != 0) {
+        GeolocationBufferTtl = other.GeolocationBufferTtl;
+      }
+      if (other.GeolocationMinBufferSize != 0) {
+        GeolocationMinBufferSize = other.GeolocationMinBufferSize;
+      }
+      if (other.GeolocationTdoa != false) {
+        GeolocationTdoa = other.GeolocationTdoa;
+      }
+      if (other.GeolocationRssi != false) {
+        GeolocationRssi = other.GeolocationRssi;
+      }
+      if (other.GeolocationGnss != false) {
+        GeolocationGnss = other.GeolocationGnss;
+      }
+      if (other.GeolocationGnssPayloadField.Length != 0) {
+        GeolocationGnssPayloadField = other.GeolocationGnssPayloadField;
+      }
+      if (other.GeolocationGnssUseRxTime != false) {
+        GeolocationGnssUseRxTime = other.GeolocationGnssUseRxTime;
+      }
+      if (other.GeolocationWifi != false) {
+        GeolocationWifi = other.GeolocationWifi;
+      }
+      if (other.GeolocationWifiPayloadField.Length != 0) {
+        GeolocationWifiPayloadField = other.GeolocationWifiPayloadField;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Token = input.ReadString();
+            break;
+          }
+          case 16: {
+            ModemEnabled = input.ReadBool();
+            break;
+          }
+          case 24: {
+            ModemPort = input.ReadUInt32();
+            break;
+          }
+          case 32: {
+            GnssPort = input.ReadUInt32();
+            break;
+          }
+          case 40: {
+            GnssUseRxTime = input.ReadBool();
+            break;
+          }
+          case 48: {
+            ParseTlv = input.ReadBool();
+            break;
+          }
+          case 56: {
+            GeolocationBufferTtl = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            GeolocationMinBufferSize = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            GeolocationTdoa = input.ReadBool();
+            break;
+          }
+          case 80: {
+            GeolocationRssi = input.ReadBool();
+            break;
+          }
+          case 88: {
+            GeolocationGnss = input.ReadBool();
+            break;
+          }
+          case 98: {
+            GeolocationGnssPayloadField = input.ReadString();
+            break;
+          }
+          case 104: {
+            GeolocationGnssUseRxTime = input.ReadBool();
+            break;
+          }
+          case 112: {
+            GeolocationWifi = input.ReadBool();
+            break;
+          }
+          case 122: {
+            GeolocationWifiPayloadField = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Token = input.ReadString();
+            break;
+          }
+          case 16: {
+            ModemEnabled = input.ReadBool();
+            break;
+          }
+          case 24: {
+            ModemPort = input.ReadUInt32();
+            break;
+          }
+          case 32: {
+            GnssPort = input.ReadUInt32();
+            break;
+          }
+          case 40: {
+            GnssUseRxTime = input.ReadBool();
+            break;
+          }
+          case 48: {
+            ParseTlv = input.ReadBool();
+            break;
+          }
+          case 56: {
+            GeolocationBufferTtl = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            GeolocationMinBufferSize = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            GeolocationTdoa = input.ReadBool();
+            break;
+          }
+          case 80: {
+            GeolocationRssi = input.ReadBool();
+            break;
+          }
+          case 88: {
+            GeolocationGnss = input.ReadBool();
+            break;
+          }
+          case 98: {
+            GeolocationGnssPayloadField = input.ReadString();
+            break;
+          }
+          case 104: {
+            GeolocationGnssUseRxTime = input.ReadBool();
+            break;
+          }
+          case 112: {
+            GeolocationWifi = input.ReadBool();
+            break;
+          }
+          case 122: {
+            GeolocationWifiPayloadField = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateLoraCloudIntegrationRequest : pb::IMessage<CreateLoraCloudIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateLoraCloudIntegrationRequest> _parser = new pb::MessageParser<CreateLoraCloudIntegrationRequest>(() => new CreateLoraCloudIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateLoraCloudIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[39]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateLoraCloudIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateLoraCloudIntegrationRequest(CreateLoraCloudIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateLoraCloudIntegrationRequest Clone() {
+      return new CreateLoraCloudIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.LoraCloudIntegration integration_;
+    /// <summary>
+    /// Integration object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.LoraCloudIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateLoraCloudIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateLoraCloudIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateLoraCloudIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.LoraCloudIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.LoraCloudIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.LoraCloudIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetLoraCloudIntegrationRequest : pb::IMessage<GetLoraCloudIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetLoraCloudIntegrationRequest> _parser = new pb::MessageParser<GetLoraCloudIntegrationRequest>(() => new GetLoraCloudIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetLoraCloudIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[40]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetLoraCloudIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetLoraCloudIntegrationRequest(GetLoraCloudIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetLoraCloudIntegrationRequest Clone() {
+      return new GetLoraCloudIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetLoraCloudIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetLoraCloudIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetLoraCloudIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetLoraCloudIntegrationResponse : pb::IMessage<GetLoraCloudIntegrationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetLoraCloudIntegrationResponse> _parser = new pb::MessageParser<GetLoraCloudIntegrationResponse>(() => new GetLoraCloudIntegrationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetLoraCloudIntegrationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[41]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetLoraCloudIntegrationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetLoraCloudIntegrationResponse(GetLoraCloudIntegrationResponse other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetLoraCloudIntegrationResponse Clone() {
+      return new GetLoraCloudIntegrationResponse(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.LoraCloudIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.LoraCloudIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetLoraCloudIntegrationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetLoraCloudIntegrationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetLoraCloudIntegrationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.LoraCloudIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.LoraCloudIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.LoraCloudIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateLoraCloudIntegrationRequest : pb::IMessage<UpdateLoraCloudIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateLoraCloudIntegrationRequest> _parser = new pb::MessageParser<UpdateLoraCloudIntegrationRequest>(() => new UpdateLoraCloudIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateLoraCloudIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[42]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateLoraCloudIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateLoraCloudIntegrationRequest(UpdateLoraCloudIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateLoraCloudIntegrationRequest Clone() {
+      return new UpdateLoraCloudIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.LoraCloudIntegration integration_;
+    /// <summary>
+    /// Integration object to update.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.LoraCloudIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateLoraCloudIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateLoraCloudIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateLoraCloudIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.LoraCloudIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.LoraCloudIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.LoraCloudIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteLoraCloudIntegrationRequest : pb::IMessage<DeleteLoraCloudIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteLoraCloudIntegrationRequest> _parser = new pb::MessageParser<DeleteLoraCloudIntegrationRequest>(() => new DeleteLoraCloudIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteLoraCloudIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[43]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteLoraCloudIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteLoraCloudIntegrationRequest(DeleteLoraCloudIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteLoraCloudIntegrationRequest Clone() {
+      return new DeleteLoraCloudIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteLoraCloudIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteLoraCloudIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteLoraCloudIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GcpPubSubIntegration : pb::IMessage<GcpPubSubIntegration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GcpPubSubIntegration> _parser = new pb::MessageParser<GcpPubSubIntegration>(() => new GcpPubSubIntegration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GcpPubSubIntegration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[44]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GcpPubSubIntegration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GcpPubSubIntegration(GcpPubSubIntegration other) : this() {
+      applicationId_ = other.applicationId_;
+      encoding_ = other.encoding_;
+      credentialsFile_ = other.credentialsFile_;
+      projectId_ = other.projectId_;
+      topicName_ = other.topicName_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GcpPubSubIntegration Clone() {
+      return new GcpPubSubIntegration(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "encoding" field.</summary>
+    public const int EncodingFieldNumber = 2;
+    private global::Chirpstack.Api.Encoding encoding_ = global::Chirpstack.Api.Encoding.Json;
+    /// <summary>
+    /// Encoding.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Encoding Encoding {
+      get { return encoding_; }
+      set {
+        encoding_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "credentials_file" field.</summary>
+    public const int CredentialsFileFieldNumber = 3;
+    private string credentialsFile_ = "";
+    /// <summary>
+    /// Credentials file.
+    /// This IAM service-account credentials file (JSON) must have the following Pub/Sub roles:
+    /// * Pub/Sub Publisher
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string CredentialsFile {
+      get { return credentialsFile_; }
+      set {
+        credentialsFile_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "project_id" field.</summary>
+    public const int ProjectIdFieldNumber = 4;
+    private string projectId_ = "";
+    /// <summary>
+    /// Project ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ProjectId {
+      get { return projectId_; }
+      set {
+        projectId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "topic_name" field.</summary>
+    public const int TopicNameFieldNumber = 5;
+    private string topicName_ = "";
+    /// <summary>
+    /// Topic name.
+    /// This is the name of the Pub/Sub topic.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TopicName {
+      get { return topicName_; }
+      set {
+        topicName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GcpPubSubIntegration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GcpPubSubIntegration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      if (Encoding != other.Encoding) return false;
+      if (CredentialsFile != other.CredentialsFile) return false;
+      if (ProjectId != other.ProjectId) return false;
+      if (TopicName != other.TopicName) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) hash ^= Encoding.GetHashCode();
+      if (CredentialsFile.Length != 0) hash ^= CredentialsFile.GetHashCode();
+      if (ProjectId.Length != 0) hash ^= ProjectId.GetHashCode();
+      if (TopicName.Length != 0) hash ^= TopicName.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Encoding);
+      }
+      if (CredentialsFile.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(CredentialsFile);
+      }
+      if (ProjectId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ProjectId);
+      }
+      if (TopicName.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(TopicName);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Encoding);
+      }
+      if (CredentialsFile.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(CredentialsFile);
+      }
+      if (ProjectId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ProjectId);
+      }
+      if (TopicName.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(TopicName);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Encoding);
+      }
+      if (CredentialsFile.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(CredentialsFile);
+      }
+      if (ProjectId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ProjectId);
+      }
+      if (TopicName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TopicName);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GcpPubSubIntegration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.Encoding != global::Chirpstack.Api.Encoding.Json) {
+        Encoding = other.Encoding;
+      }
+      if (other.CredentialsFile.Length != 0) {
+        CredentialsFile = other.CredentialsFile;
+      }
+      if (other.ProjectId.Length != 0) {
+        ProjectId = other.ProjectId;
+      }
+      if (other.TopicName.Length != 0) {
+        TopicName = other.TopicName;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 16: {
+            Encoding = (global::Chirpstack.Api.Encoding) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            CredentialsFile = input.ReadString();
+            break;
+          }
+          case 34: {
+            ProjectId = input.ReadString();
+            break;
+          }
+          case 42: {
+            TopicName = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 16: {
+            Encoding = (global::Chirpstack.Api.Encoding) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            CredentialsFile = input.ReadString();
+            break;
+          }
+          case 34: {
+            ProjectId = input.ReadString();
+            break;
+          }
+          case 42: {
+            TopicName = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateGcpPubSubIntegrationRequest : pb::IMessage<CreateGcpPubSubIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateGcpPubSubIntegrationRequest> _parser = new pb::MessageParser<CreateGcpPubSubIntegrationRequest>(() => new CreateGcpPubSubIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateGcpPubSubIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[45]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateGcpPubSubIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateGcpPubSubIntegrationRequest(CreateGcpPubSubIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateGcpPubSubIntegrationRequest Clone() {
+      return new CreateGcpPubSubIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.GcpPubSubIntegration integration_;
+    /// <summary>
+    /// Integration object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.GcpPubSubIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateGcpPubSubIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateGcpPubSubIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateGcpPubSubIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.GcpPubSubIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.GcpPubSubIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.GcpPubSubIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetGcpPubSubIntegrationRequest : pb::IMessage<GetGcpPubSubIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetGcpPubSubIntegrationRequest> _parser = new pb::MessageParser<GetGcpPubSubIntegrationRequest>(() => new GetGcpPubSubIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetGcpPubSubIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[46]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGcpPubSubIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGcpPubSubIntegrationRequest(GetGcpPubSubIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGcpPubSubIntegrationRequest Clone() {
+      return new GetGcpPubSubIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetGcpPubSubIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetGcpPubSubIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetGcpPubSubIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetGcpPubSubIntegrationResponse : pb::IMessage<GetGcpPubSubIntegrationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetGcpPubSubIntegrationResponse> _parser = new pb::MessageParser<GetGcpPubSubIntegrationResponse>(() => new GetGcpPubSubIntegrationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetGcpPubSubIntegrationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[47]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGcpPubSubIntegrationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGcpPubSubIntegrationResponse(GetGcpPubSubIntegrationResponse other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGcpPubSubIntegrationResponse Clone() {
+      return new GetGcpPubSubIntegrationResponse(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.GcpPubSubIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.GcpPubSubIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetGcpPubSubIntegrationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetGcpPubSubIntegrationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetGcpPubSubIntegrationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.GcpPubSubIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.GcpPubSubIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.GcpPubSubIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateGcpPubSubIntegrationRequest : pb::IMessage<UpdateGcpPubSubIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateGcpPubSubIntegrationRequest> _parser = new pb::MessageParser<UpdateGcpPubSubIntegrationRequest>(() => new UpdateGcpPubSubIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateGcpPubSubIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[48]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateGcpPubSubIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateGcpPubSubIntegrationRequest(UpdateGcpPubSubIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateGcpPubSubIntegrationRequest Clone() {
+      return new UpdateGcpPubSubIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.GcpPubSubIntegration integration_;
+    /// <summary>
+    /// Integration object to update.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.GcpPubSubIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateGcpPubSubIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateGcpPubSubIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateGcpPubSubIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.GcpPubSubIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.GcpPubSubIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.GcpPubSubIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteGcpPubSubIntegrationRequest : pb::IMessage<DeleteGcpPubSubIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteGcpPubSubIntegrationRequest> _parser = new pb::MessageParser<DeleteGcpPubSubIntegrationRequest>(() => new DeleteGcpPubSubIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteGcpPubSubIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[49]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteGcpPubSubIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteGcpPubSubIntegrationRequest(DeleteGcpPubSubIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteGcpPubSubIntegrationRequest Clone() {
+      return new DeleteGcpPubSubIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteGcpPubSubIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteGcpPubSubIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteGcpPubSubIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class AwsSnsIntegration : pb::IMessage<AwsSnsIntegration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<AwsSnsIntegration> _parser = new pb::MessageParser<AwsSnsIntegration>(() => new AwsSnsIntegration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<AwsSnsIntegration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[50]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AwsSnsIntegration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AwsSnsIntegration(AwsSnsIntegration other) : this() {
+      applicationId_ = other.applicationId_;
+      encoding_ = other.encoding_;
+      region_ = other.region_;
+      accessKeyId_ = other.accessKeyId_;
+      secretAccessKey_ = other.secretAccessKey_;
+      topicArn_ = other.topicArn_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AwsSnsIntegration Clone() {
+      return new AwsSnsIntegration(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "encoding" field.</summary>
+    public const int EncodingFieldNumber = 2;
+    private global::Chirpstack.Api.Encoding encoding_ = global::Chirpstack.Api.Encoding.Json;
+    /// <summary>
+    /// Encoding.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Encoding Encoding {
+      get { return encoding_; }
+      set {
+        encoding_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "region" field.</summary>
+    public const int RegionFieldNumber = 3;
+    private string region_ = "";
+    /// <summary>
+    /// AWS region.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Region {
+      get { return region_; }
+      set {
+        region_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "access_key_id" field.</summary>
+    public const int AccessKeyIdFieldNumber = 4;
+    private string accessKeyId_ = "";
+    /// <summary>
+    /// AWS Access Key ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string AccessKeyId {
+      get { return accessKeyId_; }
+      set {
+        accessKeyId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "secret_access_key" field.</summary>
+    public const int SecretAccessKeyFieldNumber = 5;
+    private string secretAccessKey_ = "";
+    /// <summary>
+    /// AWS Secret Access Key.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string SecretAccessKey {
+      get { return secretAccessKey_; }
+      set {
+        secretAccessKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "topic_arn" field.</summary>
+    public const int TopicArnFieldNumber = 6;
+    private string topicArn_ = "";
+    /// <summary>
+    /// Topic ARN.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TopicArn {
+      get { return topicArn_; }
+      set {
+        topicArn_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as AwsSnsIntegration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(AwsSnsIntegration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      if (Encoding != other.Encoding) return false;
+      if (Region != other.Region) return false;
+      if (AccessKeyId != other.AccessKeyId) return false;
+      if (SecretAccessKey != other.SecretAccessKey) return false;
+      if (TopicArn != other.TopicArn) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) hash ^= Encoding.GetHashCode();
+      if (Region.Length != 0) hash ^= Region.GetHashCode();
+      if (AccessKeyId.Length != 0) hash ^= AccessKeyId.GetHashCode();
+      if (SecretAccessKey.Length != 0) hash ^= SecretAccessKey.GetHashCode();
+      if (TopicArn.Length != 0) hash ^= TopicArn.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Encoding);
+      }
+      if (Region.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Region);
+      }
+      if (AccessKeyId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(AccessKeyId);
+      }
+      if (SecretAccessKey.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(SecretAccessKey);
+      }
+      if (TopicArn.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(TopicArn);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Encoding);
+      }
+      if (Region.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Region);
+      }
+      if (AccessKeyId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(AccessKeyId);
+      }
+      if (SecretAccessKey.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(SecretAccessKey);
+      }
+      if (TopicArn.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(TopicArn);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Encoding);
+      }
+      if (Region.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Region);
+      }
+      if (AccessKeyId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(AccessKeyId);
+      }
+      if (SecretAccessKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(SecretAccessKey);
+      }
+      if (TopicArn.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TopicArn);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(AwsSnsIntegration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.Encoding != global::Chirpstack.Api.Encoding.Json) {
+        Encoding = other.Encoding;
+      }
+      if (other.Region.Length != 0) {
+        Region = other.Region;
+      }
+      if (other.AccessKeyId.Length != 0) {
+        AccessKeyId = other.AccessKeyId;
+      }
+      if (other.SecretAccessKey.Length != 0) {
+        SecretAccessKey = other.SecretAccessKey;
+      }
+      if (other.TopicArn.Length != 0) {
+        TopicArn = other.TopicArn;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 16: {
+            Encoding = (global::Chirpstack.Api.Encoding) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            Region = input.ReadString();
+            break;
+          }
+          case 34: {
+            AccessKeyId = input.ReadString();
+            break;
+          }
+          case 42: {
+            SecretAccessKey = input.ReadString();
+            break;
+          }
+          case 50: {
+            TopicArn = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 16: {
+            Encoding = (global::Chirpstack.Api.Encoding) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            Region = input.ReadString();
+            break;
+          }
+          case 34: {
+            AccessKeyId = input.ReadString();
+            break;
+          }
+          case 42: {
+            SecretAccessKey = input.ReadString();
+            break;
+          }
+          case 50: {
+            TopicArn = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateAwsSnsIntegrationRequest : pb::IMessage<CreateAwsSnsIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateAwsSnsIntegrationRequest> _parser = new pb::MessageParser<CreateAwsSnsIntegrationRequest>(() => new CreateAwsSnsIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateAwsSnsIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[51]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateAwsSnsIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateAwsSnsIntegrationRequest(CreateAwsSnsIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateAwsSnsIntegrationRequest Clone() {
+      return new CreateAwsSnsIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.AwsSnsIntegration integration_;
+    /// <summary>
+    /// Integration object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.AwsSnsIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateAwsSnsIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateAwsSnsIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateAwsSnsIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.AwsSnsIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AwsSnsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AwsSnsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetAwsSnsIntegrationRequest : pb::IMessage<GetAwsSnsIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetAwsSnsIntegrationRequest> _parser = new pb::MessageParser<GetAwsSnsIntegrationRequest>(() => new GetAwsSnsIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetAwsSnsIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[52]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAwsSnsIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAwsSnsIntegrationRequest(GetAwsSnsIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAwsSnsIntegrationRequest Clone() {
+      return new GetAwsSnsIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetAwsSnsIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetAwsSnsIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetAwsSnsIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetAwsSnsIntegrationResponse : pb::IMessage<GetAwsSnsIntegrationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetAwsSnsIntegrationResponse> _parser = new pb::MessageParser<GetAwsSnsIntegrationResponse>(() => new GetAwsSnsIntegrationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetAwsSnsIntegrationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[53]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAwsSnsIntegrationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAwsSnsIntegrationResponse(GetAwsSnsIntegrationResponse other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAwsSnsIntegrationResponse Clone() {
+      return new GetAwsSnsIntegrationResponse(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.AwsSnsIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.AwsSnsIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetAwsSnsIntegrationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetAwsSnsIntegrationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetAwsSnsIntegrationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.AwsSnsIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AwsSnsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AwsSnsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateAwsSnsIntegrationRequest : pb::IMessage<UpdateAwsSnsIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateAwsSnsIntegrationRequest> _parser = new pb::MessageParser<UpdateAwsSnsIntegrationRequest>(() => new UpdateAwsSnsIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateAwsSnsIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[54]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateAwsSnsIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateAwsSnsIntegrationRequest(UpdateAwsSnsIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateAwsSnsIntegrationRequest Clone() {
+      return new UpdateAwsSnsIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.AwsSnsIntegration integration_;
+    /// <summary>
+    /// Integration object to update.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.AwsSnsIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateAwsSnsIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateAwsSnsIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateAwsSnsIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.AwsSnsIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AwsSnsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AwsSnsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteAwsSnsIntegrationRequest : pb::IMessage<DeleteAwsSnsIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteAwsSnsIntegrationRequest> _parser = new pb::MessageParser<DeleteAwsSnsIntegrationRequest>(() => new DeleteAwsSnsIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteAwsSnsIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[55]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteAwsSnsIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteAwsSnsIntegrationRequest(DeleteAwsSnsIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteAwsSnsIntegrationRequest Clone() {
+      return new DeleteAwsSnsIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteAwsSnsIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteAwsSnsIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteAwsSnsIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class AzureServiceBusIntegration : pb::IMessage<AzureServiceBusIntegration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<AzureServiceBusIntegration> _parser = new pb::MessageParser<AzureServiceBusIntegration>(() => new AzureServiceBusIntegration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<AzureServiceBusIntegration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[56]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AzureServiceBusIntegration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AzureServiceBusIntegration(AzureServiceBusIntegration other) : this() {
+      applicationId_ = other.applicationId_;
+      encoding_ = other.encoding_;
+      connectionString_ = other.connectionString_;
+      publishName_ = other.publishName_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AzureServiceBusIntegration Clone() {
+      return new AzureServiceBusIntegration(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "encoding" field.</summary>
+    public const int EncodingFieldNumber = 2;
+    private global::Chirpstack.Api.Encoding encoding_ = global::Chirpstack.Api.Encoding.Json;
+    /// <summary>
+    /// Encoding.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Encoding Encoding {
+      get { return encoding_; }
+      set {
+        encoding_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "connection_string" field.</summary>
+    public const int ConnectionStringFieldNumber = 3;
+    private string connectionString_ = "";
+    /// <summary>
+    /// Connection string.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ConnectionString {
+      get { return connectionString_; }
+      set {
+        connectionString_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "publish_name" field.</summary>
+    public const int PublishNameFieldNumber = 4;
+    private string publishName_ = "";
+    /// <summary>
+    /// Publish name.
+    /// This is the name of the topic or queue.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string PublishName {
+      get { return publishName_; }
+      set {
+        publishName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as AzureServiceBusIntegration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(AzureServiceBusIntegration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      if (Encoding != other.Encoding) return false;
+      if (ConnectionString != other.ConnectionString) return false;
+      if (PublishName != other.PublishName) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) hash ^= Encoding.GetHashCode();
+      if (ConnectionString.Length != 0) hash ^= ConnectionString.GetHashCode();
+      if (PublishName.Length != 0) hash ^= PublishName.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Encoding);
+      }
+      if (ConnectionString.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(ConnectionString);
+      }
+      if (PublishName.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(PublishName);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Encoding);
+      }
+      if (ConnectionString.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(ConnectionString);
+      }
+      if (PublishName.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(PublishName);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (Encoding != global::Chirpstack.Api.Encoding.Json) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Encoding);
+      }
+      if (ConnectionString.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ConnectionString);
+      }
+      if (PublishName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(PublishName);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(AzureServiceBusIntegration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.Encoding != global::Chirpstack.Api.Encoding.Json) {
+        Encoding = other.Encoding;
+      }
+      if (other.ConnectionString.Length != 0) {
+        ConnectionString = other.ConnectionString;
+      }
+      if (other.PublishName.Length != 0) {
+        PublishName = other.PublishName;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 16: {
+            Encoding = (global::Chirpstack.Api.Encoding) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            ConnectionString = input.ReadString();
+            break;
+          }
+          case 34: {
+            PublishName = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 16: {
+            Encoding = (global::Chirpstack.Api.Encoding) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            ConnectionString = input.ReadString();
+            break;
+          }
+          case 34: {
+            PublishName = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateAzureServiceBusIntegrationRequest : pb::IMessage<CreateAzureServiceBusIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateAzureServiceBusIntegrationRequest> _parser = new pb::MessageParser<CreateAzureServiceBusIntegrationRequest>(() => new CreateAzureServiceBusIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateAzureServiceBusIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[57]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateAzureServiceBusIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateAzureServiceBusIntegrationRequest(CreateAzureServiceBusIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateAzureServiceBusIntegrationRequest Clone() {
+      return new CreateAzureServiceBusIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.AzureServiceBusIntegration integration_;
+    /// <summary>
+    /// Integration object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.AzureServiceBusIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateAzureServiceBusIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateAzureServiceBusIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateAzureServiceBusIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.AzureServiceBusIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AzureServiceBusIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AzureServiceBusIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetAzureServiceBusIntegrationRequest : pb::IMessage<GetAzureServiceBusIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetAzureServiceBusIntegrationRequest> _parser = new pb::MessageParser<GetAzureServiceBusIntegrationRequest>(() => new GetAzureServiceBusIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetAzureServiceBusIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[58]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAzureServiceBusIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAzureServiceBusIntegrationRequest(GetAzureServiceBusIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAzureServiceBusIntegrationRequest Clone() {
+      return new GetAzureServiceBusIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetAzureServiceBusIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetAzureServiceBusIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetAzureServiceBusIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetAzureServiceBusIntegrationResponse : pb::IMessage<GetAzureServiceBusIntegrationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetAzureServiceBusIntegrationResponse> _parser = new pb::MessageParser<GetAzureServiceBusIntegrationResponse>(() => new GetAzureServiceBusIntegrationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetAzureServiceBusIntegrationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[59]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAzureServiceBusIntegrationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAzureServiceBusIntegrationResponse(GetAzureServiceBusIntegrationResponse other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetAzureServiceBusIntegrationResponse Clone() {
+      return new GetAzureServiceBusIntegrationResponse(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.AzureServiceBusIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.AzureServiceBusIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetAzureServiceBusIntegrationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetAzureServiceBusIntegrationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetAzureServiceBusIntegrationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.AzureServiceBusIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AzureServiceBusIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AzureServiceBusIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateAzureServiceBusIntegrationRequest : pb::IMessage<UpdateAzureServiceBusIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateAzureServiceBusIntegrationRequest> _parser = new pb::MessageParser<UpdateAzureServiceBusIntegrationRequest>(() => new UpdateAzureServiceBusIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateAzureServiceBusIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[60]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateAzureServiceBusIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateAzureServiceBusIntegrationRequest(UpdateAzureServiceBusIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateAzureServiceBusIntegrationRequest Clone() {
+      return new UpdateAzureServiceBusIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.AzureServiceBusIntegration integration_;
+    /// <summary>
+    /// Integration object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.AzureServiceBusIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateAzureServiceBusIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateAzureServiceBusIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateAzureServiceBusIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.AzureServiceBusIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AzureServiceBusIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.AzureServiceBusIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteAzureServiceBusIntegrationRequest : pb::IMessage<DeleteAzureServiceBusIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteAzureServiceBusIntegrationRequest> _parser = new pb::MessageParser<DeleteAzureServiceBusIntegrationRequest>(() => new DeleteAzureServiceBusIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteAzureServiceBusIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[61]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteAzureServiceBusIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteAzureServiceBusIntegrationRequest(DeleteAzureServiceBusIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteAzureServiceBusIntegrationRequest Clone() {
+      return new DeleteAzureServiceBusIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteAzureServiceBusIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteAzureServiceBusIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteAzureServiceBusIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class PilotThingsIntegration : pb::IMessage<PilotThingsIntegration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<PilotThingsIntegration> _parser = new pb::MessageParser<PilotThingsIntegration>(() => new PilotThingsIntegration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<PilotThingsIntegration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[62]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public PilotThingsIntegration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public PilotThingsIntegration(PilotThingsIntegration other) : this() {
+      applicationId_ = other.applicationId_;
+      server_ = other.server_;
+      token_ = other.token_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public PilotThingsIntegration Clone() {
+      return new PilotThingsIntegration(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "server" field.</summary>
+    public const int ServerFieldNumber = 2;
+    private string server_ = "";
+    /// <summary>
+    /// Server URL.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Server {
+      get { return server_; }
+      set {
+        server_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "token" field.</summary>
+    public const int TokenFieldNumber = 3;
+    private string token_ = "";
+    /// <summary>
+    /// Authentication token.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Token {
+      get { return token_; }
+      set {
+        token_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as PilotThingsIntegration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(PilotThingsIntegration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      if (Server != other.Server) return false;
+      if (Token != other.Token) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (Server.Length != 0) hash ^= Server.GetHashCode();
+      if (Token.Length != 0) hash ^= Token.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Server.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Server);
+      }
+      if (Token.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Token);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Server.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Server);
+      }
+      if (Token.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Token);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (Server.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Server);
+      }
+      if (Token.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Token);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(PilotThingsIntegration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.Server.Length != 0) {
+        Server = other.Server;
+      }
+      if (other.Token.Length != 0) {
+        Token = other.Token;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Server = input.ReadString();
+            break;
+          }
+          case 26: {
+            Token = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Server = input.ReadString();
+            break;
+          }
+          case 26: {
+            Token = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreatePilotThingsIntegrationRequest : pb::IMessage<CreatePilotThingsIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreatePilotThingsIntegrationRequest> _parser = new pb::MessageParser<CreatePilotThingsIntegrationRequest>(() => new CreatePilotThingsIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreatePilotThingsIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[63]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreatePilotThingsIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreatePilotThingsIntegrationRequest(CreatePilotThingsIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreatePilotThingsIntegrationRequest Clone() {
+      return new CreatePilotThingsIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.PilotThingsIntegration integration_;
+    /// <summary>
+    /// Integration object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.PilotThingsIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreatePilotThingsIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreatePilotThingsIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreatePilotThingsIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.PilotThingsIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.PilotThingsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.PilotThingsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetPilotThingsIntegrationRequest : pb::IMessage<GetPilotThingsIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetPilotThingsIntegrationRequest> _parser = new pb::MessageParser<GetPilotThingsIntegrationRequest>(() => new GetPilotThingsIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetPilotThingsIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[64]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetPilotThingsIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetPilotThingsIntegrationRequest(GetPilotThingsIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetPilotThingsIntegrationRequest Clone() {
+      return new GetPilotThingsIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetPilotThingsIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetPilotThingsIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetPilotThingsIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetPilotThingsIntegrationResponse : pb::IMessage<GetPilotThingsIntegrationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetPilotThingsIntegrationResponse> _parser = new pb::MessageParser<GetPilotThingsIntegrationResponse>(() => new GetPilotThingsIntegrationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetPilotThingsIntegrationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[65]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetPilotThingsIntegrationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetPilotThingsIntegrationResponse(GetPilotThingsIntegrationResponse other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetPilotThingsIntegrationResponse Clone() {
+      return new GetPilotThingsIntegrationResponse(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.PilotThingsIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.PilotThingsIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetPilotThingsIntegrationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetPilotThingsIntegrationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetPilotThingsIntegrationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.PilotThingsIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.PilotThingsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.PilotThingsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdatePilotThingsIntegrationRequest : pb::IMessage<UpdatePilotThingsIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdatePilotThingsIntegrationRequest> _parser = new pb::MessageParser<UpdatePilotThingsIntegrationRequest>(() => new UpdatePilotThingsIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdatePilotThingsIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[66]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdatePilotThingsIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdatePilotThingsIntegrationRequest(UpdatePilotThingsIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdatePilotThingsIntegrationRequest Clone() {
+      return new UpdatePilotThingsIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.PilotThingsIntegration integration_;
+    /// <summary>
+    /// Integration object to update.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.PilotThingsIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdatePilotThingsIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdatePilotThingsIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdatePilotThingsIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.PilotThingsIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.PilotThingsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.PilotThingsIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeletePilotThingsIntegrationRequest : pb::IMessage<DeletePilotThingsIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeletePilotThingsIntegrationRequest> _parser = new pb::MessageParser<DeletePilotThingsIntegrationRequest>(() => new DeletePilotThingsIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeletePilotThingsIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[67]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeletePilotThingsIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeletePilotThingsIntegrationRequest(DeletePilotThingsIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeletePilotThingsIntegrationRequest Clone() {
+      return new DeletePilotThingsIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeletePilotThingsIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeletePilotThingsIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeletePilotThingsIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class IftttIntegration : pb::IMessage<IftttIntegration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<IftttIntegration> _parser = new pb::MessageParser<IftttIntegration>(() => new IftttIntegration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<IftttIntegration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[68]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public IftttIntegration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public IftttIntegration(IftttIntegration other) : this() {
+      applicationId_ = other.applicationId_;
+      key_ = other.key_;
+      uplinkValues_ = other.uplinkValues_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public IftttIntegration Clone() {
+      return new IftttIntegration(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "key" field.</summary>
+    public const int KeyFieldNumber = 2;
+    private string key_ = "";
+    /// <summary>
+    /// Key.
+    /// This key can be obtained from the IFTTT Webhooks documentation page.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Key {
+      get { return key_; }
+      set {
+        key_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "uplink_values" field.</summary>
+    public const int UplinkValuesFieldNumber = 3;
+    private static readonly pb::FieldCodec<string> _repeated_uplinkValues_codec
+        = pb::FieldCodec.ForString(26);
+    private readonly pbc::RepeatedField<string> uplinkValues_ = new pbc::RepeatedField<string>();
+    /// <summary>
+    /// Values.
+    /// Up to 2 values can be forwarded to IFTTT. These values must map to the
+    /// decoded payload keys. For example:
+    /// {
+    ///   "batteryLevel": 75.3,
+    ///   "buttons": [{"pressed": false}, {"pressed": true}]
+    /// }
+    /// You would specify the following fields:
+    /// uplink_values = ["batteryLevel", "buttons_0_pressed"]
+    ///
+    /// Note: The first value is always used for the DevEUI.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<string> UplinkValues {
+      get { return uplinkValues_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as IftttIntegration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(IftttIntegration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      if (Key != other.Key) return false;
+      if(!uplinkValues_.Equals(other.uplinkValues_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (Key.Length != 0) hash ^= Key.GetHashCode();
+      hash ^= uplinkValues_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Key.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Key);
+      }
+      uplinkValues_.WriteTo(output, _repeated_uplinkValues_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (Key.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Key);
+      }
+      uplinkValues_.WriteTo(ref output, _repeated_uplinkValues_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (Key.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Key);
+      }
+      size += uplinkValues_.CalculateSize(_repeated_uplinkValues_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(IftttIntegration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.Key.Length != 0) {
+        Key = other.Key;
+      }
+      uplinkValues_.Add(other.uplinkValues_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Key = input.ReadString();
+            break;
+          }
+          case 26: {
+            uplinkValues_.AddEntriesFrom(input, _repeated_uplinkValues_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Key = input.ReadString();
+            break;
+          }
+          case 26: {
+            uplinkValues_.AddEntriesFrom(ref input, _repeated_uplinkValues_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateIftttIntegrationRequest : pb::IMessage<CreateIftttIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateIftttIntegrationRequest> _parser = new pb::MessageParser<CreateIftttIntegrationRequest>(() => new CreateIftttIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateIftttIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[69]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateIftttIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateIftttIntegrationRequest(CreateIftttIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateIftttIntegrationRequest Clone() {
+      return new CreateIftttIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.IftttIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.IftttIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateIftttIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateIftttIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateIftttIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.IftttIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.IftttIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.IftttIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetIftttIntegrationRequest : pb::IMessage<GetIftttIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetIftttIntegrationRequest> _parser = new pb::MessageParser<GetIftttIntegrationRequest>(() => new GetIftttIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetIftttIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[70]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetIftttIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetIftttIntegrationRequest(GetIftttIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetIftttIntegrationRequest Clone() {
+      return new GetIftttIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetIftttIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetIftttIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetIftttIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetIftttIntegrationResponse : pb::IMessage<GetIftttIntegrationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetIftttIntegrationResponse> _parser = new pb::MessageParser<GetIftttIntegrationResponse>(() => new GetIftttIntegrationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetIftttIntegrationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[71]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetIftttIntegrationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetIftttIntegrationResponse(GetIftttIntegrationResponse other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetIftttIntegrationResponse Clone() {
+      return new GetIftttIntegrationResponse(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.IftttIntegration integration_;
+    /// <summary>
+    /// Integration object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.IftttIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetIftttIntegrationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetIftttIntegrationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetIftttIntegrationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.IftttIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.IftttIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.IftttIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateIftttIntegrationRequest : pb::IMessage<UpdateIftttIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateIftttIntegrationRequest> _parser = new pb::MessageParser<UpdateIftttIntegrationRequest>(() => new UpdateIftttIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateIftttIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[72]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateIftttIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateIftttIntegrationRequest(UpdateIftttIntegrationRequest other) : this() {
+      integration_ = other.integration_ != null ? other.integration_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateIftttIntegrationRequest Clone() {
+      return new UpdateIftttIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "integration" field.</summary>
+    public const int IntegrationFieldNumber = 1;
+    private global::Chirpstack.Api.IftttIntegration integration_;
+    /// <summary>
+    /// Integration object to update.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.IftttIntegration Integration {
+      get { return integration_; }
+      set {
+        integration_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateIftttIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateIftttIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Integration, other.Integration)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (integration_ != null) hash ^= Integration.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (integration_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Integration);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (integration_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Integration);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateIftttIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.integration_ != null) {
+        if (integration_ == null) {
+          Integration = new global::Chirpstack.Api.IftttIntegration();
+        }
+        Integration.MergeFrom(other.Integration);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.IftttIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (integration_ == null) {
+              Integration = new global::Chirpstack.Api.IftttIntegration();
+            }
+            input.ReadMessage(Integration);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteIftttIntegrationRequest : pb::IMessage<DeleteIftttIntegrationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteIftttIntegrationRequest> _parser = new pb::MessageParser<DeleteIftttIntegrationRequest>(() => new DeleteIftttIntegrationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteIftttIntegrationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[73]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteIftttIntegrationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteIftttIntegrationRequest(DeleteIftttIntegrationRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteIftttIntegrationRequest Clone() {
+      return new DeleteIftttIntegrationRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteIftttIntegrationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteIftttIntegrationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteIftttIntegrationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GenerateMqttIntegrationClientCertificateRequest : pb::IMessage<GenerateMqttIntegrationClientCertificateRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GenerateMqttIntegrationClientCertificateRequest> _parser = new pb::MessageParser<GenerateMqttIntegrationClientCertificateRequest>(() => new GenerateMqttIntegrationClientCertificateRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GenerateMqttIntegrationClientCertificateRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[74]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateMqttIntegrationClientCertificateRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateMqttIntegrationClientCertificateRequest(GenerateMqttIntegrationClientCertificateRequest other) : this() {
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateMqttIntegrationClientCertificateRequest Clone() {
+      return new GenerateMqttIntegrationClientCertificateRequest(this);
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 1;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GenerateMqttIntegrationClientCertificateRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GenerateMqttIntegrationClientCertificateRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GenerateMqttIntegrationClientCertificateRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GenerateMqttIntegrationClientCertificateResponse : pb::IMessage<GenerateMqttIntegrationClientCertificateResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GenerateMqttIntegrationClientCertificateResponse> _parser = new pb::MessageParser<GenerateMqttIntegrationClientCertificateResponse>(() => new GenerateMqttIntegrationClientCertificateResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GenerateMqttIntegrationClientCertificateResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.MessageTypes[75]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateMqttIntegrationClientCertificateResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateMqttIntegrationClientCertificateResponse(GenerateMqttIntegrationClientCertificateResponse other) : this() {
+      tlsCert_ = other.tlsCert_;
+      tlsKey_ = other.tlsKey_;
+      caCert_ = other.caCert_;
+      expiresAt_ = other.expiresAt_ != null ? other.expiresAt_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateMqttIntegrationClientCertificateResponse Clone() {
+      return new GenerateMqttIntegrationClientCertificateResponse(this);
+    }
+
+    /// <summary>Field number for the "tls_cert" field.</summary>
+    public const int TlsCertFieldNumber = 1;
+    private string tlsCert_ = "";
+    /// <summary>
+    /// TLS certificate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TlsCert {
+      get { return tlsCert_; }
+      set {
+        tlsCert_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tls_key" field.</summary>
+    public const int TlsKeyFieldNumber = 2;
+    private string tlsKey_ = "";
+    /// <summary>
+    /// TLS key.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TlsKey {
+      get { return tlsKey_; }
+      set {
+        tlsKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "ca_cert" field.</summary>
+    public const int CaCertFieldNumber = 3;
+    private string caCert_ = "";
+    /// <summary>
+    /// CA certificate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string CaCert {
+      get { return caCert_; }
+      set {
+        caCert_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "expires_at" field.</summary>
+    public const int ExpiresAtFieldNumber = 4;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp expiresAt_;
+    /// <summary>
+    /// Expires at defines the expiration date of the certificate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp ExpiresAt {
+      get { return expiresAt_; }
+      set {
+        expiresAt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GenerateMqttIntegrationClientCertificateResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GenerateMqttIntegrationClientCertificateResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TlsCert != other.TlsCert) return false;
+      if (TlsKey != other.TlsKey) return false;
+      if (CaCert != other.CaCert) return false;
+      if (!object.Equals(ExpiresAt, other.ExpiresAt)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TlsCert.Length != 0) hash ^= TlsCert.GetHashCode();
+      if (TlsKey.Length != 0) hash ^= TlsKey.GetHashCode();
+      if (CaCert.Length != 0) hash ^= CaCert.GetHashCode();
+      if (expiresAt_ != null) hash ^= ExpiresAt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TlsCert.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TlsCert);
+      }
+      if (TlsKey.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(TlsKey);
+      }
+      if (CaCert.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(CaCert);
+      }
+      if (expiresAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(ExpiresAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TlsCert.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TlsCert);
+      }
+      if (TlsKey.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(TlsKey);
+      }
+      if (CaCert.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(CaCert);
+      }
+      if (expiresAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(ExpiresAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TlsCert.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TlsCert);
+      }
+      if (TlsKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TlsKey);
+      }
+      if (CaCert.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(CaCert);
+      }
+      if (expiresAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ExpiresAt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GenerateMqttIntegrationClientCertificateResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TlsCert.Length != 0) {
+        TlsCert = other.TlsCert;
+      }
+      if (other.TlsKey.Length != 0) {
+        TlsKey = other.TlsKey;
+      }
+      if (other.CaCert.Length != 0) {
+        CaCert = other.CaCert;
+      }
+      if (other.expiresAt_ != null) {
+        if (expiresAt_ == null) {
+          ExpiresAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        ExpiresAt.MergeFrom(other.ExpiresAt);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TlsCert = input.ReadString();
+            break;
+          }
+          case 18: {
+            TlsKey = input.ReadString();
+            break;
+          }
+          case 26: {
+            CaCert = input.ReadString();
+            break;
+          }
+          case 34: {
+            if (expiresAt_ == null) {
+              ExpiresAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(ExpiresAt);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TlsCert = input.ReadString();
+            break;
+          }
+          case 18: {
+            TlsKey = input.ReadString();
+            break;
+          }
+          case 26: {
+            CaCert = input.ReadString();
+            break;
+          }
+          case 34: {
+            if (expiresAt_ == null) {
+              ExpiresAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(ExpiresAt);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/ApplicationGrpc.cs b/api/csharp/Chirpstack/api/ApplicationGrpc.cs
new file mode 100644
index 00000000..7e6e9098
--- /dev/null
+++ b/api/csharp/Chirpstack/api/ApplicationGrpc.cs
@@ -0,0 +1,3534 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/application.proto
+// </auto-generated>
+#pragma warning disable 0414, 1591, 8981
+#region Designer generated code
+
+using grpc = global::Grpc.Core;
+
+namespace Chirpstack.Api {
+  /// <summary>
+  /// ApplicationService is the service providing API methods for managing applications.
+  /// </summary>
+  public static partial class ApplicationService
+  {
+    static readonly string __ServiceName = "api.ApplicationService";
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (message is global::Google.Protobuf.IBufferMessage)
+      {
+        context.SetPayloadLength(message.CalculateSize());
+        global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
+        context.Complete();
+        return;
+      }
+      #endif
+      context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static class __Helper_MessageCache<T>
+    {
+      public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (__Helper_MessageCache<T>.IsBufferMessage)
+      {
+        return parser.ParseFrom(context.PayloadAsReadOnlySequence());
+      }
+      #endif
+      return parser.ParseFrom(context.PayloadAsNewBuffer());
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateApplicationRequest> __Marshaller_api_CreateApplicationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateApplicationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateApplicationResponse> __Marshaller_api_CreateApplicationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateApplicationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetApplicationRequest> __Marshaller_api_GetApplicationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetApplicationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetApplicationResponse> __Marshaller_api_GetApplicationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetApplicationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateApplicationRequest> __Marshaller_api_UpdateApplicationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateApplicationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Google.Protobuf.WellKnownTypes.Empty> __Marshaller_google_protobuf_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Google.Protobuf.WellKnownTypes.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteApplicationRequest> __Marshaller_api_DeleteApplicationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteApplicationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListApplicationsRequest> __Marshaller_api_ListApplicationsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListApplicationsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListApplicationsResponse> __Marshaller_api_ListApplicationsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListApplicationsResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListIntegrationsRequest> __Marshaller_api_ListIntegrationsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListIntegrationsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListIntegrationsResponse> __Marshaller_api_ListIntegrationsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListIntegrationsResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateHttpIntegrationRequest> __Marshaller_api_CreateHttpIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateHttpIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetHttpIntegrationRequest> __Marshaller_api_GetHttpIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetHttpIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetHttpIntegrationResponse> __Marshaller_api_GetHttpIntegrationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetHttpIntegrationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateHttpIntegrationRequest> __Marshaller_api_UpdateHttpIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateHttpIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteHttpIntegrationRequest> __Marshaller_api_DeleteHttpIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteHttpIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateInfluxDbIntegrationRequest> __Marshaller_api_CreateInfluxDbIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateInfluxDbIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetInfluxDbIntegrationRequest> __Marshaller_api_GetInfluxDbIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetInfluxDbIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetInfluxDbIntegrationResponse> __Marshaller_api_GetInfluxDbIntegrationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetInfluxDbIntegrationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest> __Marshaller_api_UpdateInfluxDbIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest> __Marshaller_api_DeleteInfluxDbIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateThingsBoardIntegrationRequest> __Marshaller_api_CreateThingsBoardIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateThingsBoardIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetThingsBoardIntegrationRequest> __Marshaller_api_GetThingsBoardIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetThingsBoardIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetThingsBoardIntegrationResponse> __Marshaller_api_GetThingsBoardIntegrationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetThingsBoardIntegrationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest> __Marshaller_api_UpdateThingsBoardIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest> __Marshaller_api_DeleteThingsBoardIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateMyDevicesIntegrationRequest> __Marshaller_api_CreateMyDevicesIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateMyDevicesIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetMyDevicesIntegrationRequest> __Marshaller_api_GetMyDevicesIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetMyDevicesIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetMyDevicesIntegrationResponse> __Marshaller_api_GetMyDevicesIntegrationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetMyDevicesIntegrationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest> __Marshaller_api_UpdateMyDevicesIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest> __Marshaller_api_DeleteMyDevicesIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateLoraCloudIntegrationRequest> __Marshaller_api_CreateLoraCloudIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateLoraCloudIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetLoraCloudIntegrationRequest> __Marshaller_api_GetLoraCloudIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetLoraCloudIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetLoraCloudIntegrationResponse> __Marshaller_api_GetLoraCloudIntegrationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetLoraCloudIntegrationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest> __Marshaller_api_UpdateLoraCloudIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest> __Marshaller_api_DeleteLoraCloudIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest> __Marshaller_api_CreateGcpPubSubIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetGcpPubSubIntegrationRequest> __Marshaller_api_GetGcpPubSubIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetGcpPubSubIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetGcpPubSubIntegrationResponse> __Marshaller_api_GetGcpPubSubIntegrationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetGcpPubSubIntegrationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest> __Marshaller_api_UpdateGcpPubSubIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest> __Marshaller_api_DeleteGcpPubSubIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateAwsSnsIntegrationRequest> __Marshaller_api_CreateAwsSnsIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateAwsSnsIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetAwsSnsIntegrationRequest> __Marshaller_api_GetAwsSnsIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetAwsSnsIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetAwsSnsIntegrationResponse> __Marshaller_api_GetAwsSnsIntegrationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetAwsSnsIntegrationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest> __Marshaller_api_UpdateAwsSnsIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest> __Marshaller_api_DeleteAwsSnsIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest> __Marshaller_api_CreateAzureServiceBusIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest> __Marshaller_api_GetAzureServiceBusIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse> __Marshaller_api_GetAzureServiceBusIntegrationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest> __Marshaller_api_UpdateAzureServiceBusIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest> __Marshaller_api_DeleteAzureServiceBusIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreatePilotThingsIntegrationRequest> __Marshaller_api_CreatePilotThingsIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreatePilotThingsIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetPilotThingsIntegrationRequest> __Marshaller_api_GetPilotThingsIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetPilotThingsIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetPilotThingsIntegrationResponse> __Marshaller_api_GetPilotThingsIntegrationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetPilotThingsIntegrationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest> __Marshaller_api_UpdatePilotThingsIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeletePilotThingsIntegrationRequest> __Marshaller_api_DeletePilotThingsIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeletePilotThingsIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateIftttIntegrationRequest> __Marshaller_api_CreateIftttIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateIftttIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetIftttIntegrationRequest> __Marshaller_api_GetIftttIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetIftttIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetIftttIntegrationResponse> __Marshaller_api_GetIftttIntegrationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetIftttIntegrationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateIftttIntegrationRequest> __Marshaller_api_UpdateIftttIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateIftttIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteIftttIntegrationRequest> __Marshaller_api_DeleteIftttIntegrationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteIftttIntegrationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest> __Marshaller_api_GenerateMqttIntegrationClientCertificateRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse> __Marshaller_api_GenerateMqttIntegrationClientCertificateResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse.Parser));
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateApplicationRequest, global::Chirpstack.Api.CreateApplicationResponse> __Method_Create = new grpc::Method<global::Chirpstack.Api.CreateApplicationRequest, global::Chirpstack.Api.CreateApplicationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Create",
+        __Marshaller_api_CreateApplicationRequest,
+        __Marshaller_api_CreateApplicationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetApplicationRequest, global::Chirpstack.Api.GetApplicationResponse> __Method_Get = new grpc::Method<global::Chirpstack.Api.GetApplicationRequest, global::Chirpstack.Api.GetApplicationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Get",
+        __Marshaller_api_GetApplicationRequest,
+        __Marshaller_api_GetApplicationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateApplicationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Update = new grpc::Method<global::Chirpstack.Api.UpdateApplicationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Update",
+        __Marshaller_api_UpdateApplicationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteApplicationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Delete = new grpc::Method<global::Chirpstack.Api.DeleteApplicationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Delete",
+        __Marshaller_api_DeleteApplicationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListApplicationsRequest, global::Chirpstack.Api.ListApplicationsResponse> __Method_List = new grpc::Method<global::Chirpstack.Api.ListApplicationsRequest, global::Chirpstack.Api.ListApplicationsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "List",
+        __Marshaller_api_ListApplicationsRequest,
+        __Marshaller_api_ListApplicationsResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListIntegrationsRequest, global::Chirpstack.Api.ListIntegrationsResponse> __Method_ListIntegrations = new grpc::Method<global::Chirpstack.Api.ListIntegrationsRequest, global::Chirpstack.Api.ListIntegrationsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "ListIntegrations",
+        __Marshaller_api_ListIntegrationsRequest,
+        __Marshaller_api_ListIntegrationsResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateHttpIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreateHttpIntegration = new grpc::Method<global::Chirpstack.Api.CreateHttpIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateHttpIntegration",
+        __Marshaller_api_CreateHttpIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetHttpIntegrationRequest, global::Chirpstack.Api.GetHttpIntegrationResponse> __Method_GetHttpIntegration = new grpc::Method<global::Chirpstack.Api.GetHttpIntegrationRequest, global::Chirpstack.Api.GetHttpIntegrationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetHttpIntegration",
+        __Marshaller_api_GetHttpIntegrationRequest,
+        __Marshaller_api_GetHttpIntegrationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateHttpIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateHttpIntegration = new grpc::Method<global::Chirpstack.Api.UpdateHttpIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateHttpIntegration",
+        __Marshaller_api_UpdateHttpIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteHttpIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteHttpIntegration = new grpc::Method<global::Chirpstack.Api.DeleteHttpIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteHttpIntegration",
+        __Marshaller_api_DeleteHttpIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateInfluxDbIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreateInfluxDbIntegration = new grpc::Method<global::Chirpstack.Api.CreateInfluxDbIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateInfluxDbIntegration",
+        __Marshaller_api_CreateInfluxDbIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetInfluxDbIntegrationRequest, global::Chirpstack.Api.GetInfluxDbIntegrationResponse> __Method_GetInfluxDbIntegration = new grpc::Method<global::Chirpstack.Api.GetInfluxDbIntegrationRequest, global::Chirpstack.Api.GetInfluxDbIntegrationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetInfluxDbIntegration",
+        __Marshaller_api_GetInfluxDbIntegrationRequest,
+        __Marshaller_api_GetInfluxDbIntegrationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateInfluxDbIntegration = new grpc::Method<global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateInfluxDbIntegration",
+        __Marshaller_api_UpdateInfluxDbIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteInfluxDbIntegration = new grpc::Method<global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteInfluxDbIntegration",
+        __Marshaller_api_DeleteInfluxDbIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateThingsBoardIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreateThingsBoardIntegration = new grpc::Method<global::Chirpstack.Api.CreateThingsBoardIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateThingsBoardIntegration",
+        __Marshaller_api_CreateThingsBoardIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetThingsBoardIntegrationRequest, global::Chirpstack.Api.GetThingsBoardIntegrationResponse> __Method_GetThingsBoardIntegration = new grpc::Method<global::Chirpstack.Api.GetThingsBoardIntegrationRequest, global::Chirpstack.Api.GetThingsBoardIntegrationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetThingsBoardIntegration",
+        __Marshaller_api_GetThingsBoardIntegrationRequest,
+        __Marshaller_api_GetThingsBoardIntegrationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateThingsBoardIntegration = new grpc::Method<global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateThingsBoardIntegration",
+        __Marshaller_api_UpdateThingsBoardIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteThingsBoardIntegration = new grpc::Method<global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteThingsBoardIntegration",
+        __Marshaller_api_DeleteThingsBoardIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateMyDevicesIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreateMyDevicesIntegration = new grpc::Method<global::Chirpstack.Api.CreateMyDevicesIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateMyDevicesIntegration",
+        __Marshaller_api_CreateMyDevicesIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetMyDevicesIntegrationRequest, global::Chirpstack.Api.GetMyDevicesIntegrationResponse> __Method_GetMyDevicesIntegration = new grpc::Method<global::Chirpstack.Api.GetMyDevicesIntegrationRequest, global::Chirpstack.Api.GetMyDevicesIntegrationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetMyDevicesIntegration",
+        __Marshaller_api_GetMyDevicesIntegrationRequest,
+        __Marshaller_api_GetMyDevicesIntegrationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateMyDevicesIntegration = new grpc::Method<global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateMyDevicesIntegration",
+        __Marshaller_api_UpdateMyDevicesIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteMyDevicesIntegration = new grpc::Method<global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteMyDevicesIntegration",
+        __Marshaller_api_DeleteMyDevicesIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateLoraCloudIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreateLoraCloudIntegration = new grpc::Method<global::Chirpstack.Api.CreateLoraCloudIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateLoraCloudIntegration",
+        __Marshaller_api_CreateLoraCloudIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetLoraCloudIntegrationRequest, global::Chirpstack.Api.GetLoraCloudIntegrationResponse> __Method_GetLoraCloudIntegration = new grpc::Method<global::Chirpstack.Api.GetLoraCloudIntegrationRequest, global::Chirpstack.Api.GetLoraCloudIntegrationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetLoraCloudIntegration",
+        __Marshaller_api_GetLoraCloudIntegrationRequest,
+        __Marshaller_api_GetLoraCloudIntegrationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateLoraCloudIntegration = new grpc::Method<global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateLoraCloudIntegration",
+        __Marshaller_api_UpdateLoraCloudIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteLoraCloudIntegration = new grpc::Method<global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteLoraCloudIntegration",
+        __Marshaller_api_DeleteLoraCloudIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreateGcpPubSubIntegration = new grpc::Method<global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateGcpPubSubIntegration",
+        __Marshaller_api_CreateGcpPubSubIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetGcpPubSubIntegrationRequest, global::Chirpstack.Api.GetGcpPubSubIntegrationResponse> __Method_GetGcpPubSubIntegration = new grpc::Method<global::Chirpstack.Api.GetGcpPubSubIntegrationRequest, global::Chirpstack.Api.GetGcpPubSubIntegrationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetGcpPubSubIntegration",
+        __Marshaller_api_GetGcpPubSubIntegrationRequest,
+        __Marshaller_api_GetGcpPubSubIntegrationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateGcpPubSubIntegration = new grpc::Method<global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateGcpPubSubIntegration",
+        __Marshaller_api_UpdateGcpPubSubIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteGcpPubSubIntegration = new grpc::Method<global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteGcpPubSubIntegration",
+        __Marshaller_api_DeleteGcpPubSubIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateAwsSnsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreateAwsSnsIntegration = new grpc::Method<global::Chirpstack.Api.CreateAwsSnsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateAwsSnsIntegration",
+        __Marshaller_api_CreateAwsSnsIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetAwsSnsIntegrationRequest, global::Chirpstack.Api.GetAwsSnsIntegrationResponse> __Method_GetAwsSnsIntegration = new grpc::Method<global::Chirpstack.Api.GetAwsSnsIntegrationRequest, global::Chirpstack.Api.GetAwsSnsIntegrationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetAwsSnsIntegration",
+        __Marshaller_api_GetAwsSnsIntegrationRequest,
+        __Marshaller_api_GetAwsSnsIntegrationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateAwsSnsIntegration = new grpc::Method<global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateAwsSnsIntegration",
+        __Marshaller_api_UpdateAwsSnsIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteAwsSnsIntegration = new grpc::Method<global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteAwsSnsIntegration",
+        __Marshaller_api_DeleteAwsSnsIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreateAzureServiceBusIntegration = new grpc::Method<global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateAzureServiceBusIntegration",
+        __Marshaller_api_CreateAzureServiceBusIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest, global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse> __Method_GetAzureServiceBusIntegration = new grpc::Method<global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest, global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetAzureServiceBusIntegration",
+        __Marshaller_api_GetAzureServiceBusIntegrationRequest,
+        __Marshaller_api_GetAzureServiceBusIntegrationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateAzureServiceBusIntegration = new grpc::Method<global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateAzureServiceBusIntegration",
+        __Marshaller_api_UpdateAzureServiceBusIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteAzureServiceBusIntegration = new grpc::Method<global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteAzureServiceBusIntegration",
+        __Marshaller_api_DeleteAzureServiceBusIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreatePilotThingsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreatePilotThingsIntegration = new grpc::Method<global::Chirpstack.Api.CreatePilotThingsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreatePilotThingsIntegration",
+        __Marshaller_api_CreatePilotThingsIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetPilotThingsIntegrationRequest, global::Chirpstack.Api.GetPilotThingsIntegrationResponse> __Method_GetPilotThingsIntegration = new grpc::Method<global::Chirpstack.Api.GetPilotThingsIntegrationRequest, global::Chirpstack.Api.GetPilotThingsIntegrationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetPilotThingsIntegration",
+        __Marshaller_api_GetPilotThingsIntegrationRequest,
+        __Marshaller_api_GetPilotThingsIntegrationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdatePilotThingsIntegration = new grpc::Method<global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdatePilotThingsIntegration",
+        __Marshaller_api_UpdatePilotThingsIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeletePilotThingsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeletePilotThingsIntegration = new grpc::Method<global::Chirpstack.Api.DeletePilotThingsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeletePilotThingsIntegration",
+        __Marshaller_api_DeletePilotThingsIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateIftttIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreateIftttIntegration = new grpc::Method<global::Chirpstack.Api.CreateIftttIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateIftttIntegration",
+        __Marshaller_api_CreateIftttIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetIftttIntegrationRequest, global::Chirpstack.Api.GetIftttIntegrationResponse> __Method_GetIftttIntegration = new grpc::Method<global::Chirpstack.Api.GetIftttIntegrationRequest, global::Chirpstack.Api.GetIftttIntegrationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetIftttIntegration",
+        __Marshaller_api_GetIftttIntegrationRequest,
+        __Marshaller_api_GetIftttIntegrationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateIftttIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateIftttIntegration = new grpc::Method<global::Chirpstack.Api.UpdateIftttIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateIftttIntegration",
+        __Marshaller_api_UpdateIftttIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteIftttIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteIftttIntegration = new grpc::Method<global::Chirpstack.Api.DeleteIftttIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteIftttIntegration",
+        __Marshaller_api_DeleteIftttIntegrationRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest, global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse> __Method_GenerateMqttIntegrationClientCertificate = new grpc::Method<global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest, global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GenerateMqttIntegrationClientCertificate",
+        __Marshaller_api_GenerateMqttIntegrationClientCertificateRequest,
+        __Marshaller_api_GenerateMqttIntegrationClientCertificateResponse);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Chirpstack.Api.ApplicationReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of ApplicationService</summary>
+    [grpc::BindServiceMethod(typeof(ApplicationService), "BindService")]
+    public abstract partial class ApplicationServiceBase
+    {
+      /// <summary>
+      /// Create creates the given application.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.CreateApplicationResponse> Create(global::Chirpstack.Api.CreateApplicationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the application for the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetApplicationResponse> Get(global::Chirpstack.Api.GetApplicationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update updates the given application.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Update(global::Chirpstack.Api.UpdateApplicationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete the application for the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Delete(global::Chirpstack.Api.DeleteApplicationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the list of applications.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListApplicationsResponse> List(global::Chirpstack.Api.ListApplicationsRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// List all configured integrations.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListIntegrationsResponse> ListIntegrations(global::Chirpstack.Api.ListIntegrationsRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create HTTP integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreateHttpIntegration(global::Chirpstack.Api.CreateHttpIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the configured HTTP integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetHttpIntegrationResponse> GetHttpIntegration(global::Chirpstack.Api.GetHttpIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update the HTTP integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateHttpIntegration(global::Chirpstack.Api.UpdateHttpIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete the HTTP integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteHttpIntegration(global::Chirpstack.Api.DeleteHttpIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreateInfluxDbIntegration(global::Chirpstack.Api.CreateInfluxDbIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetInfluxDbIntegrationResponse> GetInfluxDbIntegration(global::Chirpstack.Api.GetInfluxDbIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateInfluxDbIntegration(global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteInfluxDbIntegration(global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreateThingsBoardIntegration(global::Chirpstack.Api.CreateThingsBoardIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetThingsBoardIntegrationResponse> GetThingsBoardIntegration(global::Chirpstack.Api.GetThingsBoardIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateThingsBoardIntegration(global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteThingsBoardIntegration(global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create myDevices integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreateMyDevicesIntegration(global::Chirpstack.Api.CreateMyDevicesIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get myDevices integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetMyDevicesIntegrationResponse> GetMyDevicesIntegration(global::Chirpstack.Api.GetMyDevicesIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update myDevices integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateMyDevicesIntegration(global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete myDevices integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteMyDevicesIntegration(global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreateLoraCloudIntegration(global::Chirpstack.Api.CreateLoraCloudIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetLoraCloudIntegrationResponse> GetLoraCloudIntegration(global::Chirpstack.Api.GetLoraCloudIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateLoraCloudIntegration(global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteLoraCloudIntegration(global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreateGcpPubSubIntegration(global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetGcpPubSubIntegrationResponse> GetGcpPubSubIntegration(global::Chirpstack.Api.GetGcpPubSubIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateGcpPubSubIntegration(global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteGcpPubSubIntegration(global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreateAwsSnsIntegration(global::Chirpstack.Api.CreateAwsSnsIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetAwsSnsIntegrationResponse> GetAwsSnsIntegration(global::Chirpstack.Api.GetAwsSnsIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAwsSnsIntegration(global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAwsSnsIntegration(global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreateAzureServiceBusIntegration(global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse> GetAzureServiceBusIntegration(global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAzureServiceBusIntegration(global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAzureServiceBusIntegration(global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreatePilotThingsIntegration(global::Chirpstack.Api.CreatePilotThingsIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetPilotThingsIntegrationResponse> GetPilotThingsIntegration(global::Chirpstack.Api.GetPilotThingsIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdatePilotThingsIntegration(global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeletePilotThingsIntegration(global::Chirpstack.Api.DeletePilotThingsIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreateIftttIntegration(global::Chirpstack.Api.CreateIftttIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetIftttIntegrationResponse> GetIftttIntegration(global::Chirpstack.Api.GetIftttIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateIftttIntegration(global::Chirpstack.Api.UpdateIftttIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteIftttIntegration(global::Chirpstack.Api.DeleteIftttIntegrationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Generates application ID specific client-certificate.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse> GenerateMqttIntegrationClientCertificate(global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for ApplicationService</summary>
+    public partial class ApplicationServiceClient : grpc::ClientBase<ApplicationServiceClient>
+    {
+      /// <summary>Creates a new client for ApplicationService</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public ApplicationServiceClient(grpc::ChannelBase channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for ApplicationService that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public ApplicationServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected ApplicationServiceClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected ApplicationServiceClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      /// Create creates the given application.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateApplicationResponse Create(global::Chirpstack.Api.CreateApplicationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Create(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create creates the given application.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateApplicationResponse Create(global::Chirpstack.Api.CreateApplicationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Create creates the given application.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateApplicationResponse> CreateAsync(global::Chirpstack.Api.CreateApplicationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create creates the given application.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateApplicationResponse> CreateAsync(global::Chirpstack.Api.CreateApplicationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Get the application for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetApplicationResponse Get(global::Chirpstack.Api.GetApplicationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Get(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the application for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetApplicationResponse Get(global::Chirpstack.Api.GetApplicationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Get the application for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetApplicationResponse> GetAsync(global::Chirpstack.Api.GetApplicationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the application for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetApplicationResponse> GetAsync(global::Chirpstack.Api.GetApplicationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Update updates the given application.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateApplicationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Update(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update updates the given application.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateApplicationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Update updates the given application.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateApplicationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update updates the given application.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateApplicationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Delete the application for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteApplicationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Delete(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the application for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteApplicationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Delete the application for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteApplicationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the application for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteApplicationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of applications.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListApplicationsResponse List(global::Chirpstack.Api.ListApplicationsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return List(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of applications.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListApplicationsResponse List(global::Chirpstack.Api.ListApplicationsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of applications.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListApplicationsResponse> ListAsync(global::Chirpstack.Api.ListApplicationsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of applications.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListApplicationsResponse> ListAsync(global::Chirpstack.Api.ListApplicationsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// List all configured integrations.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListIntegrationsResponse ListIntegrations(global::Chirpstack.Api.ListIntegrationsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListIntegrations(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List all configured integrations.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListIntegrationsResponse ListIntegrations(global::Chirpstack.Api.ListIntegrationsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_ListIntegrations, null, options, request);
+      }
+      /// <summary>
+      /// List all configured integrations.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListIntegrationsResponse> ListIntegrationsAsync(global::Chirpstack.Api.ListIntegrationsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListIntegrationsAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List all configured integrations.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListIntegrationsResponse> ListIntegrationsAsync(global::Chirpstack.Api.ListIntegrationsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_ListIntegrations, null, options, request);
+      }
+      /// <summary>
+      /// Create HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateHttpIntegration(global::Chirpstack.Api.CreateHttpIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateHttpIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateHttpIntegration(global::Chirpstack.Api.CreateHttpIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateHttpIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateHttpIntegrationAsync(global::Chirpstack.Api.CreateHttpIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateHttpIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateHttpIntegrationAsync(global::Chirpstack.Api.CreateHttpIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateHttpIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get the configured HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetHttpIntegrationResponse GetHttpIntegration(global::Chirpstack.Api.GetHttpIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetHttpIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the configured HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetHttpIntegrationResponse GetHttpIntegration(global::Chirpstack.Api.GetHttpIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetHttpIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get the configured HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetHttpIntegrationResponse> GetHttpIntegrationAsync(global::Chirpstack.Api.GetHttpIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetHttpIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the configured HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetHttpIntegrationResponse> GetHttpIntegrationAsync(global::Chirpstack.Api.GetHttpIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetHttpIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update the HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateHttpIntegration(global::Chirpstack.Api.UpdateHttpIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateHttpIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateHttpIntegration(global::Chirpstack.Api.UpdateHttpIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateHttpIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update the HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateHttpIntegrationAsync(global::Chirpstack.Api.UpdateHttpIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateHttpIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateHttpIntegrationAsync(global::Chirpstack.Api.UpdateHttpIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateHttpIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete the HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteHttpIntegration(global::Chirpstack.Api.DeleteHttpIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteHttpIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteHttpIntegration(global::Chirpstack.Api.DeleteHttpIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteHttpIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete the HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteHttpIntegrationAsync(global::Chirpstack.Api.DeleteHttpIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteHttpIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the HTTP integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteHttpIntegrationAsync(global::Chirpstack.Api.DeleteHttpIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteHttpIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateInfluxDbIntegration(global::Chirpstack.Api.CreateInfluxDbIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateInfluxDbIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateInfluxDbIntegration(global::Chirpstack.Api.CreateInfluxDbIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateInfluxDbIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateInfluxDbIntegrationAsync(global::Chirpstack.Api.CreateInfluxDbIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateInfluxDbIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateInfluxDbIntegrationAsync(global::Chirpstack.Api.CreateInfluxDbIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateInfluxDbIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetInfluxDbIntegrationResponse GetInfluxDbIntegration(global::Chirpstack.Api.GetInfluxDbIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetInfluxDbIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetInfluxDbIntegrationResponse GetInfluxDbIntegration(global::Chirpstack.Api.GetInfluxDbIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetInfluxDbIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetInfluxDbIntegrationResponse> GetInfluxDbIntegrationAsync(global::Chirpstack.Api.GetInfluxDbIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetInfluxDbIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetInfluxDbIntegrationResponse> GetInfluxDbIntegrationAsync(global::Chirpstack.Api.GetInfluxDbIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetInfluxDbIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateInfluxDbIntegration(global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateInfluxDbIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateInfluxDbIntegration(global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateInfluxDbIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateInfluxDbIntegrationAsync(global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateInfluxDbIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateInfluxDbIntegrationAsync(global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateInfluxDbIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteInfluxDbIntegration(global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteInfluxDbIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteInfluxDbIntegration(global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteInfluxDbIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteInfluxDbIntegrationAsync(global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteInfluxDbIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete InfluxDb integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteInfluxDbIntegrationAsync(global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteInfluxDbIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateThingsBoardIntegration(global::Chirpstack.Api.CreateThingsBoardIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateThingsBoardIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateThingsBoardIntegration(global::Chirpstack.Api.CreateThingsBoardIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateThingsBoardIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateThingsBoardIntegrationAsync(global::Chirpstack.Api.CreateThingsBoardIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateThingsBoardIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateThingsBoardIntegrationAsync(global::Chirpstack.Api.CreateThingsBoardIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateThingsBoardIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetThingsBoardIntegrationResponse GetThingsBoardIntegration(global::Chirpstack.Api.GetThingsBoardIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetThingsBoardIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetThingsBoardIntegrationResponse GetThingsBoardIntegration(global::Chirpstack.Api.GetThingsBoardIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetThingsBoardIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetThingsBoardIntegrationResponse> GetThingsBoardIntegrationAsync(global::Chirpstack.Api.GetThingsBoardIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetThingsBoardIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetThingsBoardIntegrationResponse> GetThingsBoardIntegrationAsync(global::Chirpstack.Api.GetThingsBoardIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetThingsBoardIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateThingsBoardIntegration(global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateThingsBoardIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateThingsBoardIntegration(global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateThingsBoardIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateThingsBoardIntegrationAsync(global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateThingsBoardIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateThingsBoardIntegrationAsync(global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateThingsBoardIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteThingsBoardIntegration(global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteThingsBoardIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteThingsBoardIntegration(global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteThingsBoardIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteThingsBoardIntegrationAsync(global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteThingsBoardIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete ThingsBoard integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteThingsBoardIntegrationAsync(global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteThingsBoardIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateMyDevicesIntegration(global::Chirpstack.Api.CreateMyDevicesIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateMyDevicesIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateMyDevicesIntegration(global::Chirpstack.Api.CreateMyDevicesIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateMyDevicesIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateMyDevicesIntegrationAsync(global::Chirpstack.Api.CreateMyDevicesIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateMyDevicesIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateMyDevicesIntegrationAsync(global::Chirpstack.Api.CreateMyDevicesIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateMyDevicesIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetMyDevicesIntegrationResponse GetMyDevicesIntegration(global::Chirpstack.Api.GetMyDevicesIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetMyDevicesIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetMyDevicesIntegrationResponse GetMyDevicesIntegration(global::Chirpstack.Api.GetMyDevicesIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetMyDevicesIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetMyDevicesIntegrationResponse> GetMyDevicesIntegrationAsync(global::Chirpstack.Api.GetMyDevicesIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetMyDevicesIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetMyDevicesIntegrationResponse> GetMyDevicesIntegrationAsync(global::Chirpstack.Api.GetMyDevicesIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetMyDevicesIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateMyDevicesIntegration(global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateMyDevicesIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateMyDevicesIntegration(global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateMyDevicesIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateMyDevicesIntegrationAsync(global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateMyDevicesIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateMyDevicesIntegrationAsync(global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateMyDevicesIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteMyDevicesIntegration(global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteMyDevicesIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteMyDevicesIntegration(global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteMyDevicesIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteMyDevicesIntegrationAsync(global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteMyDevicesIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete myDevices integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteMyDevicesIntegrationAsync(global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteMyDevicesIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateLoraCloudIntegration(global::Chirpstack.Api.CreateLoraCloudIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateLoraCloudIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateLoraCloudIntegration(global::Chirpstack.Api.CreateLoraCloudIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateLoraCloudIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateLoraCloudIntegrationAsync(global::Chirpstack.Api.CreateLoraCloudIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateLoraCloudIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateLoraCloudIntegrationAsync(global::Chirpstack.Api.CreateLoraCloudIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateLoraCloudIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetLoraCloudIntegrationResponse GetLoraCloudIntegration(global::Chirpstack.Api.GetLoraCloudIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetLoraCloudIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetLoraCloudIntegrationResponse GetLoraCloudIntegration(global::Chirpstack.Api.GetLoraCloudIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetLoraCloudIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetLoraCloudIntegrationResponse> GetLoraCloudIntegrationAsync(global::Chirpstack.Api.GetLoraCloudIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetLoraCloudIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetLoraCloudIntegrationResponse> GetLoraCloudIntegrationAsync(global::Chirpstack.Api.GetLoraCloudIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetLoraCloudIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateLoraCloudIntegration(global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateLoraCloudIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateLoraCloudIntegration(global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateLoraCloudIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateLoraCloudIntegrationAsync(global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateLoraCloudIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateLoraCloudIntegrationAsync(global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateLoraCloudIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteLoraCloudIntegration(global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteLoraCloudIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteLoraCloudIntegration(global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteLoraCloudIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteLoraCloudIntegrationAsync(global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteLoraCloudIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete LoRaCloud integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteLoraCloudIntegrationAsync(global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteLoraCloudIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateGcpPubSubIntegration(global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateGcpPubSubIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateGcpPubSubIntegration(global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateGcpPubSubIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateGcpPubSubIntegrationAsync(global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateGcpPubSubIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateGcpPubSubIntegrationAsync(global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateGcpPubSubIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetGcpPubSubIntegrationResponse GetGcpPubSubIntegration(global::Chirpstack.Api.GetGcpPubSubIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetGcpPubSubIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetGcpPubSubIntegrationResponse GetGcpPubSubIntegration(global::Chirpstack.Api.GetGcpPubSubIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetGcpPubSubIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetGcpPubSubIntegrationResponse> GetGcpPubSubIntegrationAsync(global::Chirpstack.Api.GetGcpPubSubIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetGcpPubSubIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetGcpPubSubIntegrationResponse> GetGcpPubSubIntegrationAsync(global::Chirpstack.Api.GetGcpPubSubIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetGcpPubSubIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateGcpPubSubIntegration(global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateGcpPubSubIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateGcpPubSubIntegration(global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateGcpPubSubIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateGcpPubSubIntegrationAsync(global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateGcpPubSubIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateGcpPubSubIntegrationAsync(global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateGcpPubSubIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteGcpPubSubIntegration(global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteGcpPubSubIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteGcpPubSubIntegration(global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteGcpPubSubIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteGcpPubSubIntegrationAsync(global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteGcpPubSubIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete GCP Pub/Sub integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteGcpPubSubIntegrationAsync(global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteGcpPubSubIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateAwsSnsIntegration(global::Chirpstack.Api.CreateAwsSnsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAwsSnsIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateAwsSnsIntegration(global::Chirpstack.Api.CreateAwsSnsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateAwsSnsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateAwsSnsIntegrationAsync(global::Chirpstack.Api.CreateAwsSnsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAwsSnsIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateAwsSnsIntegrationAsync(global::Chirpstack.Api.CreateAwsSnsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateAwsSnsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetAwsSnsIntegrationResponse GetAwsSnsIntegration(global::Chirpstack.Api.GetAwsSnsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAwsSnsIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetAwsSnsIntegrationResponse GetAwsSnsIntegration(global::Chirpstack.Api.GetAwsSnsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetAwsSnsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetAwsSnsIntegrationResponse> GetAwsSnsIntegrationAsync(global::Chirpstack.Api.GetAwsSnsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAwsSnsIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetAwsSnsIntegrationResponse> GetAwsSnsIntegrationAsync(global::Chirpstack.Api.GetAwsSnsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetAwsSnsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateAwsSnsIntegration(global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAwsSnsIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateAwsSnsIntegration(global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateAwsSnsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAwsSnsIntegrationAsync(global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAwsSnsIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAwsSnsIntegrationAsync(global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateAwsSnsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteAwsSnsIntegration(global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAwsSnsIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteAwsSnsIntegration(global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteAwsSnsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAwsSnsIntegrationAsync(global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAwsSnsIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete AWS SNS integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAwsSnsIntegrationAsync(global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteAwsSnsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateAzureServiceBusIntegration(global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAzureServiceBusIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateAzureServiceBusIntegration(global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateAzureServiceBusIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateAzureServiceBusIntegrationAsync(global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAzureServiceBusIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateAzureServiceBusIntegrationAsync(global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateAzureServiceBusIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse GetAzureServiceBusIntegration(global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAzureServiceBusIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse GetAzureServiceBusIntegration(global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetAzureServiceBusIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse> GetAzureServiceBusIntegrationAsync(global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAzureServiceBusIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse> GetAzureServiceBusIntegrationAsync(global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetAzureServiceBusIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateAzureServiceBusIntegration(global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAzureServiceBusIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateAzureServiceBusIntegration(global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateAzureServiceBusIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAzureServiceBusIntegrationAsync(global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAzureServiceBusIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAzureServiceBusIntegrationAsync(global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateAzureServiceBusIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteAzureServiceBusIntegration(global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAzureServiceBusIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteAzureServiceBusIntegration(global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteAzureServiceBusIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAzureServiceBusIntegrationAsync(global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAzureServiceBusIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete Azure Service-Bus integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAzureServiceBusIntegrationAsync(global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteAzureServiceBusIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreatePilotThingsIntegration(global::Chirpstack.Api.CreatePilotThingsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreatePilotThingsIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreatePilotThingsIntegration(global::Chirpstack.Api.CreatePilotThingsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreatePilotThingsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreatePilotThingsIntegrationAsync(global::Chirpstack.Api.CreatePilotThingsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreatePilotThingsIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreatePilotThingsIntegrationAsync(global::Chirpstack.Api.CreatePilotThingsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreatePilotThingsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetPilotThingsIntegrationResponse GetPilotThingsIntegration(global::Chirpstack.Api.GetPilotThingsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetPilotThingsIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetPilotThingsIntegrationResponse GetPilotThingsIntegration(global::Chirpstack.Api.GetPilotThingsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetPilotThingsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetPilotThingsIntegrationResponse> GetPilotThingsIntegrationAsync(global::Chirpstack.Api.GetPilotThingsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetPilotThingsIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetPilotThingsIntegrationResponse> GetPilotThingsIntegrationAsync(global::Chirpstack.Api.GetPilotThingsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetPilotThingsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdatePilotThingsIntegration(global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdatePilotThingsIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdatePilotThingsIntegration(global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdatePilotThingsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdatePilotThingsIntegrationAsync(global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdatePilotThingsIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdatePilotThingsIntegrationAsync(global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdatePilotThingsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeletePilotThingsIntegration(global::Chirpstack.Api.DeletePilotThingsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeletePilotThingsIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeletePilotThingsIntegration(global::Chirpstack.Api.DeletePilotThingsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeletePilotThingsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeletePilotThingsIntegrationAsync(global::Chirpstack.Api.DeletePilotThingsIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeletePilotThingsIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete Pilot Things integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeletePilotThingsIntegrationAsync(global::Chirpstack.Api.DeletePilotThingsIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeletePilotThingsIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateIftttIntegration(global::Chirpstack.Api.CreateIftttIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateIftttIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateIftttIntegration(global::Chirpstack.Api.CreateIftttIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateIftttIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Create IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateIftttIntegrationAsync(global::Chirpstack.Api.CreateIftttIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateIftttIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateIftttIntegrationAsync(global::Chirpstack.Api.CreateIftttIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateIftttIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetIftttIntegrationResponse GetIftttIntegration(global::Chirpstack.Api.GetIftttIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetIftttIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetIftttIntegrationResponse GetIftttIntegration(global::Chirpstack.Api.GetIftttIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetIftttIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Get IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetIftttIntegrationResponse> GetIftttIntegrationAsync(global::Chirpstack.Api.GetIftttIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetIftttIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetIftttIntegrationResponse> GetIftttIntegrationAsync(global::Chirpstack.Api.GetIftttIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetIftttIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateIftttIntegration(global::Chirpstack.Api.UpdateIftttIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateIftttIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateIftttIntegration(global::Chirpstack.Api.UpdateIftttIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateIftttIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Update IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateIftttIntegrationAsync(global::Chirpstack.Api.UpdateIftttIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateIftttIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateIftttIntegrationAsync(global::Chirpstack.Api.UpdateIftttIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateIftttIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteIftttIntegration(global::Chirpstack.Api.DeleteIftttIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteIftttIntegration(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteIftttIntegration(global::Chirpstack.Api.DeleteIftttIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteIftttIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Delete IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteIftttIntegrationAsync(global::Chirpstack.Api.DeleteIftttIntegrationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteIftttIntegrationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete IFTTT integration.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteIftttIntegrationAsync(global::Chirpstack.Api.DeleteIftttIntegrationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteIftttIntegration, null, options, request);
+      }
+      /// <summary>
+      /// Generates application ID specific client-certificate.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse GenerateMqttIntegrationClientCertificate(global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GenerateMqttIntegrationClientCertificate(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Generates application ID specific client-certificate.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse GenerateMqttIntegrationClientCertificate(global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GenerateMqttIntegrationClientCertificate, null, options, request);
+      }
+      /// <summary>
+      /// Generates application ID specific client-certificate.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse> GenerateMqttIntegrationClientCertificateAsync(global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GenerateMqttIntegrationClientCertificateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Generates application ID specific client-certificate.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse> GenerateMqttIntegrationClientCertificateAsync(global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GenerateMqttIntegrationClientCertificate, null, options, request);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected override ApplicationServiceClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new ApplicationServiceClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static grpc::ServerServiceDefinition BindService(ApplicationServiceBase serviceImpl)
+    {
+      return grpc::ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_Create, serviceImpl.Create)
+          .AddMethod(__Method_Get, serviceImpl.Get)
+          .AddMethod(__Method_Update, serviceImpl.Update)
+          .AddMethod(__Method_Delete, serviceImpl.Delete)
+          .AddMethod(__Method_List, serviceImpl.List)
+          .AddMethod(__Method_ListIntegrations, serviceImpl.ListIntegrations)
+          .AddMethod(__Method_CreateHttpIntegration, serviceImpl.CreateHttpIntegration)
+          .AddMethod(__Method_GetHttpIntegration, serviceImpl.GetHttpIntegration)
+          .AddMethod(__Method_UpdateHttpIntegration, serviceImpl.UpdateHttpIntegration)
+          .AddMethod(__Method_DeleteHttpIntegration, serviceImpl.DeleteHttpIntegration)
+          .AddMethod(__Method_CreateInfluxDbIntegration, serviceImpl.CreateInfluxDbIntegration)
+          .AddMethod(__Method_GetInfluxDbIntegration, serviceImpl.GetInfluxDbIntegration)
+          .AddMethod(__Method_UpdateInfluxDbIntegration, serviceImpl.UpdateInfluxDbIntegration)
+          .AddMethod(__Method_DeleteInfluxDbIntegration, serviceImpl.DeleteInfluxDbIntegration)
+          .AddMethod(__Method_CreateThingsBoardIntegration, serviceImpl.CreateThingsBoardIntegration)
+          .AddMethod(__Method_GetThingsBoardIntegration, serviceImpl.GetThingsBoardIntegration)
+          .AddMethod(__Method_UpdateThingsBoardIntegration, serviceImpl.UpdateThingsBoardIntegration)
+          .AddMethod(__Method_DeleteThingsBoardIntegration, serviceImpl.DeleteThingsBoardIntegration)
+          .AddMethod(__Method_CreateMyDevicesIntegration, serviceImpl.CreateMyDevicesIntegration)
+          .AddMethod(__Method_GetMyDevicesIntegration, serviceImpl.GetMyDevicesIntegration)
+          .AddMethod(__Method_UpdateMyDevicesIntegration, serviceImpl.UpdateMyDevicesIntegration)
+          .AddMethod(__Method_DeleteMyDevicesIntegration, serviceImpl.DeleteMyDevicesIntegration)
+          .AddMethod(__Method_CreateLoraCloudIntegration, serviceImpl.CreateLoraCloudIntegration)
+          .AddMethod(__Method_GetLoraCloudIntegration, serviceImpl.GetLoraCloudIntegration)
+          .AddMethod(__Method_UpdateLoraCloudIntegration, serviceImpl.UpdateLoraCloudIntegration)
+          .AddMethod(__Method_DeleteLoraCloudIntegration, serviceImpl.DeleteLoraCloudIntegration)
+          .AddMethod(__Method_CreateGcpPubSubIntegration, serviceImpl.CreateGcpPubSubIntegration)
+          .AddMethod(__Method_GetGcpPubSubIntegration, serviceImpl.GetGcpPubSubIntegration)
+          .AddMethod(__Method_UpdateGcpPubSubIntegration, serviceImpl.UpdateGcpPubSubIntegration)
+          .AddMethod(__Method_DeleteGcpPubSubIntegration, serviceImpl.DeleteGcpPubSubIntegration)
+          .AddMethod(__Method_CreateAwsSnsIntegration, serviceImpl.CreateAwsSnsIntegration)
+          .AddMethod(__Method_GetAwsSnsIntegration, serviceImpl.GetAwsSnsIntegration)
+          .AddMethod(__Method_UpdateAwsSnsIntegration, serviceImpl.UpdateAwsSnsIntegration)
+          .AddMethod(__Method_DeleteAwsSnsIntegration, serviceImpl.DeleteAwsSnsIntegration)
+          .AddMethod(__Method_CreateAzureServiceBusIntegration, serviceImpl.CreateAzureServiceBusIntegration)
+          .AddMethod(__Method_GetAzureServiceBusIntegration, serviceImpl.GetAzureServiceBusIntegration)
+          .AddMethod(__Method_UpdateAzureServiceBusIntegration, serviceImpl.UpdateAzureServiceBusIntegration)
+          .AddMethod(__Method_DeleteAzureServiceBusIntegration, serviceImpl.DeleteAzureServiceBusIntegration)
+          .AddMethod(__Method_CreatePilotThingsIntegration, serviceImpl.CreatePilotThingsIntegration)
+          .AddMethod(__Method_GetPilotThingsIntegration, serviceImpl.GetPilotThingsIntegration)
+          .AddMethod(__Method_UpdatePilotThingsIntegration, serviceImpl.UpdatePilotThingsIntegration)
+          .AddMethod(__Method_DeletePilotThingsIntegration, serviceImpl.DeletePilotThingsIntegration)
+          .AddMethod(__Method_CreateIftttIntegration, serviceImpl.CreateIftttIntegration)
+          .AddMethod(__Method_GetIftttIntegration, serviceImpl.GetIftttIntegration)
+          .AddMethod(__Method_UpdateIftttIntegration, serviceImpl.UpdateIftttIntegration)
+          .AddMethod(__Method_DeleteIftttIntegration, serviceImpl.DeleteIftttIntegration)
+          .AddMethod(__Method_GenerateMqttIntegrationClientCertificate, serviceImpl.GenerateMqttIntegrationClientCertificate).Build();
+    }
+
+    /// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
+    /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+    /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static void BindService(grpc::ServiceBinderBase serviceBinder, ApplicationServiceBase serviceImpl)
+    {
+      serviceBinder.AddMethod(__Method_Create, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateApplicationRequest, global::Chirpstack.Api.CreateApplicationResponse>(serviceImpl.Create));
+      serviceBinder.AddMethod(__Method_Get, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetApplicationRequest, global::Chirpstack.Api.GetApplicationResponse>(serviceImpl.Get));
+      serviceBinder.AddMethod(__Method_Update, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateApplicationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Update));
+      serviceBinder.AddMethod(__Method_Delete, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteApplicationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Delete));
+      serviceBinder.AddMethod(__Method_List, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListApplicationsRequest, global::Chirpstack.Api.ListApplicationsResponse>(serviceImpl.List));
+      serviceBinder.AddMethod(__Method_ListIntegrations, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListIntegrationsRequest, global::Chirpstack.Api.ListIntegrationsResponse>(serviceImpl.ListIntegrations));
+      serviceBinder.AddMethod(__Method_CreateHttpIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateHttpIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreateHttpIntegration));
+      serviceBinder.AddMethod(__Method_GetHttpIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetHttpIntegrationRequest, global::Chirpstack.Api.GetHttpIntegrationResponse>(serviceImpl.GetHttpIntegration));
+      serviceBinder.AddMethod(__Method_UpdateHttpIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateHttpIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateHttpIntegration));
+      serviceBinder.AddMethod(__Method_DeleteHttpIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteHttpIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteHttpIntegration));
+      serviceBinder.AddMethod(__Method_CreateInfluxDbIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateInfluxDbIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreateInfluxDbIntegration));
+      serviceBinder.AddMethod(__Method_GetInfluxDbIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetInfluxDbIntegrationRequest, global::Chirpstack.Api.GetInfluxDbIntegrationResponse>(serviceImpl.GetInfluxDbIntegration));
+      serviceBinder.AddMethod(__Method_UpdateInfluxDbIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateInfluxDbIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateInfluxDbIntegration));
+      serviceBinder.AddMethod(__Method_DeleteInfluxDbIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteInfluxDbIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteInfluxDbIntegration));
+      serviceBinder.AddMethod(__Method_CreateThingsBoardIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateThingsBoardIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreateThingsBoardIntegration));
+      serviceBinder.AddMethod(__Method_GetThingsBoardIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetThingsBoardIntegrationRequest, global::Chirpstack.Api.GetThingsBoardIntegrationResponse>(serviceImpl.GetThingsBoardIntegration));
+      serviceBinder.AddMethod(__Method_UpdateThingsBoardIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateThingsBoardIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateThingsBoardIntegration));
+      serviceBinder.AddMethod(__Method_DeleteThingsBoardIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteThingsBoardIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteThingsBoardIntegration));
+      serviceBinder.AddMethod(__Method_CreateMyDevicesIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateMyDevicesIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreateMyDevicesIntegration));
+      serviceBinder.AddMethod(__Method_GetMyDevicesIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetMyDevicesIntegrationRequest, global::Chirpstack.Api.GetMyDevicesIntegrationResponse>(serviceImpl.GetMyDevicesIntegration));
+      serviceBinder.AddMethod(__Method_UpdateMyDevicesIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateMyDevicesIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateMyDevicesIntegration));
+      serviceBinder.AddMethod(__Method_DeleteMyDevicesIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteMyDevicesIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteMyDevicesIntegration));
+      serviceBinder.AddMethod(__Method_CreateLoraCloudIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateLoraCloudIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreateLoraCloudIntegration));
+      serviceBinder.AddMethod(__Method_GetLoraCloudIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetLoraCloudIntegrationRequest, global::Chirpstack.Api.GetLoraCloudIntegrationResponse>(serviceImpl.GetLoraCloudIntegration));
+      serviceBinder.AddMethod(__Method_UpdateLoraCloudIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateLoraCloudIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateLoraCloudIntegration));
+      serviceBinder.AddMethod(__Method_DeleteLoraCloudIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteLoraCloudIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteLoraCloudIntegration));
+      serviceBinder.AddMethod(__Method_CreateGcpPubSubIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateGcpPubSubIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreateGcpPubSubIntegration));
+      serviceBinder.AddMethod(__Method_GetGcpPubSubIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetGcpPubSubIntegrationRequest, global::Chirpstack.Api.GetGcpPubSubIntegrationResponse>(serviceImpl.GetGcpPubSubIntegration));
+      serviceBinder.AddMethod(__Method_UpdateGcpPubSubIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateGcpPubSubIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateGcpPubSubIntegration));
+      serviceBinder.AddMethod(__Method_DeleteGcpPubSubIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteGcpPubSubIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteGcpPubSubIntegration));
+      serviceBinder.AddMethod(__Method_CreateAwsSnsIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateAwsSnsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreateAwsSnsIntegration));
+      serviceBinder.AddMethod(__Method_GetAwsSnsIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetAwsSnsIntegrationRequest, global::Chirpstack.Api.GetAwsSnsIntegrationResponse>(serviceImpl.GetAwsSnsIntegration));
+      serviceBinder.AddMethod(__Method_UpdateAwsSnsIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateAwsSnsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateAwsSnsIntegration));
+      serviceBinder.AddMethod(__Method_DeleteAwsSnsIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteAwsSnsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteAwsSnsIntegration));
+      serviceBinder.AddMethod(__Method_CreateAzureServiceBusIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateAzureServiceBusIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreateAzureServiceBusIntegration));
+      serviceBinder.AddMethod(__Method_GetAzureServiceBusIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetAzureServiceBusIntegrationRequest, global::Chirpstack.Api.GetAzureServiceBusIntegrationResponse>(serviceImpl.GetAzureServiceBusIntegration));
+      serviceBinder.AddMethod(__Method_UpdateAzureServiceBusIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateAzureServiceBusIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateAzureServiceBusIntegration));
+      serviceBinder.AddMethod(__Method_DeleteAzureServiceBusIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteAzureServiceBusIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteAzureServiceBusIntegration));
+      serviceBinder.AddMethod(__Method_CreatePilotThingsIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreatePilotThingsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreatePilotThingsIntegration));
+      serviceBinder.AddMethod(__Method_GetPilotThingsIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetPilotThingsIntegrationRequest, global::Chirpstack.Api.GetPilotThingsIntegrationResponse>(serviceImpl.GetPilotThingsIntegration));
+      serviceBinder.AddMethod(__Method_UpdatePilotThingsIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdatePilotThingsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdatePilotThingsIntegration));
+      serviceBinder.AddMethod(__Method_DeletePilotThingsIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeletePilotThingsIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeletePilotThingsIntegration));
+      serviceBinder.AddMethod(__Method_CreateIftttIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateIftttIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreateIftttIntegration));
+      serviceBinder.AddMethod(__Method_GetIftttIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetIftttIntegrationRequest, global::Chirpstack.Api.GetIftttIntegrationResponse>(serviceImpl.GetIftttIntegration));
+      serviceBinder.AddMethod(__Method_UpdateIftttIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateIftttIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateIftttIntegration));
+      serviceBinder.AddMethod(__Method_DeleteIftttIntegration, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteIftttIntegrationRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteIftttIntegration));
+      serviceBinder.AddMethod(__Method_GenerateMqttIntegrationClientCertificate, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateRequest, global::Chirpstack.Api.GenerateMqttIntegrationClientCertificateResponse>(serviceImpl.GenerateMqttIntegrationClientCertificate));
+    }
+
+  }
+}
+#endregion
diff --git a/api/csharp/Chirpstack/api/Device.cs b/api/csharp/Chirpstack/api/Device.cs
new file mode 100644
index 00000000..d02f53e5
--- /dev/null
+++ b/api/csharp/Chirpstack/api/Device.cs
@@ -0,0 +1,9583 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/device.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/device.proto</summary>
+  public static partial class DeviceReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/device.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static DeviceReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChBhcGkvZGV2aWNlLnByb3RvEgNhcGkaE2NvbW1vbi9jb21tb24ucHJvdG8a",
+            "HGdvb2dsZS9hcGkvYW5ub3RhdGlvbnMucHJvdG8aH2dvb2dsZS9wcm90b2J1",
+            "Zi90aW1lc3RhbXAucHJvdG8aHGdvb2dsZS9wcm90b2J1Zi9zdHJ1Y3QucHJv",
+            "dG8aG2dvb2dsZS9wcm90b2J1Zi9lbXB0eS5wcm90byLQAgoGRGV2aWNlEg8K",
+            "B2Rldl9ldWkYASABKAkSDAoEbmFtZRgCIAEoCRITCgtkZXNjcmlwdGlvbhgD",
+            "IAEoCRIWCg5hcHBsaWNhdGlvbl9pZBgEIAEoCRIZChFkZXZpY2VfcHJvZmls",
+            "ZV9pZBgFIAEoCRIXCg9za2lwX2ZjbnRfY2hlY2sYBiABKAgSEwoLaXNfZGlz",
+            "YWJsZWQYByABKAgSLQoJdmFyaWFibGVzGAggAygLMhouYXBpLkRldmljZS5W",
+            "YXJpYWJsZXNFbnRyeRIjCgR0YWdzGAkgAygLMhUuYXBpLkRldmljZS5UYWdz",
+            "RW50cnkaMAoOVmFyaWFibGVzRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVl",
+            "GAIgASgJOgI4ARorCglUYWdzRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVl",
+            "GAIgASgJOgI4ASJUCgxEZXZpY2VTdGF0dXMSDgoGbWFyZ2luGAEgASgFEh0K",
+            "FWV4dGVybmFsX3Bvd2VyX3NvdXJjZRgCIAEoCBIVCg1iYXR0ZXJ5X2xldmVs",
+            "GAMgASgCIrgCCg5EZXZpY2VMaXN0SXRlbRIPCgdkZXZfZXVpGAEgASgJEi4K",
+            "CmNyZWF0ZWRfYXQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1w",
+            "Ei4KCnVwZGF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0",
+            "YW1wEjAKDGxhc3Rfc2Vlbl9hdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5U",
+            "aW1lc3RhbXASDAoEbmFtZRgFIAEoCRITCgtkZXNjcmlwdGlvbhgGIAEoCRIZ",
+            "ChFkZXZpY2VfcHJvZmlsZV9pZBgHIAEoCRIbChNkZXZpY2VfcHJvZmlsZV9u",
+            "YW1lGAggASgJEigKDWRldmljZV9zdGF0dXMYCSABKAsyES5hcGkuRGV2aWNl",
+            "U3RhdHVzIj8KCkRldmljZUtleXMSDwoHZGV2X2V1aRgBIAEoCRIPCgdud2tf",
+            "a2V5GAIgASgJEg8KB2FwcF9rZXkYAyABKAkiMgoTQ3JlYXRlRGV2aWNlUmVx",
+            "dWVzdBIbCgZkZXZpY2UYASABKAsyCy5hcGkuRGV2aWNlIiMKEEdldERldmlj",
+            "ZVJlcXVlc3QSDwoHZGV2X2V1aRgBIAEoCSLsAQoRR2V0RGV2aWNlUmVzcG9u",
+            "c2USGwoGZGV2aWNlGAEgASgLMgsuYXBpLkRldmljZRIuCgpjcmVhdGVkX2F0",
+            "GAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVk",
+            "X2F0GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIwCgxsYXN0",
+            "X3NlZW5fYXQYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEigK",
+            "DWRldmljZV9zdGF0dXMYBSABKAsyES5hcGkuRGV2aWNlU3RhdHVzIjIKE1Vw",
+            "ZGF0ZURldmljZVJlcXVlc3QSGwoGZGV2aWNlGAEgASgLMgsuYXBpLkRldmlj",
+            "ZSImChNEZWxldGVEZXZpY2VSZXF1ZXN0Eg8KB2Rldl9ldWkYASABKAkidwoS",
+            "TGlzdERldmljZXNSZXF1ZXN0Eg0KBWxpbWl0GAEgASgNEg4KBm9mZnNldBgC",
+            "IAEoDRIOCgZzZWFyY2gYAyABKAkSFgoOYXBwbGljYXRpb25faWQYBCABKAkS",
+            "GgoSbXVsdGljYXN0X2dyb3VwX2lkGAUgASgJIk8KE0xpc3REZXZpY2VzUmVz",
+            "cG9uc2USEwoLdG90YWxfY291bnQYASABKA0SIwoGcmVzdWx0GAIgAygLMhMu",
+            "YXBpLkRldmljZUxpc3RJdGVtIj8KF0NyZWF0ZURldmljZUtleXNSZXF1ZXN0",
+            "EiQKC2RldmljZV9rZXlzGAEgASgLMg8uYXBpLkRldmljZUtleXMiJwoUR2V0",
+            "RGV2aWNlS2V5c1JlcXVlc3QSDwoHZGV2X2V1aRgBIAEoCSKdAQoVR2V0RGV2",
+            "aWNlS2V5c1Jlc3BvbnNlEiQKC2RldmljZV9rZXlzGAEgASgLMg8uYXBpLkRl",
+            "dmljZUtleXMSLgoKY3JlYXRlZF9hdBgCIAEoCzIaLmdvb2dsZS5wcm90b2J1",
+            "Zi5UaW1lc3RhbXASLgoKdXBkYXRlZF9hdBgDIAEoCzIaLmdvb2dsZS5wcm90",
+            "b2J1Zi5UaW1lc3RhbXAiPwoXVXBkYXRlRGV2aWNlS2V5c1JlcXVlc3QSJAoL",
+            "ZGV2aWNlX2tleXMYASABKAsyDy5hcGkuRGV2aWNlS2V5cyIqChdEZWxldGVE",
+            "ZXZpY2VLZXlzUmVxdWVzdBIPCgdkZXZfZXVpGAEgASgJIs8BChBEZXZpY2VB",
+            "Y3RpdmF0aW9uEg8KB2Rldl9ldWkYASABKAkSEAoIZGV2X2FkZHIYAiABKAkS",
+            "EQoJYXBwX3Nfa2V5GAMgASgJEhUKDW53a19zX2VuY19rZXkYBCABKAkSFwoP",
+            "c19ud2tfc19pbnRfa2V5GAggASgJEhcKD2ZfbndrX3NfaW50X2tleRgJIAEo",
+            "CRIQCghmX2NudF91cBgFIAEoDRIUCgxuX2ZfY250X2Rvd24YBiABKA0SFAoM",
+            "YV9mX2NudF9kb3duGAogASgNIkkKFUFjdGl2YXRlRGV2aWNlUmVxdWVzdBIw",
+            "ChFkZXZpY2VfYWN0aXZhdGlvbhgBIAEoCzIVLmFwaS5EZXZpY2VBY3RpdmF0",
+            "aW9uIioKF0RlYWN0aXZhdGVEZXZpY2VSZXF1ZXN0Eg8KB2Rldl9ldWkYASAB",
+            "KAkiLQoaR2V0RGV2aWNlQWN0aXZhdGlvblJlcXVlc3QSDwoHZGV2X2V1aRgB",
+            "IAEoCSJPChtHZXREZXZpY2VBY3RpdmF0aW9uUmVzcG9uc2USMAoRZGV2aWNl",
+            "X2FjdGl2YXRpb24YASABKAsyFS5hcGkuRGV2aWNlQWN0aXZhdGlvbiIqChdH",
+            "ZXRSYW5kb21EZXZBZGRyUmVxdWVzdBIPCgdkZXZfZXVpGAEgASgJIiwKGEdl",
+            "dFJhbmRvbURldkFkZHJSZXNwb25zZRIQCghkZXZfYWRkchgBIAEoCSKoAQoX",
+            "R2V0RGV2aWNlTWV0cmljc1JlcXVlc3QSDwoHZGV2X2V1aRgBIAEoCRIpCgVz",
+            "dGFydBgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASJwoDZW5k",
+            "GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIoCgthZ2dyZWdh",
+            "dGlvbhgEIAEoDjITLmNvbW1vbi5BZ2dyZWdhdGlvbiKTAgoYR2V0RGV2aWNl",
+            "TWV0cmljc1Jlc3BvbnNlEjsKB21ldHJpY3MYASADKAsyKi5hcGkuR2V0RGV2",
+            "aWNlTWV0cmljc1Jlc3BvbnNlLk1ldHJpY3NFbnRyeRI5CgZzdGF0ZXMYAiAD",
+            "KAsyKS5hcGkuR2V0RGV2aWNlTWV0cmljc1Jlc3BvbnNlLlN0YXRlc0VudHJ5",
+            "Gj4KDE1ldHJpY3NFbnRyeRILCgNrZXkYASABKAkSHQoFdmFsdWUYAiABKAsy",
+            "Di5jb21tb24uTWV0cmljOgI4ARo/CgtTdGF0ZXNFbnRyeRILCgNrZXkYASAB",
+            "KAkSHwoFdmFsdWUYAiABKAsyEC5hcGkuRGV2aWNlU3RhdGU6AjgBIioKC0Rl",
+            "dmljZVN0YXRlEgwKBG5hbWUYAiABKAkSDQoFdmFsdWUYAyABKAkirAEKG0dl",
+            "dERldmljZUxpbmtNZXRyaWNzUmVxdWVzdBIPCgdkZXZfZXVpGAEgASgJEikK",
+            "BXN0YXJ0GAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBInCgNl",
+            "bmQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEigKC2FnZ3Jl",
+            "Z2F0aW9uGAQgASgOMhMuY29tbW9uLkFnZ3JlZ2F0aW9uIvsBChxHZXREZXZp",
+            "Y2VMaW5rTWV0cmljc1Jlc3BvbnNlEiIKCnJ4X3BhY2tldHMYASABKAsyDi5j",
+            "b21tb24uTWV0cmljEh8KB2d3X3Jzc2kYAiABKAsyDi5jb21tb24uTWV0cmlj",
+            "Eh4KBmd3X3NuchgDIAEoCzIOLmNvbW1vbi5NZXRyaWMSKwoTcnhfcGFja2V0",
+            "c19wZXJfZnJlcRgEIAEoCzIOLmNvbW1vbi5NZXRyaWMSKQoRcnhfcGFja2V0",
+            "c19wZXJfZHIYBSABKAsyDi5jb21tb24uTWV0cmljEh4KBmVycm9ycxgGIAEo",
+            "CzIOLmNvbW1vbi5NZXRyaWMisAEKD0RldmljZVF1ZXVlSXRlbRIKCgJpZBgB",
+            "IAEoCRIPCgdkZXZfZXVpGAIgASgJEhEKCWNvbmZpcm1lZBgDIAEoCBIOCgZm",
+            "X3BvcnQYBCABKA0SDAoEZGF0YRgFIAEoDBInCgZvYmplY3QYBiABKAsyFy5n",
+            "b29nbGUucHJvdG9idWYuU3RydWN0EhIKCmlzX3BlbmRpbmcYByABKAgSEgoK",
+            "Zl9jbnRfZG93bhgIIAEoDSJJCh1FbnF1ZXVlRGV2aWNlUXVldWVJdGVtUmVx",
+            "dWVzdBIoCgpxdWV1ZV9pdGVtGAEgASgLMhQuYXBpLkRldmljZVF1ZXVlSXRl",
+            "bSIsCh5FbnF1ZXVlRGV2aWNlUXVldWVJdGVtUmVzcG9uc2USCgoCaWQYASAB",
+            "KAkiKgoXRmx1c2hEZXZpY2VRdWV1ZVJlcXVlc3QSDwoHZGV2X2V1aRgBIAEo",
+            "CSJBChpHZXREZXZpY2VRdWV1ZUl0ZW1zUmVxdWVzdBIPCgdkZXZfZXVpGAEg",
+            "ASgJEhIKCmNvdW50X29ubHkYAiABKAgiWAobR2V0RGV2aWNlUXVldWVJdGVt",
+            "c1Jlc3BvbnNlEhMKC3RvdGFsX2NvdW50GAEgASgNEiQKBnJlc3VsdBgCIAMo",
+            "CzIULmFwaS5EZXZpY2VRdWV1ZUl0ZW0iKAoVRmx1c2hEZXZOb25jZXNSZXF1",
+            "ZXN0Eg8KB2Rldl9ldWkYASABKAky0BAKDURldmljZVNlcnZpY2USUwoGQ3Jl",
+            "YXRlEhguYXBpLkNyZWF0ZURldmljZVJlcXVlc3QaFi5nb29nbGUucHJvdG9i",
+            "dWYuRW1wdHkiF4LT5JMCESIML2FwaS9kZXZpY2VzOgEqElQKA0dldBIVLmFw",
+            "aS5HZXREZXZpY2VSZXF1ZXN0GhYuYXBpLkdldERldmljZVJlc3BvbnNlIh6C",
+            "0+STAhgSFi9hcGkvZGV2aWNlcy97ZGV2X2V1aX0SZAoGVXBkYXRlEhguYXBp",
+            "LlVwZGF0ZURldmljZVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHki",
+            "KILT5JMCIhodL2FwaS9kZXZpY2VzL3tkZXZpY2UuZGV2X2V1aX06ASoSWgoG",
+            "RGVsZXRlEhguYXBpLkRlbGV0ZURldmljZVJlcXVlc3QaFi5nb29nbGUucHJv",
+            "dG9idWYuRW1wdHkiHoLT5JMCGCoWL2FwaS9kZXZpY2VzL3tkZXZfZXVpfRJP",
+            "CgRMaXN0EhcuYXBpLkxpc3REZXZpY2VzUmVxdWVzdBoYLmFwaS5MaXN0RGV2",
+            "aWNlc1Jlc3BvbnNlIhSC0+STAg4SDC9hcGkvZGV2aWNlcxJ2CgpDcmVhdGVL",
+            "ZXlzEhwuYXBpLkNyZWF0ZURldmljZUtleXNSZXF1ZXN0GhYuZ29vZ2xlLnBy",
+            "b3RvYnVmLkVtcHR5IjKC0+STAiwiJy9hcGkvZGV2aWNlcy97ZGV2aWNlX2tl",
+            "eXMuZGV2X2V1aX0va2V5czoBKhJlCgdHZXRLZXlzEhkuYXBpLkdldERldmlj",
+            "ZUtleXNSZXF1ZXN0GhouYXBpLkdldERldmljZUtleXNSZXNwb25zZSIjgtPk",
+            "kwIdEhsvYXBpL2RldmljZXMve2Rldl9ldWl9L2tleXMSdgoKVXBkYXRlS2V5",
+            "cxIcLmFwaS5VcGRhdGVEZXZpY2VLZXlzUmVxdWVzdBoWLmdvb2dsZS5wcm90",
+            "b2J1Zi5FbXB0eSIygtPkkwIsGicvYXBpL2RldmljZXMve2RldmljZV9rZXlz",
+            "LmRldl9ldWl9L2tleXM6ASoSZwoKRGVsZXRlS2V5cxIcLmFwaS5EZWxldGVE",
+            "ZXZpY2VLZXlzUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIjgtPk",
+            "kwIdKhsvYXBpL2RldmljZXMve2Rldl9ldWl9L2tleXMSbwoORmx1c2hEZXZO",
+            "b25jZXMSGi5hcGkuRmx1c2hEZXZOb25jZXNSZXF1ZXN0GhYuZ29vZ2xlLnBy",
+            "b3RvYnVmLkVtcHR5IimC0+STAiMqIS9hcGkvZGV2aWNlcy97ZGV2X2V1aX0v",
+            "ZGV2LW5vbmNlcxJ8CghBY3RpdmF0ZRIaLmFwaS5BY3RpdmF0ZURldmljZVJl",
+            "cXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiPILT5JMCNiIxL2FwaS9k",
+            "ZXZpY2VzL3tkZXZpY2VfYWN0aXZhdGlvbi5kZXZfZXVpfS9hY3RpdmF0ZToB",
+            "KhJtCgpEZWFjdGl2YXRlEhwuYXBpLkRlYWN0aXZhdGVEZXZpY2VSZXF1ZXN0",
+            "GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IimC0+STAiMqIS9hcGkvZGV2aWNl",
+            "cy97ZGV2X2V1aX0vYWN0aXZhdGlvbhJ9Cg1HZXRBY3RpdmF0aW9uEh8uYXBp",
+            "LkdldERldmljZUFjdGl2YXRpb25SZXF1ZXN0GiAuYXBpLkdldERldmljZUFj",
+            "dGl2YXRpb25SZXNwb25zZSIpgtPkkwIjEiEvYXBpL2RldmljZXMve2Rldl9l",
+            "dWl9L2FjdGl2YXRpb24SgwEKEEdldFJhbmRvbURldkFkZHISHC5hcGkuR2V0",
+            "UmFuZG9tRGV2QWRkclJlcXVlc3QaHS5hcGkuR2V0UmFuZG9tRGV2QWRkclJl",
+            "c3BvbnNlIjKC0+STAiwiKi9hcGkvZGV2aWNlcy97ZGV2X2V1aX0vZ2V0LXJh",
+            "bmRvbS1kZXYtYWRkchJxCgpHZXRNZXRyaWNzEhwuYXBpLkdldERldmljZU1l",
+            "dHJpY3NSZXF1ZXN0Gh0uYXBpLkdldERldmljZU1ldHJpY3NSZXNwb25zZSIm",
+            "gtPkkwIgEh4vYXBpL2RldmljZXMve2Rldl9ldWl9L21ldHJpY3MSggEKDkdl",
+            "dExpbmtNZXRyaWNzEiAuYXBpLkdldERldmljZUxpbmtNZXRyaWNzUmVxdWVz",
+            "dBohLmFwaS5HZXREZXZpY2VMaW5rTWV0cmljc1Jlc3BvbnNlIiuC0+STAiUS",
+            "Iy9hcGkvZGV2aWNlcy97ZGV2X2V1aX0vbGluay1tZXRyaWNzEoYBCgdFbnF1",
+            "ZXVlEiIuYXBpLkVucXVldWVEZXZpY2VRdWV1ZUl0ZW1SZXF1ZXN0GiMuYXBp",
+            "LkVucXVldWVEZXZpY2VRdWV1ZUl0ZW1SZXNwb25zZSIygtPkkwIsIicvYXBp",
+            "L2RldmljZXMve3F1ZXVlX2l0ZW0uZGV2X2V1aX0vcXVldWU6ASoSaAoKRmx1",
+            "c2hRdWV1ZRIcLmFwaS5GbHVzaERldmljZVF1ZXVlUmVxdWVzdBoWLmdvb2ds",
+            "ZS5wcm90b2J1Zi5FbXB0eSIkgtPkkwIeKhwvYXBpL2RldmljZXMve2Rldl9l",
+            "dWl9L3F1ZXVlEnMKCEdldFF1ZXVlEh8uYXBpLkdldERldmljZVF1ZXVlSXRl",
+            "bXNSZXF1ZXN0GiAuYXBpLkdldERldmljZVF1ZXVlSXRlbXNSZXNwb25zZSIk",
+            "gtPkkwIeEhwvYXBpL2RldmljZXMve2Rldl9ldWl9L3F1ZXVlQmMKEWlvLmNo",
+            "aXJwc3RhY2suYXBpQgtEZXZpY2VQcm90b1ABWi5naXRodWIuY29tL2NoaXJw",
+            "c3RhY2svY2hpcnBzdGFjay9hcGkvZ28vdjQvYXBpqgIOQ2hpcnBzdGFjay5B",
+            "cGliBnByb3RvMw=="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Chirpstack.Common.CommonReflection.Descriptor, global::Google.Api.AnnotationsReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.Device), global::Chirpstack.Api.Device.Parser, new[]{ "DevEui", "Name", "Description", "ApplicationId", "DeviceProfileId", "SkipFcntCheck", "IsDisabled", "Variables", "Tags" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceStatus), global::Chirpstack.Api.DeviceStatus.Parser, new[]{ "Margin", "ExternalPowerSource", "BatteryLevel" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceListItem), global::Chirpstack.Api.DeviceListItem.Parser, new[]{ "DevEui", "CreatedAt", "UpdatedAt", "LastSeenAt", "Name", "Description", "DeviceProfileId", "DeviceProfileName", "DeviceStatus" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceKeys), global::Chirpstack.Api.DeviceKeys.Parser, new[]{ "DevEui", "NwkKey", "AppKey" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateDeviceRequest), global::Chirpstack.Api.CreateDeviceRequest.Parser, new[]{ "Device" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceRequest), global::Chirpstack.Api.GetDeviceRequest.Parser, new[]{ "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceResponse), global::Chirpstack.Api.GetDeviceResponse.Parser, new[]{ "Device", "CreatedAt", "UpdatedAt", "LastSeenAt", "DeviceStatus" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateDeviceRequest), global::Chirpstack.Api.UpdateDeviceRequest.Parser, new[]{ "Device" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteDeviceRequest), global::Chirpstack.Api.DeleteDeviceRequest.Parser, new[]{ "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListDevicesRequest), global::Chirpstack.Api.ListDevicesRequest.Parser, new[]{ "Limit", "Offset", "Search", "ApplicationId", "MulticastGroupId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListDevicesResponse), global::Chirpstack.Api.ListDevicesResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateDeviceKeysRequest), global::Chirpstack.Api.CreateDeviceKeysRequest.Parser, new[]{ "DeviceKeys" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceKeysRequest), global::Chirpstack.Api.GetDeviceKeysRequest.Parser, new[]{ "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceKeysResponse), global::Chirpstack.Api.GetDeviceKeysResponse.Parser, new[]{ "DeviceKeys", "CreatedAt", "UpdatedAt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateDeviceKeysRequest), global::Chirpstack.Api.UpdateDeviceKeysRequest.Parser, new[]{ "DeviceKeys" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteDeviceKeysRequest), global::Chirpstack.Api.DeleteDeviceKeysRequest.Parser, new[]{ "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceActivation), global::Chirpstack.Api.DeviceActivation.Parser, new[]{ "DevEui", "DevAddr", "AppSKey", "NwkSEncKey", "SNwkSIntKey", "FNwkSIntKey", "FCntUp", "NFCntDown", "AFCntDown" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ActivateDeviceRequest), global::Chirpstack.Api.ActivateDeviceRequest.Parser, new[]{ "DeviceActivation" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeactivateDeviceRequest), global::Chirpstack.Api.DeactivateDeviceRequest.Parser, new[]{ "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceActivationRequest), global::Chirpstack.Api.GetDeviceActivationRequest.Parser, new[]{ "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceActivationResponse), global::Chirpstack.Api.GetDeviceActivationResponse.Parser, new[]{ "DeviceActivation" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetRandomDevAddrRequest), global::Chirpstack.Api.GetRandomDevAddrRequest.Parser, new[]{ "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetRandomDevAddrResponse), global::Chirpstack.Api.GetRandomDevAddrResponse.Parser, new[]{ "DevAddr" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceMetricsRequest), global::Chirpstack.Api.GetDeviceMetricsRequest.Parser, new[]{ "DevEui", "Start", "End", "Aggregation" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceMetricsResponse), global::Chirpstack.Api.GetDeviceMetricsResponse.Parser, new[]{ "Metrics", "States" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceState), global::Chirpstack.Api.DeviceState.Parser, new[]{ "Name", "Value" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceLinkMetricsRequest), global::Chirpstack.Api.GetDeviceLinkMetricsRequest.Parser, new[]{ "DevEui", "Start", "End", "Aggregation" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceLinkMetricsResponse), global::Chirpstack.Api.GetDeviceLinkMetricsResponse.Parser, new[]{ "RxPackets", "GwRssi", "GwSnr", "RxPacketsPerFreq", "RxPacketsPerDr", "Errors" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceQueueItem), global::Chirpstack.Api.DeviceQueueItem.Parser, new[]{ "Id", "DevEui", "Confirmed", "FPort", "Data", "Object", "IsPending", "FCntDown" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.EnqueueDeviceQueueItemRequest), global::Chirpstack.Api.EnqueueDeviceQueueItemRequest.Parser, new[]{ "QueueItem" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.EnqueueDeviceQueueItemResponse), global::Chirpstack.Api.EnqueueDeviceQueueItemResponse.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.FlushDeviceQueueRequest), global::Chirpstack.Api.FlushDeviceQueueRequest.Parser, new[]{ "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceQueueItemsRequest), global::Chirpstack.Api.GetDeviceQueueItemsRequest.Parser, new[]{ "DevEui", "CountOnly" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceQueueItemsResponse), global::Chirpstack.Api.GetDeviceQueueItemsResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.FlushDevNoncesRequest), global::Chirpstack.Api.FlushDevNoncesRequest.Parser, new[]{ "DevEui" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  public sealed partial class Device : pb::IMessage<Device>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<Device> _parser = new pb::MessageParser<Device>(() => new Device());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<Device> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Device() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Device(Device other) : this() {
+      devEui_ = other.devEui_;
+      name_ = other.name_;
+      description_ = other.description_;
+      applicationId_ = other.applicationId_;
+      deviceProfileId_ = other.deviceProfileId_;
+      skipFcntCheck_ = other.skipFcntCheck_;
+      isDisabled_ = other.isDisabled_;
+      variables_ = other.variables_.Clone();
+      tags_ = other.tags_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Device Clone() {
+      return new Device(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 2;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 3;
+    private string description_ = "";
+    /// <summary>
+    /// Description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 4;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "device_profile_id" field.</summary>
+    public const int DeviceProfileIdFieldNumber = 5;
+    private string deviceProfileId_ = "";
+    /// <summary>
+    /// Device-profile ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeviceProfileId {
+      get { return deviceProfileId_; }
+      set {
+        deviceProfileId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "skip_fcnt_check" field.</summary>
+    public const int SkipFcntCheckFieldNumber = 6;
+    private bool skipFcntCheck_;
+    /// <summary>
+    /// Skip frame-counter checks (this is insecure, but could be helpful for debugging).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SkipFcntCheck {
+      get { return skipFcntCheck_; }
+      set {
+        skipFcntCheck_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_disabled" field.</summary>
+    public const int IsDisabledFieldNumber = 7;
+    private bool isDisabled_;
+    /// <summary>
+    /// Device is disabled.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsDisabled {
+      get { return isDisabled_; }
+      set {
+        isDisabled_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "variables" field.</summary>
+    public const int VariablesFieldNumber = 8;
+    private static readonly pbc::MapField<string, string>.Codec _map_variables_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 66);
+    private readonly pbc::MapField<string, string> variables_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Variables (user defined).
+    /// These variables can be used together with integrations to store tokens /
+    /// secrets that must be configured per device. These variables are not
+    /// exposed in the event payloads.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Variables {
+      get { return variables_; }
+    }
+
+    /// <summary>Field number for the "tags" field.</summary>
+    public const int TagsFieldNumber = 9;
+    private static readonly pbc::MapField<string, string>.Codec _map_tags_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 74);
+    private readonly pbc::MapField<string, string> tags_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Tags (user defined).
+    /// These tags are exposed in the event payloads or to integration. Tags are
+    /// intended for aggregation and filtering.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Tags {
+      get { return tags_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as Device);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(Device other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      if (Name != other.Name) return false;
+      if (Description != other.Description) return false;
+      if (ApplicationId != other.ApplicationId) return false;
+      if (DeviceProfileId != other.DeviceProfileId) return false;
+      if (SkipFcntCheck != other.SkipFcntCheck) return false;
+      if (IsDisabled != other.IsDisabled) return false;
+      if (!Variables.Equals(other.Variables)) return false;
+      if (!Tags.Equals(other.Tags)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (DeviceProfileId.Length != 0) hash ^= DeviceProfileId.GetHashCode();
+      if (SkipFcntCheck != false) hash ^= SkipFcntCheck.GetHashCode();
+      if (IsDisabled != false) hash ^= IsDisabled.GetHashCode();
+      hash ^= Variables.GetHashCode();
+      hash ^= Tags.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ApplicationId);
+      }
+      if (DeviceProfileId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(DeviceProfileId);
+      }
+      if (SkipFcntCheck != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(SkipFcntCheck);
+      }
+      if (IsDisabled != false) {
+        output.WriteRawTag(56);
+        output.WriteBool(IsDisabled);
+      }
+      variables_.WriteTo(output, _map_variables_codec);
+      tags_.WriteTo(output, _map_tags_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ApplicationId);
+      }
+      if (DeviceProfileId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(DeviceProfileId);
+      }
+      if (SkipFcntCheck != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(SkipFcntCheck);
+      }
+      if (IsDisabled != false) {
+        output.WriteRawTag(56);
+        output.WriteBool(IsDisabled);
+      }
+      variables_.WriteTo(ref output, _map_variables_codec);
+      tags_.WriteTo(ref output, _map_tags_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (DeviceProfileId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeviceProfileId);
+      }
+      if (SkipFcntCheck != false) {
+        size += 1 + 1;
+      }
+      if (IsDisabled != false) {
+        size += 1 + 1;
+      }
+      size += variables_.CalculateSize(_map_variables_codec);
+      size += tags_.CalculateSize(_map_tags_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(Device other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.DeviceProfileId.Length != 0) {
+        DeviceProfileId = other.DeviceProfileId;
+      }
+      if (other.SkipFcntCheck != false) {
+        SkipFcntCheck = other.SkipFcntCheck;
+      }
+      if (other.IsDisabled != false) {
+        IsDisabled = other.IsDisabled;
+      }
+      variables_.Add(other.variables_);
+      tags_.Add(other.tags_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 34: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 42: {
+            DeviceProfileId = input.ReadString();
+            break;
+          }
+          case 48: {
+            SkipFcntCheck = input.ReadBool();
+            break;
+          }
+          case 56: {
+            IsDisabled = input.ReadBool();
+            break;
+          }
+          case 66: {
+            variables_.AddEntriesFrom(input, _map_variables_codec);
+            break;
+          }
+          case 74: {
+            tags_.AddEntriesFrom(input, _map_tags_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 34: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 42: {
+            DeviceProfileId = input.ReadString();
+            break;
+          }
+          case 48: {
+            SkipFcntCheck = input.ReadBool();
+            break;
+          }
+          case 56: {
+            IsDisabled = input.ReadBool();
+            break;
+          }
+          case 66: {
+            variables_.AddEntriesFrom(ref input, _map_variables_codec);
+            break;
+          }
+          case 74: {
+            tags_.AddEntriesFrom(ref input, _map_tags_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeviceStatus : pb::IMessage<DeviceStatus>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceStatus> _parser = new pb::MessageParser<DeviceStatus>(() => new DeviceStatus());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceStatus> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceStatus() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceStatus(DeviceStatus other) : this() {
+      margin_ = other.margin_;
+      externalPowerSource_ = other.externalPowerSource_;
+      batteryLevel_ = other.batteryLevel_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceStatus Clone() {
+      return new DeviceStatus(this);
+    }
+
+    /// <summary>Field number for the "margin" field.</summary>
+    public const int MarginFieldNumber = 1;
+    private int margin_;
+    /// <summary>
+    /// The device margin status
+    /// -32..32: The demodulation SNR ration in dB
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int Margin {
+      get { return margin_; }
+      set {
+        margin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "external_power_source" field.</summary>
+    public const int ExternalPowerSourceFieldNumber = 2;
+    private bool externalPowerSource_;
+    /// <summary>
+    /// Device is connected to an external power source.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool ExternalPowerSource {
+      get { return externalPowerSource_; }
+      set {
+        externalPowerSource_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "battery_level" field.</summary>
+    public const int BatteryLevelFieldNumber = 3;
+    private float batteryLevel_;
+    /// <summary>
+    /// Device battery level as a percentage.
+    /// -1 when the battery level is not available.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public float BatteryLevel {
+      get { return batteryLevel_; }
+      set {
+        batteryLevel_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceStatus);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceStatus other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Margin != other.Margin) return false;
+      if (ExternalPowerSource != other.ExternalPowerSource) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(BatteryLevel, other.BatteryLevel)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Margin != 0) hash ^= Margin.GetHashCode();
+      if (ExternalPowerSource != false) hash ^= ExternalPowerSource.GetHashCode();
+      if (BatteryLevel != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(BatteryLevel);
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Margin != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Margin);
+      }
+      if (ExternalPowerSource != false) {
+        output.WriteRawTag(16);
+        output.WriteBool(ExternalPowerSource);
+      }
+      if (BatteryLevel != 0F) {
+        output.WriteRawTag(29);
+        output.WriteFloat(BatteryLevel);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Margin != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Margin);
+      }
+      if (ExternalPowerSource != false) {
+        output.WriteRawTag(16);
+        output.WriteBool(ExternalPowerSource);
+      }
+      if (BatteryLevel != 0F) {
+        output.WriteRawTag(29);
+        output.WriteFloat(BatteryLevel);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Margin != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Margin);
+      }
+      if (ExternalPowerSource != false) {
+        size += 1 + 1;
+      }
+      if (BatteryLevel != 0F) {
+        size += 1 + 4;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceStatus other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Margin != 0) {
+        Margin = other.Margin;
+      }
+      if (other.ExternalPowerSource != false) {
+        ExternalPowerSource = other.ExternalPowerSource;
+      }
+      if (other.BatteryLevel != 0F) {
+        BatteryLevel = other.BatteryLevel;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Margin = input.ReadInt32();
+            break;
+          }
+          case 16: {
+            ExternalPowerSource = input.ReadBool();
+            break;
+          }
+          case 29: {
+            BatteryLevel = input.ReadFloat();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Margin = input.ReadInt32();
+            break;
+          }
+          case 16: {
+            ExternalPowerSource = input.ReadBool();
+            break;
+          }
+          case 29: {
+            BatteryLevel = input.ReadFloat();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeviceListItem : pb::IMessage<DeviceListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceListItem> _parser = new pb::MessageParser<DeviceListItem>(() => new DeviceListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceListItem(DeviceListItem other) : this() {
+      devEui_ = other.devEui_;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      lastSeenAt_ = other.lastSeenAt_ != null ? other.lastSeenAt_.Clone() : null;
+      name_ = other.name_;
+      description_ = other.description_;
+      deviceProfileId_ = other.deviceProfileId_;
+      deviceProfileName_ = other.deviceProfileName_;
+      deviceStatus_ = other.deviceStatus_ != null ? other.deviceStatus_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceListItem Clone() {
+      return new DeviceListItem(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "last_seen_at" field.</summary>
+    public const int LastSeenAtFieldNumber = 4;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp lastSeenAt_;
+    /// <summary>
+    /// Last seen at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp LastSeenAt {
+      get { return lastSeenAt_; }
+      set {
+        lastSeenAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 5;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 6;
+    private string description_ = "";
+    /// <summary>
+    /// Description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "device_profile_id" field.</summary>
+    public const int DeviceProfileIdFieldNumber = 7;
+    private string deviceProfileId_ = "";
+    /// <summary>
+    /// Device-profile ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeviceProfileId {
+      get { return deviceProfileId_; }
+      set {
+        deviceProfileId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "device_profile_name" field.</summary>
+    public const int DeviceProfileNameFieldNumber = 8;
+    private string deviceProfileName_ = "";
+    /// <summary>
+    /// Device-profile name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeviceProfileName {
+      get { return deviceProfileName_; }
+      set {
+        deviceProfileName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "device_status" field.</summary>
+    public const int DeviceStatusFieldNumber = 9;
+    private global::Chirpstack.Api.DeviceStatus deviceStatus_;
+    /// <summary>
+    /// Device status.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceStatus DeviceStatus {
+      get { return deviceStatus_; }
+      set {
+        deviceStatus_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (!object.Equals(LastSeenAt, other.LastSeenAt)) return false;
+      if (Name != other.Name) return false;
+      if (Description != other.Description) return false;
+      if (DeviceProfileId != other.DeviceProfileId) return false;
+      if (DeviceProfileName != other.DeviceProfileName) return false;
+      if (!object.Equals(DeviceStatus, other.DeviceStatus)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (lastSeenAt_ != null) hash ^= LastSeenAt.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (DeviceProfileId.Length != 0) hash ^= DeviceProfileId.GetHashCode();
+      if (DeviceProfileName.Length != 0) hash ^= DeviceProfileName.GetHashCode();
+      if (deviceStatus_ != null) hash ^= DeviceStatus.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(LastSeenAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(Description);
+      }
+      if (DeviceProfileId.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(DeviceProfileId);
+      }
+      if (DeviceProfileName.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(DeviceProfileName);
+      }
+      if (deviceStatus_ != null) {
+        output.WriteRawTag(74);
+        output.WriteMessage(DeviceStatus);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(LastSeenAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(Description);
+      }
+      if (DeviceProfileId.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(DeviceProfileId);
+      }
+      if (DeviceProfileName.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(DeviceProfileName);
+      }
+      if (deviceStatus_ != null) {
+        output.WriteRawTag(74);
+        output.WriteMessage(DeviceStatus);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LastSeenAt);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (DeviceProfileId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeviceProfileId);
+      }
+      if (DeviceProfileName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeviceProfileName);
+      }
+      if (deviceStatus_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceStatus);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.lastSeenAt_ != null) {
+        if (lastSeenAt_ == null) {
+          LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        LastSeenAt.MergeFrom(other.LastSeenAt);
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      if (other.DeviceProfileId.Length != 0) {
+        DeviceProfileId = other.DeviceProfileId;
+      }
+      if (other.DeviceProfileName.Length != 0) {
+        DeviceProfileName = other.DeviceProfileName;
+      }
+      if (other.deviceStatus_ != null) {
+        if (deviceStatus_ == null) {
+          DeviceStatus = new global::Chirpstack.Api.DeviceStatus();
+        }
+        DeviceStatus.MergeFrom(other.DeviceStatus);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            if (lastSeenAt_ == null) {
+              LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(LastSeenAt);
+            break;
+          }
+          case 42: {
+            Name = input.ReadString();
+            break;
+          }
+          case 50: {
+            Description = input.ReadString();
+            break;
+          }
+          case 58: {
+            DeviceProfileId = input.ReadString();
+            break;
+          }
+          case 66: {
+            DeviceProfileName = input.ReadString();
+            break;
+          }
+          case 74: {
+            if (deviceStatus_ == null) {
+              DeviceStatus = new global::Chirpstack.Api.DeviceStatus();
+            }
+            input.ReadMessage(DeviceStatus);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            if (lastSeenAt_ == null) {
+              LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(LastSeenAt);
+            break;
+          }
+          case 42: {
+            Name = input.ReadString();
+            break;
+          }
+          case 50: {
+            Description = input.ReadString();
+            break;
+          }
+          case 58: {
+            DeviceProfileId = input.ReadString();
+            break;
+          }
+          case 66: {
+            DeviceProfileName = input.ReadString();
+            break;
+          }
+          case 74: {
+            if (deviceStatus_ == null) {
+              DeviceStatus = new global::Chirpstack.Api.DeviceStatus();
+            }
+            input.ReadMessage(DeviceStatus);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeviceKeys : pb::IMessage<DeviceKeys>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceKeys> _parser = new pb::MessageParser<DeviceKeys>(() => new DeviceKeys());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceKeys> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceKeys() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceKeys(DeviceKeys other) : this() {
+      devEui_ = other.devEui_;
+      nwkKey_ = other.nwkKey_;
+      appKey_ = other.appKey_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceKeys Clone() {
+      return new DeviceKeys(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "nwk_key" field.</summary>
+    public const int NwkKeyFieldNumber = 2;
+    private string nwkKey_ = "";
+    /// <summary>
+    /// Network root key (128 bit).
+    /// Note: For LoRaWAN 1.0.x, use this field for the LoRaWAN 1.0.x 'AppKey`!
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string NwkKey {
+      get { return nwkKey_; }
+      set {
+        nwkKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "app_key" field.</summary>
+    public const int AppKeyFieldNumber = 3;
+    private string appKey_ = "";
+    /// <summary>
+    /// Application root key (128 bit).
+    /// Note: This field only needs to be set for LoRaWAN 1.1.x devices!
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string AppKey {
+      get { return appKey_; }
+      set {
+        appKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceKeys);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceKeys other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      if (NwkKey != other.NwkKey) return false;
+      if (AppKey != other.AppKey) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (NwkKey.Length != 0) hash ^= NwkKey.GetHashCode();
+      if (AppKey.Length != 0) hash ^= AppKey.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (NwkKey.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(NwkKey);
+      }
+      if (AppKey.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(AppKey);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (NwkKey.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(NwkKey);
+      }
+      if (AppKey.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(AppKey);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (NwkKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(NwkKey);
+      }
+      if (AppKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(AppKey);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceKeys other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.NwkKey.Length != 0) {
+        NwkKey = other.NwkKey;
+      }
+      if (other.AppKey.Length != 0) {
+        AppKey = other.AppKey;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            NwkKey = input.ReadString();
+            break;
+          }
+          case 26: {
+            AppKey = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            NwkKey = input.ReadString();
+            break;
+          }
+          case 26: {
+            AppKey = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateDeviceRequest : pb::IMessage<CreateDeviceRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateDeviceRequest> _parser = new pb::MessageParser<CreateDeviceRequest>(() => new CreateDeviceRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateDeviceRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceRequest(CreateDeviceRequest other) : this() {
+      device_ = other.device_ != null ? other.device_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceRequest Clone() {
+      return new CreateDeviceRequest(this);
+    }
+
+    /// <summary>Field number for the "device" field.</summary>
+    public const int DeviceFieldNumber = 1;
+    private global::Chirpstack.Api.Device device_;
+    /// <summary>
+    /// Device object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Device Device {
+      get { return device_; }
+      set {
+        device_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateDeviceRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateDeviceRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Device, other.Device)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (device_ != null) hash ^= Device.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (device_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Device);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (device_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Device);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (device_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Device);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateDeviceRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.device_ != null) {
+        if (device_ == null) {
+          Device = new global::Chirpstack.Api.Device();
+        }
+        Device.MergeFrom(other.Device);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (device_ == null) {
+              Device = new global::Chirpstack.Api.Device();
+            }
+            input.ReadMessage(Device);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (device_ == null) {
+              Device = new global::Chirpstack.Api.Device();
+            }
+            input.ReadMessage(Device);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceRequest : pb::IMessage<GetDeviceRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceRequest> _parser = new pb::MessageParser<GetDeviceRequest>(() => new GetDeviceRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceRequest(GetDeviceRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceRequest Clone() {
+      return new GetDeviceRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceResponse : pb::IMessage<GetDeviceResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceResponse> _parser = new pb::MessageParser<GetDeviceResponse>(() => new GetDeviceResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceResponse(GetDeviceResponse other) : this() {
+      device_ = other.device_ != null ? other.device_.Clone() : null;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      lastSeenAt_ = other.lastSeenAt_ != null ? other.lastSeenAt_.Clone() : null;
+      deviceStatus_ = other.deviceStatus_ != null ? other.deviceStatus_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceResponse Clone() {
+      return new GetDeviceResponse(this);
+    }
+
+    /// <summary>Field number for the "device" field.</summary>
+    public const int DeviceFieldNumber = 1;
+    private global::Chirpstack.Api.Device device_;
+    /// <summary>
+    /// Device object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Device Device {
+      get { return device_; }
+      set {
+        device_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "last_seen_at" field.</summary>
+    public const int LastSeenAtFieldNumber = 4;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp lastSeenAt_;
+    /// <summary>
+    /// Last seen at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp LastSeenAt {
+      get { return lastSeenAt_; }
+      set {
+        lastSeenAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_status" field.</summary>
+    public const int DeviceStatusFieldNumber = 5;
+    private global::Chirpstack.Api.DeviceStatus deviceStatus_;
+    /// <summary>
+    /// Device status.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceStatus DeviceStatus {
+      get { return deviceStatus_; }
+      set {
+        deviceStatus_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Device, other.Device)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (!object.Equals(LastSeenAt, other.LastSeenAt)) return false;
+      if (!object.Equals(DeviceStatus, other.DeviceStatus)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (device_ != null) hash ^= Device.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (lastSeenAt_ != null) hash ^= LastSeenAt.GetHashCode();
+      if (deviceStatus_ != null) hash ^= DeviceStatus.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (device_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Device);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(LastSeenAt);
+      }
+      if (deviceStatus_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(DeviceStatus);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (device_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Device);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(LastSeenAt);
+      }
+      if (deviceStatus_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(DeviceStatus);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (device_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Device);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LastSeenAt);
+      }
+      if (deviceStatus_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceStatus);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.device_ != null) {
+        if (device_ == null) {
+          Device = new global::Chirpstack.Api.Device();
+        }
+        Device.MergeFrom(other.Device);
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.lastSeenAt_ != null) {
+        if (lastSeenAt_ == null) {
+          LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        LastSeenAt.MergeFrom(other.LastSeenAt);
+      }
+      if (other.deviceStatus_ != null) {
+        if (deviceStatus_ == null) {
+          DeviceStatus = new global::Chirpstack.Api.DeviceStatus();
+        }
+        DeviceStatus.MergeFrom(other.DeviceStatus);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (device_ == null) {
+              Device = new global::Chirpstack.Api.Device();
+            }
+            input.ReadMessage(Device);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            if (lastSeenAt_ == null) {
+              LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(LastSeenAt);
+            break;
+          }
+          case 42: {
+            if (deviceStatus_ == null) {
+              DeviceStatus = new global::Chirpstack.Api.DeviceStatus();
+            }
+            input.ReadMessage(DeviceStatus);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (device_ == null) {
+              Device = new global::Chirpstack.Api.Device();
+            }
+            input.ReadMessage(Device);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            if (lastSeenAt_ == null) {
+              LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(LastSeenAt);
+            break;
+          }
+          case 42: {
+            if (deviceStatus_ == null) {
+              DeviceStatus = new global::Chirpstack.Api.DeviceStatus();
+            }
+            input.ReadMessage(DeviceStatus);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateDeviceRequest : pb::IMessage<UpdateDeviceRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateDeviceRequest> _parser = new pb::MessageParser<UpdateDeviceRequest>(() => new UpdateDeviceRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateDeviceRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceRequest(UpdateDeviceRequest other) : this() {
+      device_ = other.device_ != null ? other.device_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceRequest Clone() {
+      return new UpdateDeviceRequest(this);
+    }
+
+    /// <summary>Field number for the "device" field.</summary>
+    public const int DeviceFieldNumber = 1;
+    private global::Chirpstack.Api.Device device_;
+    /// <summary>
+    /// Device object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Device Device {
+      get { return device_; }
+      set {
+        device_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateDeviceRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateDeviceRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Device, other.Device)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (device_ != null) hash ^= Device.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (device_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Device);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (device_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Device);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (device_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Device);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateDeviceRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.device_ != null) {
+        if (device_ == null) {
+          Device = new global::Chirpstack.Api.Device();
+        }
+        Device.MergeFrom(other.Device);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (device_ == null) {
+              Device = new global::Chirpstack.Api.Device();
+            }
+            input.ReadMessage(Device);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (device_ == null) {
+              Device = new global::Chirpstack.Api.Device();
+            }
+            input.ReadMessage(Device);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteDeviceRequest : pb::IMessage<DeleteDeviceRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteDeviceRequest> _parser = new pb::MessageParser<DeleteDeviceRequest>(() => new DeleteDeviceRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteDeviceRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceRequest(DeleteDeviceRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceRequest Clone() {
+      return new DeleteDeviceRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteDeviceRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteDeviceRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteDeviceRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListDevicesRequest : pb::IMessage<ListDevicesRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListDevicesRequest> _parser = new pb::MessageParser<ListDevicesRequest>(() => new ListDevicesRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListDevicesRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDevicesRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDevicesRequest(ListDevicesRequest other) : this() {
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      search_ = other.search_;
+      applicationId_ = other.applicationId_;
+      multicastGroupId_ = other.multicastGroupId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDevicesRequest Clone() {
+      return new ListDevicesRequest(this);
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 1;
+    private uint limit_;
+    /// <summary>
+    /// Max number of devices to return in the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 2;
+    private uint offset_;
+    /// <summary>
+    /// Offset in the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "search" field.</summary>
+    public const int SearchFieldNumber = 3;
+    private string search_ = "";
+    /// <summary>
+    /// If set, the given string will be used to search on name (optional).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Search {
+      get { return search_; }
+      set {
+        search_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 4;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID) to filter devices on.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "multicast_group_id" field.</summary>
+    public const int MulticastGroupIdFieldNumber = 5;
+    private string multicastGroupId_ = "";
+    /// <summary>
+    /// Multicst-group ID (UUID) to filter devices on.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string MulticastGroupId {
+      get { return multicastGroupId_; }
+      set {
+        multicastGroupId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListDevicesRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListDevicesRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      if (Search != other.Search) return false;
+      if (ApplicationId != other.ApplicationId) return false;
+      if (MulticastGroupId != other.MulticastGroupId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Limit != 0) hash ^= Limit.GetHashCode();
+      if (Offset != 0) hash ^= Offset.GetHashCode();
+      if (Search.Length != 0) hash ^= Search.GetHashCode();
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (MulticastGroupId.Length != 0) hash ^= MulticastGroupId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ApplicationId);
+      }
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ApplicationId);
+      }
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Limit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Limit);
+      }
+      if (Offset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Offset);
+      }
+      if (Search.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Search);
+      }
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (MulticastGroupId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListDevicesRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Limit != 0) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0) {
+        Offset = other.Offset;
+      }
+      if (other.Search.Length != 0) {
+        Search = other.Search;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.MulticastGroupId.Length != 0) {
+        MulticastGroupId = other.MulticastGroupId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 42: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 42: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListDevicesResponse : pb::IMessage<ListDevicesResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListDevicesResponse> _parser = new pb::MessageParser<ListDevicesResponse>(() => new ListDevicesResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListDevicesResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[10]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDevicesResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDevicesResponse(ListDevicesResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDevicesResponse Clone() {
+      return new ListDevicesResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of devices.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.DeviceListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.DeviceListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.DeviceListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.DeviceListItem>();
+    /// <summary>
+    /// Result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.DeviceListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListDevicesResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListDevicesResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListDevicesResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateDeviceKeysRequest : pb::IMessage<CreateDeviceKeysRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateDeviceKeysRequest> _parser = new pb::MessageParser<CreateDeviceKeysRequest>(() => new CreateDeviceKeysRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateDeviceKeysRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[11]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceKeysRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceKeysRequest(CreateDeviceKeysRequest other) : this() {
+      deviceKeys_ = other.deviceKeys_ != null ? other.deviceKeys_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceKeysRequest Clone() {
+      return new CreateDeviceKeysRequest(this);
+    }
+
+    /// <summary>Field number for the "device_keys" field.</summary>
+    public const int DeviceKeysFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceKeys deviceKeys_;
+    /// <summary>
+    /// Device-keys object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceKeys DeviceKeys {
+      get { return deviceKeys_; }
+      set {
+        deviceKeys_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateDeviceKeysRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateDeviceKeysRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceKeys, other.DeviceKeys)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceKeys_ != null) hash ^= DeviceKeys.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceKeys_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceKeys);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceKeys_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceKeys);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceKeys_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceKeys);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateDeviceKeysRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceKeys_ != null) {
+        if (deviceKeys_ == null) {
+          DeviceKeys = new global::Chirpstack.Api.DeviceKeys();
+        }
+        DeviceKeys.MergeFrom(other.DeviceKeys);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceKeys_ == null) {
+              DeviceKeys = new global::Chirpstack.Api.DeviceKeys();
+            }
+            input.ReadMessage(DeviceKeys);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceKeys_ == null) {
+              DeviceKeys = new global::Chirpstack.Api.DeviceKeys();
+            }
+            input.ReadMessage(DeviceKeys);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceKeysRequest : pb::IMessage<GetDeviceKeysRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceKeysRequest> _parser = new pb::MessageParser<GetDeviceKeysRequest>(() => new GetDeviceKeysRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceKeysRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[12]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceKeysRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceKeysRequest(GetDeviceKeysRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceKeysRequest Clone() {
+      return new GetDeviceKeysRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceKeysRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceKeysRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceKeysRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceKeysResponse : pb::IMessage<GetDeviceKeysResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceKeysResponse> _parser = new pb::MessageParser<GetDeviceKeysResponse>(() => new GetDeviceKeysResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceKeysResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[13]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceKeysResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceKeysResponse(GetDeviceKeysResponse other) : this() {
+      deviceKeys_ = other.deviceKeys_ != null ? other.deviceKeys_.Clone() : null;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceKeysResponse Clone() {
+      return new GetDeviceKeysResponse(this);
+    }
+
+    /// <summary>Field number for the "device_keys" field.</summary>
+    public const int DeviceKeysFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceKeys deviceKeys_;
+    /// <summary>
+    /// Device-keys object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceKeys DeviceKeys {
+      get { return deviceKeys_; }
+      set {
+        deviceKeys_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceKeysResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceKeysResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceKeys, other.DeviceKeys)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceKeys_ != null) hash ^= DeviceKeys.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceKeys_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceKeys);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceKeys_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceKeys);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceKeys_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceKeys);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceKeysResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceKeys_ != null) {
+        if (deviceKeys_ == null) {
+          DeviceKeys = new global::Chirpstack.Api.DeviceKeys();
+        }
+        DeviceKeys.MergeFrom(other.DeviceKeys);
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceKeys_ == null) {
+              DeviceKeys = new global::Chirpstack.Api.DeviceKeys();
+            }
+            input.ReadMessage(DeviceKeys);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceKeys_ == null) {
+              DeviceKeys = new global::Chirpstack.Api.DeviceKeys();
+            }
+            input.ReadMessage(DeviceKeys);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateDeviceKeysRequest : pb::IMessage<UpdateDeviceKeysRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateDeviceKeysRequest> _parser = new pb::MessageParser<UpdateDeviceKeysRequest>(() => new UpdateDeviceKeysRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateDeviceKeysRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[14]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceKeysRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceKeysRequest(UpdateDeviceKeysRequest other) : this() {
+      deviceKeys_ = other.deviceKeys_ != null ? other.deviceKeys_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceKeysRequest Clone() {
+      return new UpdateDeviceKeysRequest(this);
+    }
+
+    /// <summary>Field number for the "device_keys" field.</summary>
+    public const int DeviceKeysFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceKeys deviceKeys_;
+    /// <summary>
+    /// Device-keys object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceKeys DeviceKeys {
+      get { return deviceKeys_; }
+      set {
+        deviceKeys_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateDeviceKeysRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateDeviceKeysRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceKeys, other.DeviceKeys)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceKeys_ != null) hash ^= DeviceKeys.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceKeys_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceKeys);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceKeys_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceKeys);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceKeys_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceKeys);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateDeviceKeysRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceKeys_ != null) {
+        if (deviceKeys_ == null) {
+          DeviceKeys = new global::Chirpstack.Api.DeviceKeys();
+        }
+        DeviceKeys.MergeFrom(other.DeviceKeys);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceKeys_ == null) {
+              DeviceKeys = new global::Chirpstack.Api.DeviceKeys();
+            }
+            input.ReadMessage(DeviceKeys);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceKeys_ == null) {
+              DeviceKeys = new global::Chirpstack.Api.DeviceKeys();
+            }
+            input.ReadMessage(DeviceKeys);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteDeviceKeysRequest : pb::IMessage<DeleteDeviceKeysRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteDeviceKeysRequest> _parser = new pb::MessageParser<DeleteDeviceKeysRequest>(() => new DeleteDeviceKeysRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteDeviceKeysRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[15]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceKeysRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceKeysRequest(DeleteDeviceKeysRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceKeysRequest Clone() {
+      return new DeleteDeviceKeysRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteDeviceKeysRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteDeviceKeysRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteDeviceKeysRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeviceActivation : pb::IMessage<DeviceActivation>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceActivation> _parser = new pb::MessageParser<DeviceActivation>(() => new DeviceActivation());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceActivation> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[16]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceActivation() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceActivation(DeviceActivation other) : this() {
+      devEui_ = other.devEui_;
+      devAddr_ = other.devAddr_;
+      appSKey_ = other.appSKey_;
+      nwkSEncKey_ = other.nwkSEncKey_;
+      sNwkSIntKey_ = other.sNwkSIntKey_;
+      fNwkSIntKey_ = other.fNwkSIntKey_;
+      fCntUp_ = other.fCntUp_;
+      nFCntDown_ = other.nFCntDown_;
+      aFCntDown_ = other.aFCntDown_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceActivation Clone() {
+      return new DeviceActivation(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "dev_addr" field.</summary>
+    public const int DevAddrFieldNumber = 2;
+    private string devAddr_ = "";
+    /// <summary>
+    /// Device address (HEX encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevAddr {
+      get { return devAddr_; }
+      set {
+        devAddr_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "app_s_key" field.</summary>
+    public const int AppSKeyFieldNumber = 3;
+    private string appSKey_ = "";
+    /// <summary>
+    /// Application session key (HEX encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string AppSKey {
+      get { return appSKey_; }
+      set {
+        appSKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "nwk_s_enc_key" field.</summary>
+    public const int NwkSEncKeyFieldNumber = 4;
+    private string nwkSEncKey_ = "";
+    /// <summary>
+    /// Network session encryption key (HEX encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string NwkSEncKey {
+      get { return nwkSEncKey_; }
+      set {
+        nwkSEncKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "s_nwk_s_int_key" field.</summary>
+    public const int SNwkSIntKeyFieldNumber = 8;
+    private string sNwkSIntKey_ = "";
+    /// <summary>
+    /// Serving network session integrity key (HEX encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string SNwkSIntKey {
+      get { return sNwkSIntKey_; }
+      set {
+        sNwkSIntKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "f_nwk_s_int_key" field.</summary>
+    public const int FNwkSIntKeyFieldNumber = 9;
+    private string fNwkSIntKey_ = "";
+    /// <summary>
+    /// Forwarding network session integrity key (HEX encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string FNwkSIntKey {
+      get { return fNwkSIntKey_; }
+      set {
+        fNwkSIntKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "f_cnt_up" field.</summary>
+    public const int FCntUpFieldNumber = 5;
+    private uint fCntUp_;
+    /// <summary>
+    /// Uplink frame-counter.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FCntUp {
+      get { return fCntUp_; }
+      set {
+        fCntUp_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "n_f_cnt_down" field.</summary>
+    public const int NFCntDownFieldNumber = 6;
+    private uint nFCntDown_;
+    /// <summary>
+    /// Downlink network frame-counter.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint NFCntDown {
+      get { return nFCntDown_; }
+      set {
+        nFCntDown_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "a_f_cnt_down" field.</summary>
+    public const int AFCntDownFieldNumber = 10;
+    private uint aFCntDown_;
+    /// <summary>
+    /// Downlink application frame-counter.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint AFCntDown {
+      get { return aFCntDown_; }
+      set {
+        aFCntDown_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceActivation);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceActivation other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      if (DevAddr != other.DevAddr) return false;
+      if (AppSKey != other.AppSKey) return false;
+      if (NwkSEncKey != other.NwkSEncKey) return false;
+      if (SNwkSIntKey != other.SNwkSIntKey) return false;
+      if (FNwkSIntKey != other.FNwkSIntKey) return false;
+      if (FCntUp != other.FCntUp) return false;
+      if (NFCntDown != other.NFCntDown) return false;
+      if (AFCntDown != other.AFCntDown) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (DevAddr.Length != 0) hash ^= DevAddr.GetHashCode();
+      if (AppSKey.Length != 0) hash ^= AppSKey.GetHashCode();
+      if (NwkSEncKey.Length != 0) hash ^= NwkSEncKey.GetHashCode();
+      if (SNwkSIntKey.Length != 0) hash ^= SNwkSIntKey.GetHashCode();
+      if (FNwkSIntKey.Length != 0) hash ^= FNwkSIntKey.GetHashCode();
+      if (FCntUp != 0) hash ^= FCntUp.GetHashCode();
+      if (NFCntDown != 0) hash ^= NFCntDown.GetHashCode();
+      if (AFCntDown != 0) hash ^= AFCntDown.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(DevAddr);
+      }
+      if (AppSKey.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(AppSKey);
+      }
+      if (NwkSEncKey.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(NwkSEncKey);
+      }
+      if (FCntUp != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(FCntUp);
+      }
+      if (NFCntDown != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(NFCntDown);
+      }
+      if (SNwkSIntKey.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(SNwkSIntKey);
+      }
+      if (FNwkSIntKey.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(FNwkSIntKey);
+      }
+      if (AFCntDown != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(AFCntDown);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(DevAddr);
+      }
+      if (AppSKey.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(AppSKey);
+      }
+      if (NwkSEncKey.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(NwkSEncKey);
+      }
+      if (FCntUp != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(FCntUp);
+      }
+      if (NFCntDown != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(NFCntDown);
+      }
+      if (SNwkSIntKey.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(SNwkSIntKey);
+      }
+      if (FNwkSIntKey.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(FNwkSIntKey);
+      }
+      if (AFCntDown != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(AFCntDown);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (DevAddr.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevAddr);
+      }
+      if (AppSKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(AppSKey);
+      }
+      if (NwkSEncKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(NwkSEncKey);
+      }
+      if (SNwkSIntKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(SNwkSIntKey);
+      }
+      if (FNwkSIntKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(FNwkSIntKey);
+      }
+      if (FCntUp != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FCntUp);
+      }
+      if (NFCntDown != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(NFCntDown);
+      }
+      if (AFCntDown != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(AFCntDown);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceActivation other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.DevAddr.Length != 0) {
+        DevAddr = other.DevAddr;
+      }
+      if (other.AppSKey.Length != 0) {
+        AppSKey = other.AppSKey;
+      }
+      if (other.NwkSEncKey.Length != 0) {
+        NwkSEncKey = other.NwkSEncKey;
+      }
+      if (other.SNwkSIntKey.Length != 0) {
+        SNwkSIntKey = other.SNwkSIntKey;
+      }
+      if (other.FNwkSIntKey.Length != 0) {
+        FNwkSIntKey = other.FNwkSIntKey;
+      }
+      if (other.FCntUp != 0) {
+        FCntUp = other.FCntUp;
+      }
+      if (other.NFCntDown != 0) {
+        NFCntDown = other.NFCntDown;
+      }
+      if (other.AFCntDown != 0) {
+        AFCntDown = other.AFCntDown;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            DevAddr = input.ReadString();
+            break;
+          }
+          case 26: {
+            AppSKey = input.ReadString();
+            break;
+          }
+          case 34: {
+            NwkSEncKey = input.ReadString();
+            break;
+          }
+          case 40: {
+            FCntUp = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            NFCntDown = input.ReadUInt32();
+            break;
+          }
+          case 66: {
+            SNwkSIntKey = input.ReadString();
+            break;
+          }
+          case 74: {
+            FNwkSIntKey = input.ReadString();
+            break;
+          }
+          case 80: {
+            AFCntDown = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            DevAddr = input.ReadString();
+            break;
+          }
+          case 26: {
+            AppSKey = input.ReadString();
+            break;
+          }
+          case 34: {
+            NwkSEncKey = input.ReadString();
+            break;
+          }
+          case 40: {
+            FCntUp = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            NFCntDown = input.ReadUInt32();
+            break;
+          }
+          case 66: {
+            SNwkSIntKey = input.ReadString();
+            break;
+          }
+          case 74: {
+            FNwkSIntKey = input.ReadString();
+            break;
+          }
+          case 80: {
+            AFCntDown = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ActivateDeviceRequest : pb::IMessage<ActivateDeviceRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ActivateDeviceRequest> _parser = new pb::MessageParser<ActivateDeviceRequest>(() => new ActivateDeviceRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ActivateDeviceRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[17]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ActivateDeviceRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ActivateDeviceRequest(ActivateDeviceRequest other) : this() {
+      deviceActivation_ = other.deviceActivation_ != null ? other.deviceActivation_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ActivateDeviceRequest Clone() {
+      return new ActivateDeviceRequest(this);
+    }
+
+    /// <summary>Field number for the "device_activation" field.</summary>
+    public const int DeviceActivationFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceActivation deviceActivation_;
+    /// <summary>
+    /// Device activation object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceActivation DeviceActivation {
+      get { return deviceActivation_; }
+      set {
+        deviceActivation_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ActivateDeviceRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ActivateDeviceRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceActivation, other.DeviceActivation)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceActivation_ != null) hash ^= DeviceActivation.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceActivation_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceActivation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceActivation_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceActivation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceActivation_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceActivation);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ActivateDeviceRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceActivation_ != null) {
+        if (deviceActivation_ == null) {
+          DeviceActivation = new global::Chirpstack.Api.DeviceActivation();
+        }
+        DeviceActivation.MergeFrom(other.DeviceActivation);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceActivation_ == null) {
+              DeviceActivation = new global::Chirpstack.Api.DeviceActivation();
+            }
+            input.ReadMessage(DeviceActivation);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceActivation_ == null) {
+              DeviceActivation = new global::Chirpstack.Api.DeviceActivation();
+            }
+            input.ReadMessage(DeviceActivation);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeactivateDeviceRequest : pb::IMessage<DeactivateDeviceRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeactivateDeviceRequest> _parser = new pb::MessageParser<DeactivateDeviceRequest>(() => new DeactivateDeviceRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeactivateDeviceRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[18]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeactivateDeviceRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeactivateDeviceRequest(DeactivateDeviceRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeactivateDeviceRequest Clone() {
+      return new DeactivateDeviceRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeactivateDeviceRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeactivateDeviceRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeactivateDeviceRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceActivationRequest : pb::IMessage<GetDeviceActivationRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceActivationRequest> _parser = new pb::MessageParser<GetDeviceActivationRequest>(() => new GetDeviceActivationRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceActivationRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[19]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceActivationRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceActivationRequest(GetDeviceActivationRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceActivationRequest Clone() {
+      return new GetDeviceActivationRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceActivationRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceActivationRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceActivationRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceActivationResponse : pb::IMessage<GetDeviceActivationResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceActivationResponse> _parser = new pb::MessageParser<GetDeviceActivationResponse>(() => new GetDeviceActivationResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceActivationResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[20]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceActivationResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceActivationResponse(GetDeviceActivationResponse other) : this() {
+      deviceActivation_ = other.deviceActivation_ != null ? other.deviceActivation_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceActivationResponse Clone() {
+      return new GetDeviceActivationResponse(this);
+    }
+
+    /// <summary>Field number for the "device_activation" field.</summary>
+    public const int DeviceActivationFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceActivation deviceActivation_;
+    /// <summary>
+    /// Device activation object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceActivation DeviceActivation {
+      get { return deviceActivation_; }
+      set {
+        deviceActivation_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceActivationResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceActivationResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceActivation, other.DeviceActivation)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceActivation_ != null) hash ^= DeviceActivation.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceActivation_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceActivation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceActivation_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceActivation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceActivation_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceActivation);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceActivationResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceActivation_ != null) {
+        if (deviceActivation_ == null) {
+          DeviceActivation = new global::Chirpstack.Api.DeviceActivation();
+        }
+        DeviceActivation.MergeFrom(other.DeviceActivation);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceActivation_ == null) {
+              DeviceActivation = new global::Chirpstack.Api.DeviceActivation();
+            }
+            input.ReadMessage(DeviceActivation);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceActivation_ == null) {
+              DeviceActivation = new global::Chirpstack.Api.DeviceActivation();
+            }
+            input.ReadMessage(DeviceActivation);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetRandomDevAddrRequest : pb::IMessage<GetRandomDevAddrRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetRandomDevAddrRequest> _parser = new pb::MessageParser<GetRandomDevAddrRequest>(() => new GetRandomDevAddrRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetRandomDevAddrRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[21]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRandomDevAddrRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRandomDevAddrRequest(GetRandomDevAddrRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRandomDevAddrRequest Clone() {
+      return new GetRandomDevAddrRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetRandomDevAddrRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetRandomDevAddrRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetRandomDevAddrRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetRandomDevAddrResponse : pb::IMessage<GetRandomDevAddrResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetRandomDevAddrResponse> _parser = new pb::MessageParser<GetRandomDevAddrResponse>(() => new GetRandomDevAddrResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetRandomDevAddrResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[22]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRandomDevAddrResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRandomDevAddrResponse(GetRandomDevAddrResponse other) : this() {
+      devAddr_ = other.devAddr_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRandomDevAddrResponse Clone() {
+      return new GetRandomDevAddrResponse(this);
+    }
+
+    /// <summary>Field number for the "dev_addr" field.</summary>
+    public const int DevAddrFieldNumber = 1;
+    private string devAddr_ = "";
+    /// <summary>
+    /// DevAddr.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevAddr {
+      get { return devAddr_; }
+      set {
+        devAddr_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetRandomDevAddrResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetRandomDevAddrResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevAddr != other.DevAddr) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevAddr.Length != 0) hash ^= DevAddr.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevAddr);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevAddr);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevAddr.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevAddr);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetRandomDevAddrResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevAddr.Length != 0) {
+        DevAddr = other.DevAddr;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevAddr = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevAddr = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceMetricsRequest : pb::IMessage<GetDeviceMetricsRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceMetricsRequest> _parser = new pb::MessageParser<GetDeviceMetricsRequest>(() => new GetDeviceMetricsRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceMetricsRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[23]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceMetricsRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceMetricsRequest(GetDeviceMetricsRequest other) : this() {
+      devEui_ = other.devEui_;
+      start_ = other.start_ != null ? other.start_.Clone() : null;
+      end_ = other.end_ != null ? other.end_.Clone() : null;
+      aggregation_ = other.aggregation_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceMetricsRequest Clone() {
+      return new GetDeviceMetricsRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "start" field.</summary>
+    public const int StartFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp start_;
+    /// <summary>
+    /// Interval start timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Start {
+      get { return start_; }
+      set {
+        start_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "end" field.</summary>
+    public const int EndFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp end_;
+    /// <summary>
+    /// Interval end timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp End {
+      get { return end_; }
+      set {
+        end_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "aggregation" field.</summary>
+    public const int AggregationFieldNumber = 4;
+    private global::Chirpstack.Common.Aggregation aggregation_ = global::Chirpstack.Common.Aggregation.Hour;
+    /// <summary>
+    /// Aggregation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Aggregation Aggregation {
+      get { return aggregation_; }
+      set {
+        aggregation_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceMetricsRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceMetricsRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      if (!object.Equals(Start, other.Start)) return false;
+      if (!object.Equals(End, other.End)) return false;
+      if (Aggregation != other.Aggregation) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (start_ != null) hash ^= Start.GetHashCode();
+      if (end_ != null) hash ^= End.GetHashCode();
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) hash ^= Aggregation.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (start_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Start);
+      }
+      if (end_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(End);
+      }
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Aggregation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (start_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Start);
+      }
+      if (end_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(End);
+      }
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Aggregation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (start_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Start);
+      }
+      if (end_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(End);
+      }
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Aggregation);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceMetricsRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.start_ != null) {
+        if (start_ == null) {
+          Start = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Start.MergeFrom(other.Start);
+      }
+      if (other.end_ != null) {
+        if (end_ == null) {
+          End = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        End.MergeFrom(other.End);
+      }
+      if (other.Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        Aggregation = other.Aggregation;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (start_ == null) {
+              Start = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Start);
+            break;
+          }
+          case 26: {
+            if (end_ == null) {
+              End = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(End);
+            break;
+          }
+          case 32: {
+            Aggregation = (global::Chirpstack.Common.Aggregation) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (start_ == null) {
+              Start = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Start);
+            break;
+          }
+          case 26: {
+            if (end_ == null) {
+              End = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(End);
+            break;
+          }
+          case 32: {
+            Aggregation = (global::Chirpstack.Common.Aggregation) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceMetricsResponse : pb::IMessage<GetDeviceMetricsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceMetricsResponse> _parser = new pb::MessageParser<GetDeviceMetricsResponse>(() => new GetDeviceMetricsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceMetricsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[24]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceMetricsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceMetricsResponse(GetDeviceMetricsResponse other) : this() {
+      metrics_ = other.metrics_.Clone();
+      states_ = other.states_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceMetricsResponse Clone() {
+      return new GetDeviceMetricsResponse(this);
+    }
+
+    /// <summary>Field number for the "metrics" field.</summary>
+    public const int MetricsFieldNumber = 1;
+    private static readonly pbc::MapField<string, global::Chirpstack.Common.Metric>.Codec _map_metrics_codec
+        = new pbc::MapField<string, global::Chirpstack.Common.Metric>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForMessage(18, global::Chirpstack.Common.Metric.Parser), 10);
+    private readonly pbc::MapField<string, global::Chirpstack.Common.Metric> metrics_ = new pbc::MapField<string, global::Chirpstack.Common.Metric>();
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, global::Chirpstack.Common.Metric> Metrics {
+      get { return metrics_; }
+    }
+
+    /// <summary>Field number for the "states" field.</summary>
+    public const int StatesFieldNumber = 2;
+    private static readonly pbc::MapField<string, global::Chirpstack.Api.DeviceState>.Codec _map_states_codec
+        = new pbc::MapField<string, global::Chirpstack.Api.DeviceState>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.DeviceState.Parser), 18);
+    private readonly pbc::MapField<string, global::Chirpstack.Api.DeviceState> states_ = new pbc::MapField<string, global::Chirpstack.Api.DeviceState>();
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, global::Chirpstack.Api.DeviceState> States {
+      get { return states_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceMetricsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceMetricsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!Metrics.Equals(other.Metrics)) return false;
+      if (!States.Equals(other.States)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= Metrics.GetHashCode();
+      hash ^= States.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      metrics_.WriteTo(output, _map_metrics_codec);
+      states_.WriteTo(output, _map_states_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      metrics_.WriteTo(ref output, _map_metrics_codec);
+      states_.WriteTo(ref output, _map_states_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      size += metrics_.CalculateSize(_map_metrics_codec);
+      size += states_.CalculateSize(_map_states_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceMetricsResponse other) {
+      if (other == null) {
+        return;
+      }
+      metrics_.Add(other.metrics_);
+      states_.Add(other.states_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            metrics_.AddEntriesFrom(input, _map_metrics_codec);
+            break;
+          }
+          case 18: {
+            states_.AddEntriesFrom(input, _map_states_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            metrics_.AddEntriesFrom(ref input, _map_metrics_codec);
+            break;
+          }
+          case 18: {
+            states_.AddEntriesFrom(ref input, _map_states_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeviceState : pb::IMessage<DeviceState>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceState> _parser = new pb::MessageParser<DeviceState>(() => new DeviceState());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceState> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[25]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceState() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceState(DeviceState other) : this() {
+      name_ = other.name_;
+      value_ = other.value_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceState Clone() {
+      return new DeviceState(this);
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 2;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "value" field.</summary>
+    public const int ValueFieldNumber = 3;
+    private string value_ = "";
+    /// <summary>
+    /// Value.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Value {
+      get { return value_; }
+      set {
+        value_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceState);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceState other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Name != other.Name) return false;
+      if (Value != other.Value) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Value.Length != 0) hash ^= Value.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Value.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Value.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Value);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Value.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Value);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceState other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Value.Length != 0) {
+        Value = other.Value;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Value = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Value = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceLinkMetricsRequest : pb::IMessage<GetDeviceLinkMetricsRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceLinkMetricsRequest> _parser = new pb::MessageParser<GetDeviceLinkMetricsRequest>(() => new GetDeviceLinkMetricsRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceLinkMetricsRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[26]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceLinkMetricsRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceLinkMetricsRequest(GetDeviceLinkMetricsRequest other) : this() {
+      devEui_ = other.devEui_;
+      start_ = other.start_ != null ? other.start_.Clone() : null;
+      end_ = other.end_ != null ? other.end_.Clone() : null;
+      aggregation_ = other.aggregation_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceLinkMetricsRequest Clone() {
+      return new GetDeviceLinkMetricsRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// DevEUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "start" field.</summary>
+    public const int StartFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp start_;
+    /// <summary>
+    /// Interval start timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Start {
+      get { return start_; }
+      set {
+        start_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "end" field.</summary>
+    public const int EndFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp end_;
+    /// <summary>
+    /// Interval end timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp End {
+      get { return end_; }
+      set {
+        end_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "aggregation" field.</summary>
+    public const int AggregationFieldNumber = 4;
+    private global::Chirpstack.Common.Aggregation aggregation_ = global::Chirpstack.Common.Aggregation.Hour;
+    /// <summary>
+    /// Aggregation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Aggregation Aggregation {
+      get { return aggregation_; }
+      set {
+        aggregation_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceLinkMetricsRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceLinkMetricsRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      if (!object.Equals(Start, other.Start)) return false;
+      if (!object.Equals(End, other.End)) return false;
+      if (Aggregation != other.Aggregation) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (start_ != null) hash ^= Start.GetHashCode();
+      if (end_ != null) hash ^= End.GetHashCode();
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) hash ^= Aggregation.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (start_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Start);
+      }
+      if (end_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(End);
+      }
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Aggregation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (start_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Start);
+      }
+      if (end_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(End);
+      }
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Aggregation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (start_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Start);
+      }
+      if (end_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(End);
+      }
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Aggregation);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceLinkMetricsRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.start_ != null) {
+        if (start_ == null) {
+          Start = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Start.MergeFrom(other.Start);
+      }
+      if (other.end_ != null) {
+        if (end_ == null) {
+          End = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        End.MergeFrom(other.End);
+      }
+      if (other.Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        Aggregation = other.Aggregation;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (start_ == null) {
+              Start = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Start);
+            break;
+          }
+          case 26: {
+            if (end_ == null) {
+              End = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(End);
+            break;
+          }
+          case 32: {
+            Aggregation = (global::Chirpstack.Common.Aggregation) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (start_ == null) {
+              Start = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Start);
+            break;
+          }
+          case 26: {
+            if (end_ == null) {
+              End = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(End);
+            break;
+          }
+          case 32: {
+            Aggregation = (global::Chirpstack.Common.Aggregation) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceLinkMetricsResponse : pb::IMessage<GetDeviceLinkMetricsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceLinkMetricsResponse> _parser = new pb::MessageParser<GetDeviceLinkMetricsResponse>(() => new GetDeviceLinkMetricsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceLinkMetricsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[27]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceLinkMetricsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceLinkMetricsResponse(GetDeviceLinkMetricsResponse other) : this() {
+      rxPackets_ = other.rxPackets_ != null ? other.rxPackets_.Clone() : null;
+      gwRssi_ = other.gwRssi_ != null ? other.gwRssi_.Clone() : null;
+      gwSnr_ = other.gwSnr_ != null ? other.gwSnr_.Clone() : null;
+      rxPacketsPerFreq_ = other.rxPacketsPerFreq_ != null ? other.rxPacketsPerFreq_.Clone() : null;
+      rxPacketsPerDr_ = other.rxPacketsPerDr_ != null ? other.rxPacketsPerDr_.Clone() : null;
+      errors_ = other.errors_ != null ? other.errors_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceLinkMetricsResponse Clone() {
+      return new GetDeviceLinkMetricsResponse(this);
+    }
+
+    /// <summary>Field number for the "rx_packets" field.</summary>
+    public const int RxPacketsFieldNumber = 1;
+    private global::Chirpstack.Common.Metric rxPackets_;
+    /// <summary>
+    /// Packets received from the device.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric RxPackets {
+      get { return rxPackets_; }
+      set {
+        rxPackets_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "gw_rssi" field.</summary>
+    public const int GwRssiFieldNumber = 2;
+    private global::Chirpstack.Common.Metric gwRssi_;
+    /// <summary>
+    /// RSSI (as reported by the gateway(s)).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric GwRssi {
+      get { return gwRssi_; }
+      set {
+        gwRssi_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "gw_snr" field.</summary>
+    public const int GwSnrFieldNumber = 3;
+    private global::Chirpstack.Common.Metric gwSnr_;
+    /// <summary>
+    /// SNR (as reported by the gateway(s)).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric GwSnr {
+      get { return gwSnr_; }
+      set {
+        gwSnr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_packets_per_freq" field.</summary>
+    public const int RxPacketsPerFreqFieldNumber = 4;
+    private global::Chirpstack.Common.Metric rxPacketsPerFreq_;
+    /// <summary>
+    /// Packets received by frequency.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric RxPacketsPerFreq {
+      get { return rxPacketsPerFreq_; }
+      set {
+        rxPacketsPerFreq_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_packets_per_dr" field.</summary>
+    public const int RxPacketsPerDrFieldNumber = 5;
+    private global::Chirpstack.Common.Metric rxPacketsPerDr_;
+    /// <summary>
+    /// Packets received by DR.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric RxPacketsPerDr {
+      get { return rxPacketsPerDr_; }
+      set {
+        rxPacketsPerDr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "errors" field.</summary>
+    public const int ErrorsFieldNumber = 6;
+    private global::Chirpstack.Common.Metric errors_;
+    /// <summary>
+    /// Errors.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric Errors {
+      get { return errors_; }
+      set {
+        errors_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceLinkMetricsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceLinkMetricsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(RxPackets, other.RxPackets)) return false;
+      if (!object.Equals(GwRssi, other.GwRssi)) return false;
+      if (!object.Equals(GwSnr, other.GwSnr)) return false;
+      if (!object.Equals(RxPacketsPerFreq, other.RxPacketsPerFreq)) return false;
+      if (!object.Equals(RxPacketsPerDr, other.RxPacketsPerDr)) return false;
+      if (!object.Equals(Errors, other.Errors)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (rxPackets_ != null) hash ^= RxPackets.GetHashCode();
+      if (gwRssi_ != null) hash ^= GwRssi.GetHashCode();
+      if (gwSnr_ != null) hash ^= GwSnr.GetHashCode();
+      if (rxPacketsPerFreq_ != null) hash ^= RxPacketsPerFreq.GetHashCode();
+      if (rxPacketsPerDr_ != null) hash ^= RxPacketsPerDr.GetHashCode();
+      if (errors_ != null) hash ^= Errors.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (rxPackets_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(RxPackets);
+      }
+      if (gwRssi_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(GwRssi);
+      }
+      if (gwSnr_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(GwSnr);
+      }
+      if (rxPacketsPerFreq_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(RxPacketsPerFreq);
+      }
+      if (rxPacketsPerDr_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(RxPacketsPerDr);
+      }
+      if (errors_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Errors);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (rxPackets_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(RxPackets);
+      }
+      if (gwRssi_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(GwRssi);
+      }
+      if (gwSnr_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(GwSnr);
+      }
+      if (rxPacketsPerFreq_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(RxPacketsPerFreq);
+      }
+      if (rxPacketsPerDr_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(RxPacketsPerDr);
+      }
+      if (errors_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Errors);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (rxPackets_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(RxPackets);
+      }
+      if (gwRssi_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(GwRssi);
+      }
+      if (gwSnr_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(GwSnr);
+      }
+      if (rxPacketsPerFreq_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(RxPacketsPerFreq);
+      }
+      if (rxPacketsPerDr_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(RxPacketsPerDr);
+      }
+      if (errors_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Errors);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceLinkMetricsResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.rxPackets_ != null) {
+        if (rxPackets_ == null) {
+          RxPackets = new global::Chirpstack.Common.Metric();
+        }
+        RxPackets.MergeFrom(other.RxPackets);
+      }
+      if (other.gwRssi_ != null) {
+        if (gwRssi_ == null) {
+          GwRssi = new global::Chirpstack.Common.Metric();
+        }
+        GwRssi.MergeFrom(other.GwRssi);
+      }
+      if (other.gwSnr_ != null) {
+        if (gwSnr_ == null) {
+          GwSnr = new global::Chirpstack.Common.Metric();
+        }
+        GwSnr.MergeFrom(other.GwSnr);
+      }
+      if (other.rxPacketsPerFreq_ != null) {
+        if (rxPacketsPerFreq_ == null) {
+          RxPacketsPerFreq = new global::Chirpstack.Common.Metric();
+        }
+        RxPacketsPerFreq.MergeFrom(other.RxPacketsPerFreq);
+      }
+      if (other.rxPacketsPerDr_ != null) {
+        if (rxPacketsPerDr_ == null) {
+          RxPacketsPerDr = new global::Chirpstack.Common.Metric();
+        }
+        RxPacketsPerDr.MergeFrom(other.RxPacketsPerDr);
+      }
+      if (other.errors_ != null) {
+        if (errors_ == null) {
+          Errors = new global::Chirpstack.Common.Metric();
+        }
+        Errors.MergeFrom(other.Errors);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (rxPackets_ == null) {
+              RxPackets = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPackets);
+            break;
+          }
+          case 18: {
+            if (gwRssi_ == null) {
+              GwRssi = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(GwRssi);
+            break;
+          }
+          case 26: {
+            if (gwSnr_ == null) {
+              GwSnr = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(GwSnr);
+            break;
+          }
+          case 34: {
+            if (rxPacketsPerFreq_ == null) {
+              RxPacketsPerFreq = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPacketsPerFreq);
+            break;
+          }
+          case 42: {
+            if (rxPacketsPerDr_ == null) {
+              RxPacketsPerDr = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPacketsPerDr);
+            break;
+          }
+          case 50: {
+            if (errors_ == null) {
+              Errors = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(Errors);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (rxPackets_ == null) {
+              RxPackets = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPackets);
+            break;
+          }
+          case 18: {
+            if (gwRssi_ == null) {
+              GwRssi = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(GwRssi);
+            break;
+          }
+          case 26: {
+            if (gwSnr_ == null) {
+              GwSnr = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(GwSnr);
+            break;
+          }
+          case 34: {
+            if (rxPacketsPerFreq_ == null) {
+              RxPacketsPerFreq = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPacketsPerFreq);
+            break;
+          }
+          case 42: {
+            if (rxPacketsPerDr_ == null) {
+              RxPacketsPerDr = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPacketsPerDr);
+            break;
+          }
+          case 50: {
+            if (errors_ == null) {
+              Errors = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(Errors);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeviceQueueItem : pb::IMessage<DeviceQueueItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceQueueItem> _parser = new pb::MessageParser<DeviceQueueItem>(() => new DeviceQueueItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceQueueItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[28]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceQueueItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceQueueItem(DeviceQueueItem other) : this() {
+      id_ = other.id_;
+      devEui_ = other.devEui_;
+      confirmed_ = other.confirmed_;
+      fPort_ = other.fPort_;
+      data_ = other.data_;
+      object_ = other.object_ != null ? other.object_.Clone() : null;
+      isPending_ = other.isPending_;
+      fCntDown_ = other.fCntDown_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceQueueItem Clone() {
+      return new DeviceQueueItem(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID (UUID).
+    /// This is automatically generated on enqueue.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 2;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "confirmed" field.</summary>
+    public const int ConfirmedFieldNumber = 3;
+    private bool confirmed_;
+    /// <summary>
+    /// Confirmed.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Confirmed {
+      get { return confirmed_; }
+      set {
+        confirmed_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "f_port" field.</summary>
+    public const int FPortFieldNumber = 4;
+    private uint fPort_;
+    /// <summary>
+    /// FPort (must be > 0).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FPort {
+      get { return fPort_; }
+      set {
+        fPort_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "data" field.</summary>
+    public const int DataFieldNumber = 5;
+    private pb::ByteString data_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Data.
+    /// Or use the json_object field when a codec has been configured.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Data {
+      get { return data_; }
+      set {
+        data_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "object" field.</summary>
+    public const int ObjectFieldNumber = 6;
+    private global::Google.Protobuf.WellKnownTypes.Struct object_;
+    /// <summary>
+    /// Only use this when a codec has been configured that can encode this
+    /// object to bytes.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Struct Object {
+      get { return object_; }
+      set {
+        object_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_pending" field.</summary>
+    public const int IsPendingFieldNumber = 7;
+    private bool isPending_;
+    /// <summary>
+    /// Is pending.
+    /// This is set to true when the downlink is pending.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsPending {
+      get { return isPending_; }
+      set {
+        isPending_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "f_cnt_down" field.</summary>
+    public const int FCntDownFieldNumber = 8;
+    private uint fCntDown_;
+    /// <summary>
+    /// Downlink frame-counter.
+    /// This is set when the payload has been sent as downlink.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FCntDown {
+      get { return fCntDown_; }
+      set {
+        fCntDown_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceQueueItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceQueueItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (DevEui != other.DevEui) return false;
+      if (Confirmed != other.Confirmed) return false;
+      if (FPort != other.FPort) return false;
+      if (Data != other.Data) return false;
+      if (!object.Equals(Object, other.Object)) return false;
+      if (IsPending != other.IsPending) return false;
+      if (FCntDown != other.FCntDown) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (Confirmed != false) hash ^= Confirmed.GetHashCode();
+      if (FPort != 0) hash ^= FPort.GetHashCode();
+      if (Data.Length != 0) hash ^= Data.GetHashCode();
+      if (object_ != null) hash ^= Object.GetHashCode();
+      if (IsPending != false) hash ^= IsPending.GetHashCode();
+      if (FCntDown != 0) hash ^= FCntDown.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(DevEui);
+      }
+      if (Confirmed != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(Confirmed);
+      }
+      if (FPort != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(FPort);
+      }
+      if (Data.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteBytes(Data);
+      }
+      if (object_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Object);
+      }
+      if (IsPending != false) {
+        output.WriteRawTag(56);
+        output.WriteBool(IsPending);
+      }
+      if (FCntDown != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(FCntDown);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(DevEui);
+      }
+      if (Confirmed != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(Confirmed);
+      }
+      if (FPort != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(FPort);
+      }
+      if (Data.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteBytes(Data);
+      }
+      if (object_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Object);
+      }
+      if (IsPending != false) {
+        output.WriteRawTag(56);
+        output.WriteBool(IsPending);
+      }
+      if (FCntDown != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(FCntDown);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (Confirmed != false) {
+        size += 1 + 1;
+      }
+      if (FPort != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FPort);
+      }
+      if (Data.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Data);
+      }
+      if (object_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Object);
+      }
+      if (IsPending != false) {
+        size += 1 + 1;
+      }
+      if (FCntDown != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FCntDown);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceQueueItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.Confirmed != false) {
+        Confirmed = other.Confirmed;
+      }
+      if (other.FPort != 0) {
+        FPort = other.FPort;
+      }
+      if (other.Data.Length != 0) {
+        Data = other.Data;
+      }
+      if (other.object_ != null) {
+        if (object_ == null) {
+          Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+        }
+        Object.MergeFrom(other.Object);
+      }
+      if (other.IsPending != false) {
+        IsPending = other.IsPending;
+      }
+      if (other.FCntDown != 0) {
+        FCntDown = other.FCntDown;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 24: {
+            Confirmed = input.ReadBool();
+            break;
+          }
+          case 32: {
+            FPort = input.ReadUInt32();
+            break;
+          }
+          case 42: {
+            Data = input.ReadBytes();
+            break;
+          }
+          case 50: {
+            if (object_ == null) {
+              Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+            }
+            input.ReadMessage(Object);
+            break;
+          }
+          case 56: {
+            IsPending = input.ReadBool();
+            break;
+          }
+          case 64: {
+            FCntDown = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 24: {
+            Confirmed = input.ReadBool();
+            break;
+          }
+          case 32: {
+            FPort = input.ReadUInt32();
+            break;
+          }
+          case 42: {
+            Data = input.ReadBytes();
+            break;
+          }
+          case 50: {
+            if (object_ == null) {
+              Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+            }
+            input.ReadMessage(Object);
+            break;
+          }
+          case 56: {
+            IsPending = input.ReadBool();
+            break;
+          }
+          case 64: {
+            FCntDown = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class EnqueueDeviceQueueItemRequest : pb::IMessage<EnqueueDeviceQueueItemRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<EnqueueDeviceQueueItemRequest> _parser = new pb::MessageParser<EnqueueDeviceQueueItemRequest>(() => new EnqueueDeviceQueueItemRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<EnqueueDeviceQueueItemRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[29]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueDeviceQueueItemRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueDeviceQueueItemRequest(EnqueueDeviceQueueItemRequest other) : this() {
+      queueItem_ = other.queueItem_ != null ? other.queueItem_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueDeviceQueueItemRequest Clone() {
+      return new EnqueueDeviceQueueItemRequest(this);
+    }
+
+    /// <summary>Field number for the "queue_item" field.</summary>
+    public const int QueueItemFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceQueueItem queueItem_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceQueueItem QueueItem {
+      get { return queueItem_; }
+      set {
+        queueItem_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as EnqueueDeviceQueueItemRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(EnqueueDeviceQueueItemRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(QueueItem, other.QueueItem)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (queueItem_ != null) hash ^= QueueItem.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (queueItem_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(QueueItem);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (queueItem_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(QueueItem);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (queueItem_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(QueueItem);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(EnqueueDeviceQueueItemRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.queueItem_ != null) {
+        if (queueItem_ == null) {
+          QueueItem = new global::Chirpstack.Api.DeviceQueueItem();
+        }
+        QueueItem.MergeFrom(other.QueueItem);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (queueItem_ == null) {
+              QueueItem = new global::Chirpstack.Api.DeviceQueueItem();
+            }
+            input.ReadMessage(QueueItem);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (queueItem_ == null) {
+              QueueItem = new global::Chirpstack.Api.DeviceQueueItem();
+            }
+            input.ReadMessage(QueueItem);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class EnqueueDeviceQueueItemResponse : pb::IMessage<EnqueueDeviceQueueItemResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<EnqueueDeviceQueueItemResponse> _parser = new pb::MessageParser<EnqueueDeviceQueueItemResponse>(() => new EnqueueDeviceQueueItemResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<EnqueueDeviceQueueItemResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[30]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueDeviceQueueItemResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueDeviceQueueItemResponse(EnqueueDeviceQueueItemResponse other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueDeviceQueueItemResponse Clone() {
+      return new EnqueueDeviceQueueItemResponse(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as EnqueueDeviceQueueItemResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(EnqueueDeviceQueueItemResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(EnqueueDeviceQueueItemResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class FlushDeviceQueueRequest : pb::IMessage<FlushDeviceQueueRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<FlushDeviceQueueRequest> _parser = new pb::MessageParser<FlushDeviceQueueRequest>(() => new FlushDeviceQueueRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<FlushDeviceQueueRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[31]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FlushDeviceQueueRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FlushDeviceQueueRequest(FlushDeviceQueueRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FlushDeviceQueueRequest Clone() {
+      return new FlushDeviceQueueRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as FlushDeviceQueueRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(FlushDeviceQueueRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(FlushDeviceQueueRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceQueueItemsRequest : pb::IMessage<GetDeviceQueueItemsRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceQueueItemsRequest> _parser = new pb::MessageParser<GetDeviceQueueItemsRequest>(() => new GetDeviceQueueItemsRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceQueueItemsRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[32]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceQueueItemsRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceQueueItemsRequest(GetDeviceQueueItemsRequest other) : this() {
+      devEui_ = other.devEui_;
+      countOnly_ = other.countOnly_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceQueueItemsRequest Clone() {
+      return new GetDeviceQueueItemsRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "count_only" field.</summary>
+    public const int CountOnlyFieldNumber = 2;
+    private bool countOnly_;
+    /// <summary>
+    /// Return only the count, not the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool CountOnly {
+      get { return countOnly_; }
+      set {
+        countOnly_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceQueueItemsRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceQueueItemsRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      if (CountOnly != other.CountOnly) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (CountOnly != false) hash ^= CountOnly.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (CountOnly != false) {
+        output.WriteRawTag(16);
+        output.WriteBool(CountOnly);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (CountOnly != false) {
+        output.WriteRawTag(16);
+        output.WriteBool(CountOnly);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (CountOnly != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceQueueItemsRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.CountOnly != false) {
+        CountOnly = other.CountOnly;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 16: {
+            CountOnly = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 16: {
+            CountOnly = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceQueueItemsResponse : pb::IMessage<GetDeviceQueueItemsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceQueueItemsResponse> _parser = new pb::MessageParser<GetDeviceQueueItemsResponse>(() => new GetDeviceQueueItemsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceQueueItemsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[33]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceQueueItemsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceQueueItemsResponse(GetDeviceQueueItemsResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceQueueItemsResponse Clone() {
+      return new GetDeviceQueueItemsResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of queue items.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.DeviceQueueItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.DeviceQueueItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.DeviceQueueItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.DeviceQueueItem>();
+    /// <summary>
+    /// Result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.DeviceQueueItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceQueueItemsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceQueueItemsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceQueueItemsResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class FlushDevNoncesRequest : pb::IMessage<FlushDevNoncesRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<FlushDevNoncesRequest> _parser = new pb::MessageParser<FlushDevNoncesRequest>(() => new FlushDevNoncesRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<FlushDevNoncesRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.MessageTypes[34]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FlushDevNoncesRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FlushDevNoncesRequest(FlushDevNoncesRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FlushDevNoncesRequest Clone() {
+      return new FlushDevNoncesRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as FlushDevNoncesRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(FlushDevNoncesRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(FlushDevNoncesRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/DeviceGrpc.cs b/api/csharp/Chirpstack/api/DeviceGrpc.cs
new file mode 100644
index 00000000..428a42ce
--- /dev/null
+++ b/api/csharp/Chirpstack/api/DeviceGrpc.cs
@@ -0,0 +1,1516 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/device.proto
+// </auto-generated>
+#pragma warning disable 0414, 1591, 8981
+#region Designer generated code
+
+using grpc = global::Grpc.Core;
+
+namespace Chirpstack.Api {
+  /// <summary>
+  /// DeviceService is the service providing API methods for managing devices.
+  /// </summary>
+  public static partial class DeviceService
+  {
+    static readonly string __ServiceName = "api.DeviceService";
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (message is global::Google.Protobuf.IBufferMessage)
+      {
+        context.SetPayloadLength(message.CalculateSize());
+        global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
+        context.Complete();
+        return;
+      }
+      #endif
+      context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static class __Helper_MessageCache<T>
+    {
+      public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (__Helper_MessageCache<T>.IsBufferMessage)
+      {
+        return parser.ParseFrom(context.PayloadAsReadOnlySequence());
+      }
+      #endif
+      return parser.ParseFrom(context.PayloadAsNewBuffer());
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateDeviceRequest> __Marshaller_api_CreateDeviceRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateDeviceRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Google.Protobuf.WellKnownTypes.Empty> __Marshaller_google_protobuf_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Google.Protobuf.WellKnownTypes.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceRequest> __Marshaller_api_GetDeviceRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceResponse> __Marshaller_api_GetDeviceResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateDeviceRequest> __Marshaller_api_UpdateDeviceRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateDeviceRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteDeviceRequest> __Marshaller_api_DeleteDeviceRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteDeviceRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListDevicesRequest> __Marshaller_api_ListDevicesRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListDevicesRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListDevicesResponse> __Marshaller_api_ListDevicesResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListDevicesResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateDeviceKeysRequest> __Marshaller_api_CreateDeviceKeysRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateDeviceKeysRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceKeysRequest> __Marshaller_api_GetDeviceKeysRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceKeysRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceKeysResponse> __Marshaller_api_GetDeviceKeysResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceKeysResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateDeviceKeysRequest> __Marshaller_api_UpdateDeviceKeysRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateDeviceKeysRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteDeviceKeysRequest> __Marshaller_api_DeleteDeviceKeysRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteDeviceKeysRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.FlushDevNoncesRequest> __Marshaller_api_FlushDevNoncesRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.FlushDevNoncesRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ActivateDeviceRequest> __Marshaller_api_ActivateDeviceRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ActivateDeviceRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeactivateDeviceRequest> __Marshaller_api_DeactivateDeviceRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeactivateDeviceRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceActivationRequest> __Marshaller_api_GetDeviceActivationRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceActivationRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceActivationResponse> __Marshaller_api_GetDeviceActivationResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceActivationResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetRandomDevAddrRequest> __Marshaller_api_GetRandomDevAddrRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetRandomDevAddrRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetRandomDevAddrResponse> __Marshaller_api_GetRandomDevAddrResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetRandomDevAddrResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceMetricsRequest> __Marshaller_api_GetDeviceMetricsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceMetricsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceMetricsResponse> __Marshaller_api_GetDeviceMetricsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceMetricsResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceLinkMetricsRequest> __Marshaller_api_GetDeviceLinkMetricsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceLinkMetricsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceLinkMetricsResponse> __Marshaller_api_GetDeviceLinkMetricsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceLinkMetricsResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.EnqueueDeviceQueueItemRequest> __Marshaller_api_EnqueueDeviceQueueItemRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.EnqueueDeviceQueueItemRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.EnqueueDeviceQueueItemResponse> __Marshaller_api_EnqueueDeviceQueueItemResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.EnqueueDeviceQueueItemResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.FlushDeviceQueueRequest> __Marshaller_api_FlushDeviceQueueRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.FlushDeviceQueueRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceQueueItemsRequest> __Marshaller_api_GetDeviceQueueItemsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceQueueItemsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceQueueItemsResponse> __Marshaller_api_GetDeviceQueueItemsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceQueueItemsResponse.Parser));
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Create = new grpc::Method<global::Chirpstack.Api.CreateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Create",
+        __Marshaller_api_CreateDeviceRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetDeviceRequest, global::Chirpstack.Api.GetDeviceResponse> __Method_Get = new grpc::Method<global::Chirpstack.Api.GetDeviceRequest, global::Chirpstack.Api.GetDeviceResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Get",
+        __Marshaller_api_GetDeviceRequest,
+        __Marshaller_api_GetDeviceResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Update = new grpc::Method<global::Chirpstack.Api.UpdateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Update",
+        __Marshaller_api_UpdateDeviceRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Delete = new grpc::Method<global::Chirpstack.Api.DeleteDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Delete",
+        __Marshaller_api_DeleteDeviceRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListDevicesRequest, global::Chirpstack.Api.ListDevicesResponse> __Method_List = new grpc::Method<global::Chirpstack.Api.ListDevicesRequest, global::Chirpstack.Api.ListDevicesResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "List",
+        __Marshaller_api_ListDevicesRequest,
+        __Marshaller_api_ListDevicesResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateDeviceKeysRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_CreateKeys = new grpc::Method<global::Chirpstack.Api.CreateDeviceKeysRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateKeys",
+        __Marshaller_api_CreateDeviceKeysRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetDeviceKeysRequest, global::Chirpstack.Api.GetDeviceKeysResponse> __Method_GetKeys = new grpc::Method<global::Chirpstack.Api.GetDeviceKeysRequest, global::Chirpstack.Api.GetDeviceKeysResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetKeys",
+        __Marshaller_api_GetDeviceKeysRequest,
+        __Marshaller_api_GetDeviceKeysResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateDeviceKeysRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateKeys = new grpc::Method<global::Chirpstack.Api.UpdateDeviceKeysRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateKeys",
+        __Marshaller_api_UpdateDeviceKeysRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteDeviceKeysRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteKeys = new grpc::Method<global::Chirpstack.Api.DeleteDeviceKeysRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteKeys",
+        __Marshaller_api_DeleteDeviceKeysRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.FlushDevNoncesRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_FlushDevNonces = new grpc::Method<global::Chirpstack.Api.FlushDevNoncesRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "FlushDevNonces",
+        __Marshaller_api_FlushDevNoncesRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ActivateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Activate = new grpc::Method<global::Chirpstack.Api.ActivateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Activate",
+        __Marshaller_api_ActivateDeviceRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeactivateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Deactivate = new grpc::Method<global::Chirpstack.Api.DeactivateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Deactivate",
+        __Marshaller_api_DeactivateDeviceRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetDeviceActivationRequest, global::Chirpstack.Api.GetDeviceActivationResponse> __Method_GetActivation = new grpc::Method<global::Chirpstack.Api.GetDeviceActivationRequest, global::Chirpstack.Api.GetDeviceActivationResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetActivation",
+        __Marshaller_api_GetDeviceActivationRequest,
+        __Marshaller_api_GetDeviceActivationResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetRandomDevAddrRequest, global::Chirpstack.Api.GetRandomDevAddrResponse> __Method_GetRandomDevAddr = new grpc::Method<global::Chirpstack.Api.GetRandomDevAddrRequest, global::Chirpstack.Api.GetRandomDevAddrResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetRandomDevAddr",
+        __Marshaller_api_GetRandomDevAddrRequest,
+        __Marshaller_api_GetRandomDevAddrResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetDeviceMetricsRequest, global::Chirpstack.Api.GetDeviceMetricsResponse> __Method_GetMetrics = new grpc::Method<global::Chirpstack.Api.GetDeviceMetricsRequest, global::Chirpstack.Api.GetDeviceMetricsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetMetrics",
+        __Marshaller_api_GetDeviceMetricsRequest,
+        __Marshaller_api_GetDeviceMetricsResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetDeviceLinkMetricsRequest, global::Chirpstack.Api.GetDeviceLinkMetricsResponse> __Method_GetLinkMetrics = new grpc::Method<global::Chirpstack.Api.GetDeviceLinkMetricsRequest, global::Chirpstack.Api.GetDeviceLinkMetricsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetLinkMetrics",
+        __Marshaller_api_GetDeviceLinkMetricsRequest,
+        __Marshaller_api_GetDeviceLinkMetricsResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.EnqueueDeviceQueueItemRequest, global::Chirpstack.Api.EnqueueDeviceQueueItemResponse> __Method_Enqueue = new grpc::Method<global::Chirpstack.Api.EnqueueDeviceQueueItemRequest, global::Chirpstack.Api.EnqueueDeviceQueueItemResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Enqueue",
+        __Marshaller_api_EnqueueDeviceQueueItemRequest,
+        __Marshaller_api_EnqueueDeviceQueueItemResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.FlushDeviceQueueRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_FlushQueue = new grpc::Method<global::Chirpstack.Api.FlushDeviceQueueRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "FlushQueue",
+        __Marshaller_api_FlushDeviceQueueRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetDeviceQueueItemsRequest, global::Chirpstack.Api.GetDeviceQueueItemsResponse> __Method_GetQueue = new grpc::Method<global::Chirpstack.Api.GetDeviceQueueItemsRequest, global::Chirpstack.Api.GetDeviceQueueItemsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetQueue",
+        __Marshaller_api_GetDeviceQueueItemsRequest,
+        __Marshaller_api_GetDeviceQueueItemsResponse);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Chirpstack.Api.DeviceReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of DeviceService</summary>
+    [grpc::BindServiceMethod(typeof(DeviceService), "BindService")]
+    public abstract partial class DeviceServiceBase
+    {
+      /// <summary>
+      /// Create the given device.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Create(global::Chirpstack.Api.CreateDeviceRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get returns the device for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetDeviceResponse> Get(global::Chirpstack.Api.GetDeviceRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update the given device.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Update(global::Chirpstack.Api.UpdateDeviceRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete the device with the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Delete(global::Chirpstack.Api.DeleteDeviceRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the list of devices.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListDevicesResponse> List(global::Chirpstack.Api.ListDevicesRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Create the given device-keys.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> CreateKeys(global::Chirpstack.Api.CreateDeviceKeysRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the device-keys for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetDeviceKeysResponse> GetKeys(global::Chirpstack.Api.GetDeviceKeysRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update the given device-keys.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateKeys(global::Chirpstack.Api.UpdateDeviceKeysRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete the device-keys for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteKeys(global::Chirpstack.Api.DeleteDeviceKeysRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// FlushDevNonces flushes the OTAA device nonces.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> FlushDevNonces(global::Chirpstack.Api.FlushDevNoncesRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Activate (re)activates the device with the given parameters (for ABP or for importing OTAA activations).
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Activate(global::Chirpstack.Api.ActivateDeviceRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Deactivate de-activates the device.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Deactivate(global::Chirpstack.Api.DeactivateDeviceRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// GetActivation returns the current activation details of the device (OTAA or ABP).
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetDeviceActivationResponse> GetActivation(global::Chirpstack.Api.GetDeviceActivationRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// GetRandomDevAddr returns a random DevAddr taking the NwkID prefix into account.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetRandomDevAddrResponse> GetRandomDevAddr(global::Chirpstack.Api.GetRandomDevAddrRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// GetMetrics returns the device metrics.
+      /// Note that this requires a device-profile with codec and measurements configured.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetDeviceMetricsResponse> GetMetrics(global::Chirpstack.Api.GetDeviceMetricsRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// GetLinkMetrics returns the device link metrics.
+      /// This includes uplinks, downlinks, RSSI, SNR, etc...
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetDeviceLinkMetricsResponse> GetLinkMetrics(global::Chirpstack.Api.GetDeviceLinkMetricsRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Enqueue adds the given item to the downlink queue.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.EnqueueDeviceQueueItemResponse> Enqueue(global::Chirpstack.Api.EnqueueDeviceQueueItemRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// FlushQueue flushes the downlink device-queue.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> FlushQueue(global::Chirpstack.Api.FlushDeviceQueueRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// GetQueue returns the downlink device-queue.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetDeviceQueueItemsResponse> GetQueue(global::Chirpstack.Api.GetDeviceQueueItemsRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for DeviceService</summary>
+    public partial class DeviceServiceClient : grpc::ClientBase<DeviceServiceClient>
+    {
+      /// <summary>Creates a new client for DeviceService</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public DeviceServiceClient(grpc::ChannelBase channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for DeviceService that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public DeviceServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected DeviceServiceClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected DeviceServiceClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      /// Create the given device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Create(global::Chirpstack.Api.CreateDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Create(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create the given device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Create(global::Chirpstack.Api.CreateDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Create the given device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateAsync(global::Chirpstack.Api.CreateDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create the given device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateAsync(global::Chirpstack.Api.CreateDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Get returns the device for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceResponse Get(global::Chirpstack.Api.GetDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Get(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get returns the device for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceResponse Get(global::Chirpstack.Api.GetDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Get returns the device for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceResponse> GetAsync(global::Chirpstack.Api.GetDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get returns the device for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceResponse> GetAsync(global::Chirpstack.Api.GetDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Update the given device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Update(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Update the given device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Delete the device with the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Delete(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the device with the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Delete the device with the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the device with the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of devices.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListDevicesResponse List(global::Chirpstack.Api.ListDevicesRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return List(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of devices.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListDevicesResponse List(global::Chirpstack.Api.ListDevicesRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of devices.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListDevicesResponse> ListAsync(global::Chirpstack.Api.ListDevicesRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of devices.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListDevicesResponse> ListAsync(global::Chirpstack.Api.ListDevicesRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// Create the given device-keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateKeys(global::Chirpstack.Api.CreateDeviceKeysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateKeys(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create the given device-keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty CreateKeys(global::Chirpstack.Api.CreateDeviceKeysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateKeys, null, options, request);
+      }
+      /// <summary>
+      /// Create the given device-keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateKeysAsync(global::Chirpstack.Api.CreateDeviceKeysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateKeysAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create the given device-keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateKeysAsync(global::Chirpstack.Api.CreateDeviceKeysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateKeys, null, options, request);
+      }
+      /// <summary>
+      /// Get the device-keys for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceKeysResponse GetKeys(global::Chirpstack.Api.GetDeviceKeysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetKeys(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the device-keys for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceKeysResponse GetKeys(global::Chirpstack.Api.GetDeviceKeysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetKeys, null, options, request);
+      }
+      /// <summary>
+      /// Get the device-keys for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceKeysResponse> GetKeysAsync(global::Chirpstack.Api.GetDeviceKeysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetKeysAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the device-keys for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceKeysResponse> GetKeysAsync(global::Chirpstack.Api.GetDeviceKeysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetKeys, null, options, request);
+      }
+      /// <summary>
+      /// Update the given device-keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateKeys(global::Chirpstack.Api.UpdateDeviceKeysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateKeys(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given device-keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateKeys(global::Chirpstack.Api.UpdateDeviceKeysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateKeys, null, options, request);
+      }
+      /// <summary>
+      /// Update the given device-keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateKeysAsync(global::Chirpstack.Api.UpdateDeviceKeysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateKeysAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given device-keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateKeysAsync(global::Chirpstack.Api.UpdateDeviceKeysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateKeys, null, options, request);
+      }
+      /// <summary>
+      /// Delete the device-keys for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteKeys(global::Chirpstack.Api.DeleteDeviceKeysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteKeys(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the device-keys for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteKeys(global::Chirpstack.Api.DeleteDeviceKeysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteKeys, null, options, request);
+      }
+      /// <summary>
+      /// Delete the device-keys for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteKeysAsync(global::Chirpstack.Api.DeleteDeviceKeysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteKeysAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the device-keys for the given DevEUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteKeysAsync(global::Chirpstack.Api.DeleteDeviceKeysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteKeys, null, options, request);
+      }
+      /// <summary>
+      /// FlushDevNonces flushes the OTAA device nonces.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty FlushDevNonces(global::Chirpstack.Api.FlushDevNoncesRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return FlushDevNonces(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// FlushDevNonces flushes the OTAA device nonces.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty FlushDevNonces(global::Chirpstack.Api.FlushDevNoncesRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_FlushDevNonces, null, options, request);
+      }
+      /// <summary>
+      /// FlushDevNonces flushes the OTAA device nonces.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> FlushDevNoncesAsync(global::Chirpstack.Api.FlushDevNoncesRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return FlushDevNoncesAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// FlushDevNonces flushes the OTAA device nonces.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> FlushDevNoncesAsync(global::Chirpstack.Api.FlushDevNoncesRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_FlushDevNonces, null, options, request);
+      }
+      /// <summary>
+      /// Activate (re)activates the device with the given parameters (for ABP or for importing OTAA activations).
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Activate(global::Chirpstack.Api.ActivateDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Activate(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Activate (re)activates the device with the given parameters (for ABP or for importing OTAA activations).
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Activate(global::Chirpstack.Api.ActivateDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Activate, null, options, request);
+      }
+      /// <summary>
+      /// Activate (re)activates the device with the given parameters (for ABP or for importing OTAA activations).
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> ActivateAsync(global::Chirpstack.Api.ActivateDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ActivateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Activate (re)activates the device with the given parameters (for ABP or for importing OTAA activations).
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> ActivateAsync(global::Chirpstack.Api.ActivateDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Activate, null, options, request);
+      }
+      /// <summary>
+      /// Deactivate de-activates the device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Deactivate(global::Chirpstack.Api.DeactivateDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Deactivate(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Deactivate de-activates the device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Deactivate(global::Chirpstack.Api.DeactivateDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Deactivate, null, options, request);
+      }
+      /// <summary>
+      /// Deactivate de-activates the device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeactivateAsync(global::Chirpstack.Api.DeactivateDeviceRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeactivateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Deactivate de-activates the device.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeactivateAsync(global::Chirpstack.Api.DeactivateDeviceRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Deactivate, null, options, request);
+      }
+      /// <summary>
+      /// GetActivation returns the current activation details of the device (OTAA or ABP).
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceActivationResponse GetActivation(global::Chirpstack.Api.GetDeviceActivationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetActivation(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetActivation returns the current activation details of the device (OTAA or ABP).
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceActivationResponse GetActivation(global::Chirpstack.Api.GetDeviceActivationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetActivation, null, options, request);
+      }
+      /// <summary>
+      /// GetActivation returns the current activation details of the device (OTAA or ABP).
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceActivationResponse> GetActivationAsync(global::Chirpstack.Api.GetDeviceActivationRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetActivationAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetActivation returns the current activation details of the device (OTAA or ABP).
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceActivationResponse> GetActivationAsync(global::Chirpstack.Api.GetDeviceActivationRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetActivation, null, options, request);
+      }
+      /// <summary>
+      /// GetRandomDevAddr returns a random DevAddr taking the NwkID prefix into account.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetRandomDevAddrResponse GetRandomDevAddr(global::Chirpstack.Api.GetRandomDevAddrRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetRandomDevAddr(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetRandomDevAddr returns a random DevAddr taking the NwkID prefix into account.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetRandomDevAddrResponse GetRandomDevAddr(global::Chirpstack.Api.GetRandomDevAddrRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetRandomDevAddr, null, options, request);
+      }
+      /// <summary>
+      /// GetRandomDevAddr returns a random DevAddr taking the NwkID prefix into account.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetRandomDevAddrResponse> GetRandomDevAddrAsync(global::Chirpstack.Api.GetRandomDevAddrRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetRandomDevAddrAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetRandomDevAddr returns a random DevAddr taking the NwkID prefix into account.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetRandomDevAddrResponse> GetRandomDevAddrAsync(global::Chirpstack.Api.GetRandomDevAddrRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetRandomDevAddr, null, options, request);
+      }
+      /// <summary>
+      /// GetMetrics returns the device metrics.
+      /// Note that this requires a device-profile with codec and measurements configured.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceMetricsResponse GetMetrics(global::Chirpstack.Api.GetDeviceMetricsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetMetrics(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetMetrics returns the device metrics.
+      /// Note that this requires a device-profile with codec and measurements configured.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceMetricsResponse GetMetrics(global::Chirpstack.Api.GetDeviceMetricsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetMetrics, null, options, request);
+      }
+      /// <summary>
+      /// GetMetrics returns the device metrics.
+      /// Note that this requires a device-profile with codec and measurements configured.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceMetricsResponse> GetMetricsAsync(global::Chirpstack.Api.GetDeviceMetricsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetMetricsAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetMetrics returns the device metrics.
+      /// Note that this requires a device-profile with codec and measurements configured.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceMetricsResponse> GetMetricsAsync(global::Chirpstack.Api.GetDeviceMetricsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetMetrics, null, options, request);
+      }
+      /// <summary>
+      /// GetLinkMetrics returns the device link metrics.
+      /// This includes uplinks, downlinks, RSSI, SNR, etc...
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceLinkMetricsResponse GetLinkMetrics(global::Chirpstack.Api.GetDeviceLinkMetricsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetLinkMetrics(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetLinkMetrics returns the device link metrics.
+      /// This includes uplinks, downlinks, RSSI, SNR, etc...
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceLinkMetricsResponse GetLinkMetrics(global::Chirpstack.Api.GetDeviceLinkMetricsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetLinkMetrics, null, options, request);
+      }
+      /// <summary>
+      /// GetLinkMetrics returns the device link metrics.
+      /// This includes uplinks, downlinks, RSSI, SNR, etc...
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceLinkMetricsResponse> GetLinkMetricsAsync(global::Chirpstack.Api.GetDeviceLinkMetricsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetLinkMetricsAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetLinkMetrics returns the device link metrics.
+      /// This includes uplinks, downlinks, RSSI, SNR, etc...
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceLinkMetricsResponse> GetLinkMetricsAsync(global::Chirpstack.Api.GetDeviceLinkMetricsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetLinkMetrics, null, options, request);
+      }
+      /// <summary>
+      /// Enqueue adds the given item to the downlink queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.EnqueueDeviceQueueItemResponse Enqueue(global::Chirpstack.Api.EnqueueDeviceQueueItemRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Enqueue(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Enqueue adds the given item to the downlink queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.EnqueueDeviceQueueItemResponse Enqueue(global::Chirpstack.Api.EnqueueDeviceQueueItemRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Enqueue, null, options, request);
+      }
+      /// <summary>
+      /// Enqueue adds the given item to the downlink queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.EnqueueDeviceQueueItemResponse> EnqueueAsync(global::Chirpstack.Api.EnqueueDeviceQueueItemRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return EnqueueAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Enqueue adds the given item to the downlink queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.EnqueueDeviceQueueItemResponse> EnqueueAsync(global::Chirpstack.Api.EnqueueDeviceQueueItemRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Enqueue, null, options, request);
+      }
+      /// <summary>
+      /// FlushQueue flushes the downlink device-queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty FlushQueue(global::Chirpstack.Api.FlushDeviceQueueRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return FlushQueue(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// FlushQueue flushes the downlink device-queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty FlushQueue(global::Chirpstack.Api.FlushDeviceQueueRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_FlushQueue, null, options, request);
+      }
+      /// <summary>
+      /// FlushQueue flushes the downlink device-queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> FlushQueueAsync(global::Chirpstack.Api.FlushDeviceQueueRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return FlushQueueAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// FlushQueue flushes the downlink device-queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> FlushQueueAsync(global::Chirpstack.Api.FlushDeviceQueueRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_FlushQueue, null, options, request);
+      }
+      /// <summary>
+      /// GetQueue returns the downlink device-queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceQueueItemsResponse GetQueue(global::Chirpstack.Api.GetDeviceQueueItemsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetQueue(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetQueue returns the downlink device-queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceQueueItemsResponse GetQueue(global::Chirpstack.Api.GetDeviceQueueItemsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetQueue, null, options, request);
+      }
+      /// <summary>
+      /// GetQueue returns the downlink device-queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceQueueItemsResponse> GetQueueAsync(global::Chirpstack.Api.GetDeviceQueueItemsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetQueueAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetQueue returns the downlink device-queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceQueueItemsResponse> GetQueueAsync(global::Chirpstack.Api.GetDeviceQueueItemsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetQueue, null, options, request);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected override DeviceServiceClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new DeviceServiceClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static grpc::ServerServiceDefinition BindService(DeviceServiceBase serviceImpl)
+    {
+      return grpc::ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_Create, serviceImpl.Create)
+          .AddMethod(__Method_Get, serviceImpl.Get)
+          .AddMethod(__Method_Update, serviceImpl.Update)
+          .AddMethod(__Method_Delete, serviceImpl.Delete)
+          .AddMethod(__Method_List, serviceImpl.List)
+          .AddMethod(__Method_CreateKeys, serviceImpl.CreateKeys)
+          .AddMethod(__Method_GetKeys, serviceImpl.GetKeys)
+          .AddMethod(__Method_UpdateKeys, serviceImpl.UpdateKeys)
+          .AddMethod(__Method_DeleteKeys, serviceImpl.DeleteKeys)
+          .AddMethod(__Method_FlushDevNonces, serviceImpl.FlushDevNonces)
+          .AddMethod(__Method_Activate, serviceImpl.Activate)
+          .AddMethod(__Method_Deactivate, serviceImpl.Deactivate)
+          .AddMethod(__Method_GetActivation, serviceImpl.GetActivation)
+          .AddMethod(__Method_GetRandomDevAddr, serviceImpl.GetRandomDevAddr)
+          .AddMethod(__Method_GetMetrics, serviceImpl.GetMetrics)
+          .AddMethod(__Method_GetLinkMetrics, serviceImpl.GetLinkMetrics)
+          .AddMethod(__Method_Enqueue, serviceImpl.Enqueue)
+          .AddMethod(__Method_FlushQueue, serviceImpl.FlushQueue)
+          .AddMethod(__Method_GetQueue, serviceImpl.GetQueue).Build();
+    }
+
+    /// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
+    /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+    /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static void BindService(grpc::ServiceBinderBase serviceBinder, DeviceServiceBase serviceImpl)
+    {
+      serviceBinder.AddMethod(__Method_Create, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Create));
+      serviceBinder.AddMethod(__Method_Get, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetDeviceRequest, global::Chirpstack.Api.GetDeviceResponse>(serviceImpl.Get));
+      serviceBinder.AddMethod(__Method_Update, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Update));
+      serviceBinder.AddMethod(__Method_Delete, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Delete));
+      serviceBinder.AddMethod(__Method_List, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListDevicesRequest, global::Chirpstack.Api.ListDevicesResponse>(serviceImpl.List));
+      serviceBinder.AddMethod(__Method_CreateKeys, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateDeviceKeysRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.CreateKeys));
+      serviceBinder.AddMethod(__Method_GetKeys, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetDeviceKeysRequest, global::Chirpstack.Api.GetDeviceKeysResponse>(serviceImpl.GetKeys));
+      serviceBinder.AddMethod(__Method_UpdateKeys, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateDeviceKeysRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateKeys));
+      serviceBinder.AddMethod(__Method_DeleteKeys, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteDeviceKeysRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteKeys));
+      serviceBinder.AddMethod(__Method_FlushDevNonces, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.FlushDevNoncesRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.FlushDevNonces));
+      serviceBinder.AddMethod(__Method_Activate, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ActivateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Activate));
+      serviceBinder.AddMethod(__Method_Deactivate, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeactivateDeviceRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Deactivate));
+      serviceBinder.AddMethod(__Method_GetActivation, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetDeviceActivationRequest, global::Chirpstack.Api.GetDeviceActivationResponse>(serviceImpl.GetActivation));
+      serviceBinder.AddMethod(__Method_GetRandomDevAddr, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetRandomDevAddrRequest, global::Chirpstack.Api.GetRandomDevAddrResponse>(serviceImpl.GetRandomDevAddr));
+      serviceBinder.AddMethod(__Method_GetMetrics, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetDeviceMetricsRequest, global::Chirpstack.Api.GetDeviceMetricsResponse>(serviceImpl.GetMetrics));
+      serviceBinder.AddMethod(__Method_GetLinkMetrics, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetDeviceLinkMetricsRequest, global::Chirpstack.Api.GetDeviceLinkMetricsResponse>(serviceImpl.GetLinkMetrics));
+      serviceBinder.AddMethod(__Method_Enqueue, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.EnqueueDeviceQueueItemRequest, global::Chirpstack.Api.EnqueueDeviceQueueItemResponse>(serviceImpl.Enqueue));
+      serviceBinder.AddMethod(__Method_FlushQueue, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.FlushDeviceQueueRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.FlushQueue));
+      serviceBinder.AddMethod(__Method_GetQueue, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetDeviceQueueItemsRequest, global::Chirpstack.Api.GetDeviceQueueItemsResponse>(serviceImpl.GetQueue));
+    }
+
+  }
+}
+#endregion
diff --git a/api/csharp/Chirpstack/api/DeviceProfile.cs b/api/csharp/Chirpstack/api/DeviceProfile.cs
new file mode 100644
index 00000000..20d67f1e
--- /dev/null
+++ b/api/csharp/Chirpstack/api/DeviceProfile.cs
@@ -0,0 +1,4543 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/device_profile.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/device_profile.proto</summary>
+  public static partial class DeviceProfileReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/device_profile.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static DeviceProfileReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChhhcGkvZGV2aWNlX3Byb2ZpbGUucHJvdG8SA2FwaRocZ29vZ2xlL2FwaS9h",
+            "bm5vdGF0aW9ucy5wcm90bxofZ29vZ2xlL3Byb3RvYnVmL3RpbWVzdGFtcC5w",
+            "cm90bxobZ29vZ2xlL3Byb3RvYnVmL2VtcHR5LnByb3RvGhNjb21tb24vY29t",
+            "bW9uLnByb3RvIukHCg1EZXZpY2VQcm9maWxlEgoKAmlkGAEgASgJEhEKCXRl",
+            "bmFudF9pZBgCIAEoCRIMCgRuYW1lGAMgASgJEhMKC2Rlc2NyaXB0aW9uGBog",
+            "ASgJEh4KBnJlZ2lvbhgEIAEoDjIOLmNvbW1vbi5SZWdpb24SJwoLbWFjX3Zl",
+            "cnNpb24YBSABKA4yEi5jb21tb24uTWFjVmVyc2lvbhI2ChNyZWdfcGFyYW1z",
+            "X3JldmlzaW9uGAYgASgOMhkuY29tbW9uLlJlZ1BhcmFtc1JldmlzaW9uEhgK",
+            "EGFkcl9hbGdvcml0aG1faWQYByABKAkSMAoVcGF5bG9hZF9jb2RlY19ydW50",
+            "aW1lGAggASgOMhEuYXBpLkNvZGVjUnVudGltZRIcChRwYXlsb2FkX2NvZGVj",
+            "X3NjcmlwdBgJIAEoCRIfChdmbHVzaF9xdWV1ZV9vbl9hY3RpdmF0ZRgKIAEo",
+            "CBIXCg91cGxpbmtfaW50ZXJ2YWwYCyABKA0SIgoaZGV2aWNlX3N0YXR1c19y",
+            "ZXFfaW50ZXJ2YWwYDCABKA0SFQoNc3VwcG9ydHNfb3RhYRgNIAEoCBIYChBz",
+            "dXBwb3J0c19jbGFzc19iGA4gASgIEhgKEHN1cHBvcnRzX2NsYXNzX2MYDyAB",
+            "KAgSFwoPY2xhc3NfYl90aW1lb3V0GBAgASgNEh4KFmNsYXNzX2JfcGluZ19z",
+            "bG90X25iX2sYESABKA0SHAoUY2xhc3NfYl9waW5nX3Nsb3RfZHIYEiABKA0S",
+            "HgoWY2xhc3NfYl9waW5nX3Nsb3RfZnJlcRgTIAEoDRIXCg9jbGFzc19jX3Rp",
+            "bWVvdXQYFCABKA0SFQoNYWJwX3J4MV9kZWxheRgVIAEoDRIZChFhYnBfcngx",
+            "X2RyX29mZnNldBgWIAEoDRISCgphYnBfcngyX2RyGBcgASgNEhQKDGFicF9y",
+            "eDJfZnJlcRgYIAEoDRIqCgR0YWdzGBkgAygLMhwuYXBpLkRldmljZVByb2Zp",
+            "bGUuVGFnc0VudHJ5EjoKDG1lYXN1cmVtZW50cxgbIAMoCzIkLmFwaS5EZXZp",
+            "Y2VQcm9maWxlLk1lYXN1cmVtZW50c0VudHJ5EiAKGGF1dG9fZGV0ZWN0X21l",
+            "YXN1cmVtZW50cxgcIAEoCBIYChByZWdpb25fY29uZmlnX2lkGB0gASgJGisK",
+            "CVRhZ3NFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBGkUK",
+            "EU1lYXN1cmVtZW50c0VudHJ5EgsKA2tleRgBIAEoCRIfCgV2YWx1ZRgCIAEo",
+            "CzIQLmFwaS5NZWFzdXJlbWVudDoCOAEiPwoLTWVhc3VyZW1lbnQSDAoEbmFt",
+            "ZRgCIAEoCRIiCgRraW5kGAMgASgOMhQuYXBpLk1lYXN1cmVtZW50S2luZCLd",
+            "AgoVRGV2aWNlUHJvZmlsZUxpc3RJdGVtEgoKAmlkGAEgASgJEi4KCmNyZWF0",
+            "ZWRfYXQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCnVw",
+            "ZGF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEgwK",
+            "BG5hbWUYBCABKAkSHgoGcmVnaW9uGAUgASgOMg4uY29tbW9uLlJlZ2lvbhIn",
+            "CgttYWNfdmVyc2lvbhgGIAEoDjISLmNvbW1vbi5NYWNWZXJzaW9uEjYKE3Jl",
+            "Z19wYXJhbXNfcmV2aXNpb24YByABKA4yGS5jb21tb24uUmVnUGFyYW1zUmV2",
+            "aXNpb24SFQoNc3VwcG9ydHNfb3RhYRgIIAEoCBIYChBzdXBwb3J0c19jbGFz",
+            "c19iGAkgASgIEhgKEHN1cHBvcnRzX2NsYXNzX2MYCiABKAgiSAoaQ3JlYXRl",
+            "RGV2aWNlUHJvZmlsZVJlcXVlc3QSKgoOZGV2aWNlX3Byb2ZpbGUYASABKAsy",
+            "Ei5hcGkuRGV2aWNlUHJvZmlsZSIpChtDcmVhdGVEZXZpY2VQcm9maWxlUmVz",
+            "cG9uc2USCgoCaWQYASABKAkiJQoXR2V0RGV2aWNlUHJvZmlsZVJlcXVlc3QS",
+            "CgoCaWQYASABKAkipgEKGEdldERldmljZVByb2ZpbGVSZXNwb25zZRIqCg5k",
+            "ZXZpY2VfcHJvZmlsZRgBIAEoCzISLmFwaS5EZXZpY2VQcm9maWxlEi4KCmNy",
+            "ZWF0ZWRfYXQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4K",
+            "CnVwZGF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1w",
+            "IkgKGlVwZGF0ZURldmljZVByb2ZpbGVSZXF1ZXN0EioKDmRldmljZV9wcm9m",
+            "aWxlGAEgASgLMhIuYXBpLkRldmljZVByb2ZpbGUiKAoaRGVsZXRlRGV2aWNl",
+            "UHJvZmlsZVJlcXVlc3QSCgoCaWQYASABKAkiXQoZTGlzdERldmljZVByb2Zp",
+            "bGVzUmVxdWVzdBINCgVsaW1pdBgBIAEoDRIOCgZvZmZzZXQYAiABKA0SDgoG",
+            "c2VhcmNoGAMgASgJEhEKCXRlbmFudF9pZBgEIAEoCSJdChpMaXN0RGV2aWNl",
+            "UHJvZmlsZXNSZXNwb25zZRITCgt0b3RhbF9jb3VudBgBIAEoDRIqCgZyZXN1",
+            "bHQYAiADKAsyGi5hcGkuRGV2aWNlUHJvZmlsZUxpc3RJdGVtImgKJkxpc3RE",
+            "ZXZpY2VQcm9maWxlQWRyQWxnb3JpdGhtc1Jlc3BvbnNlEhMKC3RvdGFsX2Nv",
+            "dW50GAEgASgNEikKBnJlc3VsdBgCIAMoCzIZLmFwaS5BZHJBbGdvcml0aG1M",
+            "aXN0SXRlbSIwChRBZHJBbGdvcml0aG1MaXN0SXRlbRIKCgJpZBgBIAEoCRIM",
+            "CgRuYW1lGAIgASgJKjEKDENvZGVjUnVudGltZRIICgROT05FEAASDwoLQ0FZ",
+            "RU5ORV9MUFAQARIGCgJKUxACKlAKD01lYXN1cmVtZW50S2luZBILCgdVTktO",
+            "T1dOEAASCwoHQ09VTlRFUhABEgwKCEFCU09MVVRFEAISCQoFR0FVR0UQAxIK",
+            "CgZTVFJJTkcQBDK4BQoURGV2aWNlUHJvZmlsZVNlcnZpY2USbAoGQ3JlYXRl",
+            "Eh8uYXBpLkNyZWF0ZURldmljZVByb2ZpbGVSZXF1ZXN0GiAuYXBpLkNyZWF0",
+            "ZURldmljZVByb2ZpbGVSZXNwb25zZSIfgtPkkwIZIhQvYXBpL2RldmljZS1w",
+            "cm9maWxlczoBKhJlCgNHZXQSHC5hcGkuR2V0RGV2aWNlUHJvZmlsZVJlcXVl",
+            "c3QaHS5hcGkuR2V0RGV2aWNlUHJvZmlsZVJlc3BvbnNlIiGC0+STAhsSGS9h",
+            "cGkvZGV2aWNlLXByb2ZpbGVzL3tpZH0SdgoGVXBkYXRlEh8uYXBpLlVwZGF0",
+            "ZURldmljZVByb2ZpbGVSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5",
+            "IjOC0+STAi0aKC9hcGkvZGV2aWNlLXByb2ZpbGVzL3tkZXZpY2VfcHJvZmls",
+            "ZS5pZH06ASoSZAoGRGVsZXRlEh8uYXBpLkRlbGV0ZURldmljZVByb2ZpbGVS",
+            "ZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IiGC0+STAhsqGS9hcGkv",
+            "ZGV2aWNlLXByb2ZpbGVzL3tpZH0SZQoETGlzdBIeLmFwaS5MaXN0RGV2aWNl",
+            "UHJvZmlsZXNSZXF1ZXN0Gh8uYXBpLkxpc3REZXZpY2VQcm9maWxlc1Jlc3Bv",
+            "bnNlIhyC0+STAhYSFC9hcGkvZGV2aWNlLXByb2ZpbGVzEoUBChFMaXN0QWRy",
+            "QWxnb3JpdGhtcxIWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRorLmFwaS5MaXN0",
+            "RGV2aWNlUHJvZmlsZUFkckFsZ29yaXRobXNSZXNwb25zZSIrgtPkkwIlEiMv",
+            "YXBpL2RldmljZS1wcm9maWxlcy9hZHItYWxnb3JpdGhtc0JqChFpby5jaGly",
+            "cHN0YWNrLmFwaUISRGV2aWNlUHJvZmlsZVByb3RvUAFaLmdpdGh1Yi5jb20v",
+            "Y2hpcnBzdGFjay9jaGlycHN0YWNrL2FwaS9nby92NC9hcGmqAg5DaGlycHN0",
+            "YWNrLkFwaWIGcHJvdG8z"));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Api.AnnotationsReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Chirpstack.Common.CommonReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Chirpstack.Api.CodecRuntime), typeof(global::Chirpstack.Api.MeasurementKind), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceProfile), global::Chirpstack.Api.DeviceProfile.Parser, new[]{ "Id", "TenantId", "Name", "Description", "Region", "MacVersion", "RegParamsRevision", "AdrAlgorithmId", "PayloadCodecRuntime", "PayloadCodecScript", "FlushQueueOnActivate", "UplinkInterval", "DeviceStatusReqInterval", "SupportsOtaa", "SupportsClassB", "SupportsClassC", "ClassBTimeout", "ClassBPingSlotNbK", "ClassBPingSlotDr", "ClassBPingSlotFreq", "ClassCTimeout", "AbpRx1Delay", "AbpRx1DrOffset", "AbpRx2Dr", "AbpRx2Freq", "Tags", "Measurements", "AutoDetectMeasurements", "RegionConfigId" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.Measurement), global::Chirpstack.Api.Measurement.Parser, new[]{ "Name", "Kind" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceProfileListItem), global::Chirpstack.Api.DeviceProfileListItem.Parser, new[]{ "Id", "CreatedAt", "UpdatedAt", "Name", "Region", "MacVersion", "RegParamsRevision", "SupportsOtaa", "SupportsClassB", "SupportsClassC" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateDeviceProfileRequest), global::Chirpstack.Api.CreateDeviceProfileRequest.Parser, new[]{ "DeviceProfile" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateDeviceProfileResponse), global::Chirpstack.Api.CreateDeviceProfileResponse.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceProfileRequest), global::Chirpstack.Api.GetDeviceProfileRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceProfileResponse), global::Chirpstack.Api.GetDeviceProfileResponse.Parser, new[]{ "DeviceProfile", "CreatedAt", "UpdatedAt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateDeviceProfileRequest), global::Chirpstack.Api.UpdateDeviceProfileRequest.Parser, new[]{ "DeviceProfile" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteDeviceProfileRequest), global::Chirpstack.Api.DeleteDeviceProfileRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListDeviceProfilesRequest), global::Chirpstack.Api.ListDeviceProfilesRequest.Parser, new[]{ "Limit", "Offset", "Search", "TenantId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListDeviceProfilesResponse), global::Chirpstack.Api.ListDeviceProfilesResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse), global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.AdrAlgorithmListItem), global::Chirpstack.Api.AdrAlgorithmListItem.Parser, new[]{ "Id", "Name" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Enums
+  public enum CodecRuntime {
+    /// <summary>
+    /// None.
+    /// </summary>
+    [pbr::OriginalName("NONE")] None = 0,
+    /// <summary>
+    /// Cayenne LPP.
+    /// </summary>
+    [pbr::OriginalName("CAYENNE_LPP")] CayenneLpp = 1,
+    /// <summary>
+    /// JavaScript.
+    /// </summary>
+    [pbr::OriginalName("JS")] Js = 2,
+  }
+
+  public enum MeasurementKind {
+    /// <summary>
+    /// Unknown (in which case it is not tracked).
+    /// </summary>
+    [pbr::OriginalName("UNKNOWN")] Unknown = 0,
+    /// <summary>
+    /// Incrementing counters that never decrease (these are not reset on each reading).
+    /// </summary>
+    [pbr::OriginalName("COUNTER")] Counter = 1,
+    /// <summary>
+    /// Counters that do get reset upon reading.
+    /// </summary>
+    [pbr::OriginalName("ABSOLUTE")] Absolute = 2,
+    /// <summary>
+    /// E.g. a temperature value.
+    /// </summary>
+    [pbr::OriginalName("GAUGE")] Gauge = 3,
+    /// <summary>
+    /// E.g. a firmware version, true / false value.
+    /// </summary>
+    [pbr::OriginalName("STRING")] String = 4,
+  }
+
+  #endregion
+
+  #region Messages
+  public sealed partial class DeviceProfile : pb::IMessage<DeviceProfile>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceProfile> _parser = new pb::MessageParser<DeviceProfile>(() => new DeviceProfile());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceProfile> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfile() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfile(DeviceProfile other) : this() {
+      id_ = other.id_;
+      tenantId_ = other.tenantId_;
+      name_ = other.name_;
+      description_ = other.description_;
+      region_ = other.region_;
+      macVersion_ = other.macVersion_;
+      regParamsRevision_ = other.regParamsRevision_;
+      adrAlgorithmId_ = other.adrAlgorithmId_;
+      payloadCodecRuntime_ = other.payloadCodecRuntime_;
+      payloadCodecScript_ = other.payloadCodecScript_;
+      flushQueueOnActivate_ = other.flushQueueOnActivate_;
+      uplinkInterval_ = other.uplinkInterval_;
+      deviceStatusReqInterval_ = other.deviceStatusReqInterval_;
+      supportsOtaa_ = other.supportsOtaa_;
+      supportsClassB_ = other.supportsClassB_;
+      supportsClassC_ = other.supportsClassC_;
+      classBTimeout_ = other.classBTimeout_;
+      classBPingSlotNbK_ = other.classBPingSlotNbK_;
+      classBPingSlotDr_ = other.classBPingSlotDr_;
+      classBPingSlotFreq_ = other.classBPingSlotFreq_;
+      classCTimeout_ = other.classCTimeout_;
+      abpRx1Delay_ = other.abpRx1Delay_;
+      abpRx1DrOffset_ = other.abpRx1DrOffset_;
+      abpRx2Dr_ = other.abpRx2Dr_;
+      abpRx2Freq_ = other.abpRx2Freq_;
+      tags_ = other.tags_.Clone();
+      measurements_ = other.measurements_.Clone();
+      autoDetectMeasurements_ = other.autoDetectMeasurements_;
+      regionConfigId_ = other.regionConfigId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfile Clone() {
+      return new DeviceProfile(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Device-profile ID (UUID).
+    /// Note: on create this will be automatically generated.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 2;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 3;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 26;
+    private string description_ = "";
+    /// <summary>
+    /// Description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "region" field.</summary>
+    public const int RegionFieldNumber = 4;
+    private global::Chirpstack.Common.Region region_ = global::Chirpstack.Common.Region.Eu868;
+    /// <summary>
+    /// Region.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Region Region {
+      get { return region_; }
+      set {
+        region_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "mac_version" field.</summary>
+    public const int MacVersionFieldNumber = 5;
+    private global::Chirpstack.Common.MacVersion macVersion_ = global::Chirpstack.Common.MacVersion.Lorawan100;
+    /// <summary>
+    /// LoRaWAN mac-version.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.MacVersion MacVersion {
+      get { return macVersion_; }
+      set {
+        macVersion_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "reg_params_revision" field.</summary>
+    public const int RegParamsRevisionFieldNumber = 6;
+    private global::Chirpstack.Common.RegParamsRevision regParamsRevision_ = global::Chirpstack.Common.RegParamsRevision.A;
+    /// <summary>
+    /// Regional parameters revision.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.RegParamsRevision RegParamsRevision {
+      get { return regParamsRevision_; }
+      set {
+        regParamsRevision_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "adr_algorithm_id" field.</summary>
+    public const int AdrAlgorithmIdFieldNumber = 7;
+    private string adrAlgorithmId_ = "";
+    /// <summary>
+    /// ADR algorithm ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string AdrAlgorithmId {
+      get { return adrAlgorithmId_; }
+      set {
+        adrAlgorithmId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "payload_codec_runtime" field.</summary>
+    public const int PayloadCodecRuntimeFieldNumber = 8;
+    private global::Chirpstack.Api.CodecRuntime payloadCodecRuntime_ = global::Chirpstack.Api.CodecRuntime.None;
+    /// <summary>
+    /// Payload codec runtime.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.CodecRuntime PayloadCodecRuntime {
+      get { return payloadCodecRuntime_; }
+      set {
+        payloadCodecRuntime_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "payload_codec_script" field.</summary>
+    public const int PayloadCodecScriptFieldNumber = 9;
+    private string payloadCodecScript_ = "";
+    /// <summary>
+    /// Payload codec script.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string PayloadCodecScript {
+      get { return payloadCodecScript_; }
+      set {
+        payloadCodecScript_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "flush_queue_on_activate" field.</summary>
+    public const int FlushQueueOnActivateFieldNumber = 10;
+    private bool flushQueueOnActivate_;
+    /// <summary>
+    /// Flush queue on device activation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool FlushQueueOnActivate {
+      get { return flushQueueOnActivate_; }
+      set {
+        flushQueueOnActivate_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "uplink_interval" field.</summary>
+    public const int UplinkIntervalFieldNumber = 11;
+    private uint uplinkInterval_;
+    /// <summary>
+    /// Uplink interval (seconds).
+    /// This defines the expected uplink interval which the device uses for
+    /// communication. If the uplink interval has expired and no uplink has
+    /// been received, the device is considered inactive.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint UplinkInterval {
+      get { return uplinkInterval_; }
+      set {
+        uplinkInterval_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_status_req_interval" field.</summary>
+    public const int DeviceStatusReqIntervalFieldNumber = 12;
+    private uint deviceStatusReqInterval_;
+    /// <summary>
+    /// Device-status request interval (times / day).
+    /// This defines the times per day that ChirpStack will request the device-status
+    /// from the device.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint DeviceStatusReqInterval {
+      get { return deviceStatusReqInterval_; }
+      set {
+        deviceStatusReqInterval_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_otaa" field.</summary>
+    public const int SupportsOtaaFieldNumber = 13;
+    private bool supportsOtaa_;
+    /// <summary>
+    /// Supports OTAA.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsOtaa {
+      get { return supportsOtaa_; }
+      set {
+        supportsOtaa_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_class_b" field.</summary>
+    public const int SupportsClassBFieldNumber = 14;
+    private bool supportsClassB_;
+    /// <summary>
+    /// Supports Class B.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsClassB {
+      get { return supportsClassB_; }
+      set {
+        supportsClassB_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_class_c" field.</summary>
+    public const int SupportsClassCFieldNumber = 15;
+    private bool supportsClassC_;
+    /// <summary>
+    /// Supports Class-C.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsClassC {
+      get { return supportsClassC_; }
+      set {
+        supportsClassC_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_timeout" field.</summary>
+    public const int ClassBTimeoutFieldNumber = 16;
+    private uint classBTimeout_;
+    /// <summary>
+    /// Class-B timeout (seconds).
+    /// This is the maximum time ChirpStack will wait to receive an acknowledgement from the device (if requested).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBTimeout {
+      get { return classBTimeout_; }
+      set {
+        classBTimeout_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_ping_slot_nb_k" field.</summary>
+    public const int ClassBPingSlotNbKFieldNumber = 17;
+    private uint classBPingSlotNbK_;
+    /// <summary>
+    /// Class-B ping-slots per beacon period.
+    /// Valid options are: 0 - 7.
+    ///
+    /// The actual number of ping-slots per beacon period equals to 2^k.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBPingSlotNbK {
+      get { return classBPingSlotNbK_; }
+      set {
+        classBPingSlotNbK_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_ping_slot_dr" field.</summary>
+    public const int ClassBPingSlotDrFieldNumber = 18;
+    private uint classBPingSlotDr_;
+    /// <summary>
+    /// Class-B ping-slot DR.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBPingSlotDr {
+      get { return classBPingSlotDr_; }
+      set {
+        classBPingSlotDr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_ping_slot_freq" field.</summary>
+    public const int ClassBPingSlotFreqFieldNumber = 19;
+    private uint classBPingSlotFreq_;
+    /// <summary>
+    /// Class-B ping-slot freq (Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBPingSlotFreq {
+      get { return classBPingSlotFreq_; }
+      set {
+        classBPingSlotFreq_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_c_timeout" field.</summary>
+    public const int ClassCTimeoutFieldNumber = 20;
+    private uint classCTimeout_;
+    /// <summary>
+    /// Class-C timeout (seconds).
+    /// This is the maximum time ChirpStack will wait to receive an acknowledgement from the device (if requested).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassCTimeout {
+      get { return classCTimeout_; }
+      set {
+        classCTimeout_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "abp_rx1_delay" field.</summary>
+    public const int AbpRx1DelayFieldNumber = 21;
+    private uint abpRx1Delay_;
+    /// <summary>
+    /// RX1 delay (for ABP).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint AbpRx1Delay {
+      get { return abpRx1Delay_; }
+      set {
+        abpRx1Delay_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "abp_rx1_dr_offset" field.</summary>
+    public const int AbpRx1DrOffsetFieldNumber = 22;
+    private uint abpRx1DrOffset_;
+    /// <summary>
+    /// RX1 DR offset (for ABP).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint AbpRx1DrOffset {
+      get { return abpRx1DrOffset_; }
+      set {
+        abpRx1DrOffset_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "abp_rx2_dr" field.</summary>
+    public const int AbpRx2DrFieldNumber = 23;
+    private uint abpRx2Dr_;
+    /// <summary>
+    /// RX2 DR (for ABP).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint AbpRx2Dr {
+      get { return abpRx2Dr_; }
+      set {
+        abpRx2Dr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "abp_rx2_freq" field.</summary>
+    public const int AbpRx2FreqFieldNumber = 24;
+    private uint abpRx2Freq_;
+    /// <summary>
+    /// RX2 frequency (for ABP, Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint AbpRx2Freq {
+      get { return abpRx2Freq_; }
+      set {
+        abpRx2Freq_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tags" field.</summary>
+    public const int TagsFieldNumber = 25;
+    private static readonly pbc::MapField<string, string>.Codec _map_tags_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 202);
+    private readonly pbc::MapField<string, string> tags_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// User defined tags.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Tags {
+      get { return tags_; }
+    }
+
+    /// <summary>Field number for the "measurements" field.</summary>
+    public const int MeasurementsFieldNumber = 27;
+    private static readonly pbc::MapField<string, global::Chirpstack.Api.Measurement>.Codec _map_measurements_codec
+        = new pbc::MapField<string, global::Chirpstack.Api.Measurement>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.Measurement.Parser), 218);
+    private readonly pbc::MapField<string, global::Chirpstack.Api.Measurement> measurements_ = new pbc::MapField<string, global::Chirpstack.Api.Measurement>();
+    /// <summary>
+    /// Measurements.
+    /// If defined, ChirpStack will visualize these metrics in the web-interface.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, global::Chirpstack.Api.Measurement> Measurements {
+      get { return measurements_; }
+    }
+
+    /// <summary>Field number for the "auto_detect_measurements" field.</summary>
+    public const int AutoDetectMeasurementsFieldNumber = 28;
+    private bool autoDetectMeasurements_;
+    /// <summary>
+    /// Auto-detect measurements.
+    /// If set to true, measurements will be automatically added based on the
+    /// keys of the decoded payload. In cases where the decoded payload contains
+    /// random keys in the data, you want to set this to false.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool AutoDetectMeasurements {
+      get { return autoDetectMeasurements_; }
+      set {
+        autoDetectMeasurements_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "region_config_id" field.</summary>
+    public const int RegionConfigIdFieldNumber = 29;
+    private string regionConfigId_ = "";
+    /// <summary>
+    /// Region configuration ID.
+    /// If set, devices will only use the associated region. If let blank, then
+    /// devices will use all regions matching the selected common-name. Note
+    /// that multiple region configurations can exist for the same common-name,
+    /// e.g. to provide an 8 channel and 16 channel configuration for the US915
+    /// band.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string RegionConfigId {
+      get { return regionConfigId_; }
+      set {
+        regionConfigId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceProfile);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceProfile other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (TenantId != other.TenantId) return false;
+      if (Name != other.Name) return false;
+      if (Description != other.Description) return false;
+      if (Region != other.Region) return false;
+      if (MacVersion != other.MacVersion) return false;
+      if (RegParamsRevision != other.RegParamsRevision) return false;
+      if (AdrAlgorithmId != other.AdrAlgorithmId) return false;
+      if (PayloadCodecRuntime != other.PayloadCodecRuntime) return false;
+      if (PayloadCodecScript != other.PayloadCodecScript) return false;
+      if (FlushQueueOnActivate != other.FlushQueueOnActivate) return false;
+      if (UplinkInterval != other.UplinkInterval) return false;
+      if (DeviceStatusReqInterval != other.DeviceStatusReqInterval) return false;
+      if (SupportsOtaa != other.SupportsOtaa) return false;
+      if (SupportsClassB != other.SupportsClassB) return false;
+      if (SupportsClassC != other.SupportsClassC) return false;
+      if (ClassBTimeout != other.ClassBTimeout) return false;
+      if (ClassBPingSlotNbK != other.ClassBPingSlotNbK) return false;
+      if (ClassBPingSlotDr != other.ClassBPingSlotDr) return false;
+      if (ClassBPingSlotFreq != other.ClassBPingSlotFreq) return false;
+      if (ClassCTimeout != other.ClassCTimeout) return false;
+      if (AbpRx1Delay != other.AbpRx1Delay) return false;
+      if (AbpRx1DrOffset != other.AbpRx1DrOffset) return false;
+      if (AbpRx2Dr != other.AbpRx2Dr) return false;
+      if (AbpRx2Freq != other.AbpRx2Freq) return false;
+      if (!Tags.Equals(other.Tags)) return false;
+      if (!Measurements.Equals(other.Measurements)) return false;
+      if (AutoDetectMeasurements != other.AutoDetectMeasurements) return false;
+      if (RegionConfigId != other.RegionConfigId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (Region != global::Chirpstack.Common.Region.Eu868) hash ^= Region.GetHashCode();
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) hash ^= MacVersion.GetHashCode();
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) hash ^= RegParamsRevision.GetHashCode();
+      if (AdrAlgorithmId.Length != 0) hash ^= AdrAlgorithmId.GetHashCode();
+      if (PayloadCodecRuntime != global::Chirpstack.Api.CodecRuntime.None) hash ^= PayloadCodecRuntime.GetHashCode();
+      if (PayloadCodecScript.Length != 0) hash ^= PayloadCodecScript.GetHashCode();
+      if (FlushQueueOnActivate != false) hash ^= FlushQueueOnActivate.GetHashCode();
+      if (UplinkInterval != 0) hash ^= UplinkInterval.GetHashCode();
+      if (DeviceStatusReqInterval != 0) hash ^= DeviceStatusReqInterval.GetHashCode();
+      if (SupportsOtaa != false) hash ^= SupportsOtaa.GetHashCode();
+      if (SupportsClassB != false) hash ^= SupportsClassB.GetHashCode();
+      if (SupportsClassC != false) hash ^= SupportsClassC.GetHashCode();
+      if (ClassBTimeout != 0) hash ^= ClassBTimeout.GetHashCode();
+      if (ClassBPingSlotNbK != 0) hash ^= ClassBPingSlotNbK.GetHashCode();
+      if (ClassBPingSlotDr != 0) hash ^= ClassBPingSlotDr.GetHashCode();
+      if (ClassBPingSlotFreq != 0) hash ^= ClassBPingSlotFreq.GetHashCode();
+      if (ClassCTimeout != 0) hash ^= ClassCTimeout.GetHashCode();
+      if (AbpRx1Delay != 0) hash ^= AbpRx1Delay.GetHashCode();
+      if (AbpRx1DrOffset != 0) hash ^= AbpRx1DrOffset.GetHashCode();
+      if (AbpRx2Dr != 0) hash ^= AbpRx2Dr.GetHashCode();
+      if (AbpRx2Freq != 0) hash ^= AbpRx2Freq.GetHashCode();
+      hash ^= Tags.GetHashCode();
+      hash ^= Measurements.GetHashCode();
+      if (AutoDetectMeasurements != false) hash ^= AutoDetectMeasurements.GetHashCode();
+      if (RegionConfigId.Length != 0) hash ^= RegionConfigId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(TenantId);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Name);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) RegParamsRevision);
+      }
+      if (AdrAlgorithmId.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(AdrAlgorithmId);
+      }
+      if (PayloadCodecRuntime != global::Chirpstack.Api.CodecRuntime.None) {
+        output.WriteRawTag(64);
+        output.WriteEnum((int) PayloadCodecRuntime);
+      }
+      if (PayloadCodecScript.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(PayloadCodecScript);
+      }
+      if (FlushQueueOnActivate != false) {
+        output.WriteRawTag(80);
+        output.WriteBool(FlushQueueOnActivate);
+      }
+      if (UplinkInterval != 0) {
+        output.WriteRawTag(88);
+        output.WriteUInt32(UplinkInterval);
+      }
+      if (DeviceStatusReqInterval != 0) {
+        output.WriteRawTag(96);
+        output.WriteUInt32(DeviceStatusReqInterval);
+      }
+      if (SupportsOtaa != false) {
+        output.WriteRawTag(104);
+        output.WriteBool(SupportsOtaa);
+      }
+      if (SupportsClassB != false) {
+        output.WriteRawTag(112);
+        output.WriteBool(SupportsClassB);
+      }
+      if (SupportsClassC != false) {
+        output.WriteRawTag(120);
+        output.WriteBool(SupportsClassC);
+      }
+      if (ClassBTimeout != 0) {
+        output.WriteRawTag(128, 1);
+        output.WriteUInt32(ClassBTimeout);
+      }
+      if (ClassBPingSlotNbK != 0) {
+        output.WriteRawTag(136, 1);
+        output.WriteUInt32(ClassBPingSlotNbK);
+      }
+      if (ClassBPingSlotDr != 0) {
+        output.WriteRawTag(144, 1);
+        output.WriteUInt32(ClassBPingSlotDr);
+      }
+      if (ClassBPingSlotFreq != 0) {
+        output.WriteRawTag(152, 1);
+        output.WriteUInt32(ClassBPingSlotFreq);
+      }
+      if (ClassCTimeout != 0) {
+        output.WriteRawTag(160, 1);
+        output.WriteUInt32(ClassCTimeout);
+      }
+      if (AbpRx1Delay != 0) {
+        output.WriteRawTag(168, 1);
+        output.WriteUInt32(AbpRx1Delay);
+      }
+      if (AbpRx1DrOffset != 0) {
+        output.WriteRawTag(176, 1);
+        output.WriteUInt32(AbpRx1DrOffset);
+      }
+      if (AbpRx2Dr != 0) {
+        output.WriteRawTag(184, 1);
+        output.WriteUInt32(AbpRx2Dr);
+      }
+      if (AbpRx2Freq != 0) {
+        output.WriteRawTag(192, 1);
+        output.WriteUInt32(AbpRx2Freq);
+      }
+      tags_.WriteTo(output, _map_tags_codec);
+      if (Description.Length != 0) {
+        output.WriteRawTag(210, 1);
+        output.WriteString(Description);
+      }
+      measurements_.WriteTo(output, _map_measurements_codec);
+      if (AutoDetectMeasurements != false) {
+        output.WriteRawTag(224, 1);
+        output.WriteBool(AutoDetectMeasurements);
+      }
+      if (RegionConfigId.Length != 0) {
+        output.WriteRawTag(234, 1);
+        output.WriteString(RegionConfigId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(TenantId);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Name);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) RegParamsRevision);
+      }
+      if (AdrAlgorithmId.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(AdrAlgorithmId);
+      }
+      if (PayloadCodecRuntime != global::Chirpstack.Api.CodecRuntime.None) {
+        output.WriteRawTag(64);
+        output.WriteEnum((int) PayloadCodecRuntime);
+      }
+      if (PayloadCodecScript.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(PayloadCodecScript);
+      }
+      if (FlushQueueOnActivate != false) {
+        output.WriteRawTag(80);
+        output.WriteBool(FlushQueueOnActivate);
+      }
+      if (UplinkInterval != 0) {
+        output.WriteRawTag(88);
+        output.WriteUInt32(UplinkInterval);
+      }
+      if (DeviceStatusReqInterval != 0) {
+        output.WriteRawTag(96);
+        output.WriteUInt32(DeviceStatusReqInterval);
+      }
+      if (SupportsOtaa != false) {
+        output.WriteRawTag(104);
+        output.WriteBool(SupportsOtaa);
+      }
+      if (SupportsClassB != false) {
+        output.WriteRawTag(112);
+        output.WriteBool(SupportsClassB);
+      }
+      if (SupportsClassC != false) {
+        output.WriteRawTag(120);
+        output.WriteBool(SupportsClassC);
+      }
+      if (ClassBTimeout != 0) {
+        output.WriteRawTag(128, 1);
+        output.WriteUInt32(ClassBTimeout);
+      }
+      if (ClassBPingSlotNbK != 0) {
+        output.WriteRawTag(136, 1);
+        output.WriteUInt32(ClassBPingSlotNbK);
+      }
+      if (ClassBPingSlotDr != 0) {
+        output.WriteRawTag(144, 1);
+        output.WriteUInt32(ClassBPingSlotDr);
+      }
+      if (ClassBPingSlotFreq != 0) {
+        output.WriteRawTag(152, 1);
+        output.WriteUInt32(ClassBPingSlotFreq);
+      }
+      if (ClassCTimeout != 0) {
+        output.WriteRawTag(160, 1);
+        output.WriteUInt32(ClassCTimeout);
+      }
+      if (AbpRx1Delay != 0) {
+        output.WriteRawTag(168, 1);
+        output.WriteUInt32(AbpRx1Delay);
+      }
+      if (AbpRx1DrOffset != 0) {
+        output.WriteRawTag(176, 1);
+        output.WriteUInt32(AbpRx1DrOffset);
+      }
+      if (AbpRx2Dr != 0) {
+        output.WriteRawTag(184, 1);
+        output.WriteUInt32(AbpRx2Dr);
+      }
+      if (AbpRx2Freq != 0) {
+        output.WriteRawTag(192, 1);
+        output.WriteUInt32(AbpRx2Freq);
+      }
+      tags_.WriteTo(ref output, _map_tags_codec);
+      if (Description.Length != 0) {
+        output.WriteRawTag(210, 1);
+        output.WriteString(Description);
+      }
+      measurements_.WriteTo(ref output, _map_measurements_codec);
+      if (AutoDetectMeasurements != false) {
+        output.WriteRawTag(224, 1);
+        output.WriteBool(AutoDetectMeasurements);
+      }
+      if (RegionConfigId.Length != 0) {
+        output.WriteRawTag(234, 1);
+        output.WriteString(RegionConfigId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Description.Length != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) RegParamsRevision);
+      }
+      if (AdrAlgorithmId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(AdrAlgorithmId);
+      }
+      if (PayloadCodecRuntime != global::Chirpstack.Api.CodecRuntime.None) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) PayloadCodecRuntime);
+      }
+      if (PayloadCodecScript.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(PayloadCodecScript);
+      }
+      if (FlushQueueOnActivate != false) {
+        size += 1 + 1;
+      }
+      if (UplinkInterval != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(UplinkInterval);
+      }
+      if (DeviceStatusReqInterval != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(DeviceStatusReqInterval);
+      }
+      if (SupportsOtaa != false) {
+        size += 1 + 1;
+      }
+      if (SupportsClassB != false) {
+        size += 1 + 1;
+      }
+      if (SupportsClassC != false) {
+        size += 1 + 1;
+      }
+      if (ClassBTimeout != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(ClassBTimeout);
+      }
+      if (ClassBPingSlotNbK != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(ClassBPingSlotNbK);
+      }
+      if (ClassBPingSlotDr != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(ClassBPingSlotDr);
+      }
+      if (ClassBPingSlotFreq != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(ClassBPingSlotFreq);
+      }
+      if (ClassCTimeout != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(ClassCTimeout);
+      }
+      if (AbpRx1Delay != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(AbpRx1Delay);
+      }
+      if (AbpRx1DrOffset != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(AbpRx1DrOffset);
+      }
+      if (AbpRx2Dr != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(AbpRx2Dr);
+      }
+      if (AbpRx2Freq != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(AbpRx2Freq);
+      }
+      size += tags_.CalculateSize(_map_tags_codec);
+      size += measurements_.CalculateSize(_map_measurements_codec);
+      if (AutoDetectMeasurements != false) {
+        size += 2 + 1;
+      }
+      if (RegionConfigId.Length != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeStringSize(RegionConfigId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceProfile other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      if (other.Region != global::Chirpstack.Common.Region.Eu868) {
+        Region = other.Region;
+      }
+      if (other.MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        MacVersion = other.MacVersion;
+      }
+      if (other.RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        RegParamsRevision = other.RegParamsRevision;
+      }
+      if (other.AdrAlgorithmId.Length != 0) {
+        AdrAlgorithmId = other.AdrAlgorithmId;
+      }
+      if (other.PayloadCodecRuntime != global::Chirpstack.Api.CodecRuntime.None) {
+        PayloadCodecRuntime = other.PayloadCodecRuntime;
+      }
+      if (other.PayloadCodecScript.Length != 0) {
+        PayloadCodecScript = other.PayloadCodecScript;
+      }
+      if (other.FlushQueueOnActivate != false) {
+        FlushQueueOnActivate = other.FlushQueueOnActivate;
+      }
+      if (other.UplinkInterval != 0) {
+        UplinkInterval = other.UplinkInterval;
+      }
+      if (other.DeviceStatusReqInterval != 0) {
+        DeviceStatusReqInterval = other.DeviceStatusReqInterval;
+      }
+      if (other.SupportsOtaa != false) {
+        SupportsOtaa = other.SupportsOtaa;
+      }
+      if (other.SupportsClassB != false) {
+        SupportsClassB = other.SupportsClassB;
+      }
+      if (other.SupportsClassC != false) {
+        SupportsClassC = other.SupportsClassC;
+      }
+      if (other.ClassBTimeout != 0) {
+        ClassBTimeout = other.ClassBTimeout;
+      }
+      if (other.ClassBPingSlotNbK != 0) {
+        ClassBPingSlotNbK = other.ClassBPingSlotNbK;
+      }
+      if (other.ClassBPingSlotDr != 0) {
+        ClassBPingSlotDr = other.ClassBPingSlotDr;
+      }
+      if (other.ClassBPingSlotFreq != 0) {
+        ClassBPingSlotFreq = other.ClassBPingSlotFreq;
+      }
+      if (other.ClassCTimeout != 0) {
+        ClassCTimeout = other.ClassCTimeout;
+      }
+      if (other.AbpRx1Delay != 0) {
+        AbpRx1Delay = other.AbpRx1Delay;
+      }
+      if (other.AbpRx1DrOffset != 0) {
+        AbpRx1DrOffset = other.AbpRx1DrOffset;
+      }
+      if (other.AbpRx2Dr != 0) {
+        AbpRx2Dr = other.AbpRx2Dr;
+      }
+      if (other.AbpRx2Freq != 0) {
+        AbpRx2Freq = other.AbpRx2Freq;
+      }
+      tags_.Add(other.tags_);
+      measurements_.Add(other.measurements_);
+      if (other.AutoDetectMeasurements != false) {
+        AutoDetectMeasurements = other.AutoDetectMeasurements;
+      }
+      if (other.RegionConfigId.Length != 0) {
+        RegionConfigId = other.RegionConfigId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 26: {
+            Name = input.ReadString();
+            break;
+          }
+          case 32: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 40: {
+            MacVersion = (global::Chirpstack.Common.MacVersion) input.ReadEnum();
+            break;
+          }
+          case 48: {
+            RegParamsRevision = (global::Chirpstack.Common.RegParamsRevision) input.ReadEnum();
+            break;
+          }
+          case 58: {
+            AdrAlgorithmId = input.ReadString();
+            break;
+          }
+          case 64: {
+            PayloadCodecRuntime = (global::Chirpstack.Api.CodecRuntime) input.ReadEnum();
+            break;
+          }
+          case 74: {
+            PayloadCodecScript = input.ReadString();
+            break;
+          }
+          case 80: {
+            FlushQueueOnActivate = input.ReadBool();
+            break;
+          }
+          case 88: {
+            UplinkInterval = input.ReadUInt32();
+            break;
+          }
+          case 96: {
+            DeviceStatusReqInterval = input.ReadUInt32();
+            break;
+          }
+          case 104: {
+            SupportsOtaa = input.ReadBool();
+            break;
+          }
+          case 112: {
+            SupportsClassB = input.ReadBool();
+            break;
+          }
+          case 120: {
+            SupportsClassC = input.ReadBool();
+            break;
+          }
+          case 128: {
+            ClassBTimeout = input.ReadUInt32();
+            break;
+          }
+          case 136: {
+            ClassBPingSlotNbK = input.ReadUInt32();
+            break;
+          }
+          case 144: {
+            ClassBPingSlotDr = input.ReadUInt32();
+            break;
+          }
+          case 152: {
+            ClassBPingSlotFreq = input.ReadUInt32();
+            break;
+          }
+          case 160: {
+            ClassCTimeout = input.ReadUInt32();
+            break;
+          }
+          case 168: {
+            AbpRx1Delay = input.ReadUInt32();
+            break;
+          }
+          case 176: {
+            AbpRx1DrOffset = input.ReadUInt32();
+            break;
+          }
+          case 184: {
+            AbpRx2Dr = input.ReadUInt32();
+            break;
+          }
+          case 192: {
+            AbpRx2Freq = input.ReadUInt32();
+            break;
+          }
+          case 202: {
+            tags_.AddEntriesFrom(input, _map_tags_codec);
+            break;
+          }
+          case 210: {
+            Description = input.ReadString();
+            break;
+          }
+          case 218: {
+            measurements_.AddEntriesFrom(input, _map_measurements_codec);
+            break;
+          }
+          case 224: {
+            AutoDetectMeasurements = input.ReadBool();
+            break;
+          }
+          case 234: {
+            RegionConfigId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 26: {
+            Name = input.ReadString();
+            break;
+          }
+          case 32: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 40: {
+            MacVersion = (global::Chirpstack.Common.MacVersion) input.ReadEnum();
+            break;
+          }
+          case 48: {
+            RegParamsRevision = (global::Chirpstack.Common.RegParamsRevision) input.ReadEnum();
+            break;
+          }
+          case 58: {
+            AdrAlgorithmId = input.ReadString();
+            break;
+          }
+          case 64: {
+            PayloadCodecRuntime = (global::Chirpstack.Api.CodecRuntime) input.ReadEnum();
+            break;
+          }
+          case 74: {
+            PayloadCodecScript = input.ReadString();
+            break;
+          }
+          case 80: {
+            FlushQueueOnActivate = input.ReadBool();
+            break;
+          }
+          case 88: {
+            UplinkInterval = input.ReadUInt32();
+            break;
+          }
+          case 96: {
+            DeviceStatusReqInterval = input.ReadUInt32();
+            break;
+          }
+          case 104: {
+            SupportsOtaa = input.ReadBool();
+            break;
+          }
+          case 112: {
+            SupportsClassB = input.ReadBool();
+            break;
+          }
+          case 120: {
+            SupportsClassC = input.ReadBool();
+            break;
+          }
+          case 128: {
+            ClassBTimeout = input.ReadUInt32();
+            break;
+          }
+          case 136: {
+            ClassBPingSlotNbK = input.ReadUInt32();
+            break;
+          }
+          case 144: {
+            ClassBPingSlotDr = input.ReadUInt32();
+            break;
+          }
+          case 152: {
+            ClassBPingSlotFreq = input.ReadUInt32();
+            break;
+          }
+          case 160: {
+            ClassCTimeout = input.ReadUInt32();
+            break;
+          }
+          case 168: {
+            AbpRx1Delay = input.ReadUInt32();
+            break;
+          }
+          case 176: {
+            AbpRx1DrOffset = input.ReadUInt32();
+            break;
+          }
+          case 184: {
+            AbpRx2Dr = input.ReadUInt32();
+            break;
+          }
+          case 192: {
+            AbpRx2Freq = input.ReadUInt32();
+            break;
+          }
+          case 202: {
+            tags_.AddEntriesFrom(ref input, _map_tags_codec);
+            break;
+          }
+          case 210: {
+            Description = input.ReadString();
+            break;
+          }
+          case 218: {
+            measurements_.AddEntriesFrom(ref input, _map_measurements_codec);
+            break;
+          }
+          case 224: {
+            AutoDetectMeasurements = input.ReadBool();
+            break;
+          }
+          case 234: {
+            RegionConfigId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class Measurement : pb::IMessage<Measurement>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<Measurement> _parser = new pb::MessageParser<Measurement>(() => new Measurement());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<Measurement> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Measurement() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Measurement(Measurement other) : this() {
+      name_ = other.name_;
+      kind_ = other.kind_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Measurement Clone() {
+      return new Measurement(this);
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 2;
+    private string name_ = "";
+    /// <summary>
+    /// Name (user defined).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "kind" field.</summary>
+    public const int KindFieldNumber = 3;
+    private global::Chirpstack.Api.MeasurementKind kind_ = global::Chirpstack.Api.MeasurementKind.Unknown;
+    /// <summary>
+    /// Kind.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MeasurementKind Kind {
+      get { return kind_; }
+      set {
+        kind_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as Measurement);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(Measurement other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Name != other.Name) return false;
+      if (Kind != other.Kind) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Kind != global::Chirpstack.Api.MeasurementKind.Unknown) hash ^= Kind.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Kind != global::Chirpstack.Api.MeasurementKind.Unknown) {
+        output.WriteRawTag(24);
+        output.WriteEnum((int) Kind);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Kind != global::Chirpstack.Api.MeasurementKind.Unknown) {
+        output.WriteRawTag(24);
+        output.WriteEnum((int) Kind);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Kind != global::Chirpstack.Api.MeasurementKind.Unknown) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Kind);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(Measurement other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Kind != global::Chirpstack.Api.MeasurementKind.Unknown) {
+        Kind = other.Kind;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 24: {
+            Kind = (global::Chirpstack.Api.MeasurementKind) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 24: {
+            Kind = (global::Chirpstack.Api.MeasurementKind) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeviceProfileListItem : pb::IMessage<DeviceProfileListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceProfileListItem> _parser = new pb::MessageParser<DeviceProfileListItem>(() => new DeviceProfileListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceProfileListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfileListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfileListItem(DeviceProfileListItem other) : this() {
+      id_ = other.id_;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      name_ = other.name_;
+      region_ = other.region_;
+      macVersion_ = other.macVersion_;
+      regParamsRevision_ = other.regParamsRevision_;
+      supportsOtaa_ = other.supportsOtaa_;
+      supportsClassB_ = other.supportsClassB_;
+      supportsClassC_ = other.supportsClassC_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfileListItem Clone() {
+      return new DeviceProfileListItem(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Device-profile ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 4;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "region" field.</summary>
+    public const int RegionFieldNumber = 5;
+    private global::Chirpstack.Common.Region region_ = global::Chirpstack.Common.Region.Eu868;
+    /// <summary>
+    /// Region.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Region Region {
+      get { return region_; }
+      set {
+        region_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "mac_version" field.</summary>
+    public const int MacVersionFieldNumber = 6;
+    private global::Chirpstack.Common.MacVersion macVersion_ = global::Chirpstack.Common.MacVersion.Lorawan100;
+    /// <summary>
+    /// LoRaWAN mac-version.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.MacVersion MacVersion {
+      get { return macVersion_; }
+      set {
+        macVersion_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "reg_params_revision" field.</summary>
+    public const int RegParamsRevisionFieldNumber = 7;
+    private global::Chirpstack.Common.RegParamsRevision regParamsRevision_ = global::Chirpstack.Common.RegParamsRevision.A;
+    /// <summary>
+    /// Regional parameters revision.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.RegParamsRevision RegParamsRevision {
+      get { return regParamsRevision_; }
+      set {
+        regParamsRevision_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_otaa" field.</summary>
+    public const int SupportsOtaaFieldNumber = 8;
+    private bool supportsOtaa_;
+    /// <summary>
+    /// Supports OTAA.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsOtaa {
+      get { return supportsOtaa_; }
+      set {
+        supportsOtaa_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_class_b" field.</summary>
+    public const int SupportsClassBFieldNumber = 9;
+    private bool supportsClassB_;
+    /// <summary>
+    /// Supports Class-B.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsClassB {
+      get { return supportsClassB_; }
+      set {
+        supportsClassB_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_class_c" field.</summary>
+    public const int SupportsClassCFieldNumber = 10;
+    private bool supportsClassC_;
+    /// <summary>
+    /// Supports Class-C.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsClassC {
+      get { return supportsClassC_; }
+      set {
+        supportsClassC_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceProfileListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceProfileListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (Name != other.Name) return false;
+      if (Region != other.Region) return false;
+      if (MacVersion != other.MacVersion) return false;
+      if (RegParamsRevision != other.RegParamsRevision) return false;
+      if (SupportsOtaa != other.SupportsOtaa) return false;
+      if (SupportsClassB != other.SupportsClassB) return false;
+      if (SupportsClassC != other.SupportsClassC) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Region != global::Chirpstack.Common.Region.Eu868) hash ^= Region.GetHashCode();
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) hash ^= MacVersion.GetHashCode();
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) hash ^= RegParamsRevision.GetHashCode();
+      if (SupportsOtaa != false) hash ^= SupportsOtaa.GetHashCode();
+      if (SupportsClassB != false) hash ^= SupportsClassB.GetHashCode();
+      if (SupportsClassC != false) hash ^= SupportsClassC.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) RegParamsRevision);
+      }
+      if (SupportsOtaa != false) {
+        output.WriteRawTag(64);
+        output.WriteBool(SupportsOtaa);
+      }
+      if (SupportsClassB != false) {
+        output.WriteRawTag(72);
+        output.WriteBool(SupportsClassB);
+      }
+      if (SupportsClassC != false) {
+        output.WriteRawTag(80);
+        output.WriteBool(SupportsClassC);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) RegParamsRevision);
+      }
+      if (SupportsOtaa != false) {
+        output.WriteRawTag(64);
+        output.WriteBool(SupportsOtaa);
+      }
+      if (SupportsClassB != false) {
+        output.WriteRawTag(72);
+        output.WriteBool(SupportsClassB);
+      }
+      if (SupportsClassC != false) {
+        output.WriteRawTag(80);
+        output.WriteBool(SupportsClassC);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) RegParamsRevision);
+      }
+      if (SupportsOtaa != false) {
+        size += 1 + 1;
+      }
+      if (SupportsClassB != false) {
+        size += 1 + 1;
+      }
+      if (SupportsClassC != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceProfileListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Region != global::Chirpstack.Common.Region.Eu868) {
+        Region = other.Region;
+      }
+      if (other.MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        MacVersion = other.MacVersion;
+      }
+      if (other.RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        RegParamsRevision = other.RegParamsRevision;
+      }
+      if (other.SupportsOtaa != false) {
+        SupportsOtaa = other.SupportsOtaa;
+      }
+      if (other.SupportsClassB != false) {
+        SupportsClassB = other.SupportsClassB;
+      }
+      if (other.SupportsClassC != false) {
+        SupportsClassC = other.SupportsClassC;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Name = input.ReadString();
+            break;
+          }
+          case 40: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 48: {
+            MacVersion = (global::Chirpstack.Common.MacVersion) input.ReadEnum();
+            break;
+          }
+          case 56: {
+            RegParamsRevision = (global::Chirpstack.Common.RegParamsRevision) input.ReadEnum();
+            break;
+          }
+          case 64: {
+            SupportsOtaa = input.ReadBool();
+            break;
+          }
+          case 72: {
+            SupportsClassB = input.ReadBool();
+            break;
+          }
+          case 80: {
+            SupportsClassC = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Name = input.ReadString();
+            break;
+          }
+          case 40: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 48: {
+            MacVersion = (global::Chirpstack.Common.MacVersion) input.ReadEnum();
+            break;
+          }
+          case 56: {
+            RegParamsRevision = (global::Chirpstack.Common.RegParamsRevision) input.ReadEnum();
+            break;
+          }
+          case 64: {
+            SupportsOtaa = input.ReadBool();
+            break;
+          }
+          case 72: {
+            SupportsClassB = input.ReadBool();
+            break;
+          }
+          case 80: {
+            SupportsClassC = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateDeviceProfileRequest : pb::IMessage<CreateDeviceProfileRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateDeviceProfileRequest> _parser = new pb::MessageParser<CreateDeviceProfileRequest>(() => new CreateDeviceProfileRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateDeviceProfileRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceProfileRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceProfileRequest(CreateDeviceProfileRequest other) : this() {
+      deviceProfile_ = other.deviceProfile_ != null ? other.deviceProfile_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceProfileRequest Clone() {
+      return new CreateDeviceProfileRequest(this);
+    }
+
+    /// <summary>Field number for the "device_profile" field.</summary>
+    public const int DeviceProfileFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceProfile deviceProfile_;
+    /// <summary>
+    /// Object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceProfile DeviceProfile {
+      get { return deviceProfile_; }
+      set {
+        deviceProfile_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateDeviceProfileRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateDeviceProfileRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceProfile, other.DeviceProfile)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceProfile_ != null) hash ^= DeviceProfile.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceProfile_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfile);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceProfile_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfile);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceProfile_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceProfile);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateDeviceProfileRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceProfile_ != null) {
+        if (deviceProfile_ == null) {
+          DeviceProfile = new global::Chirpstack.Api.DeviceProfile();
+        }
+        DeviceProfile.MergeFrom(other.DeviceProfile);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceProfile_ == null) {
+              DeviceProfile = new global::Chirpstack.Api.DeviceProfile();
+            }
+            input.ReadMessage(DeviceProfile);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceProfile_ == null) {
+              DeviceProfile = new global::Chirpstack.Api.DeviceProfile();
+            }
+            input.ReadMessage(DeviceProfile);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateDeviceProfileResponse : pb::IMessage<CreateDeviceProfileResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateDeviceProfileResponse> _parser = new pb::MessageParser<CreateDeviceProfileResponse>(() => new CreateDeviceProfileResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateDeviceProfileResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceProfileResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceProfileResponse(CreateDeviceProfileResponse other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceProfileResponse Clone() {
+      return new CreateDeviceProfileResponse(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateDeviceProfileResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateDeviceProfileResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateDeviceProfileResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceProfileRequest : pb::IMessage<GetDeviceProfileRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceProfileRequest> _parser = new pb::MessageParser<GetDeviceProfileRequest>(() => new GetDeviceProfileRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceProfileRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileRequest(GetDeviceProfileRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileRequest Clone() {
+      return new GetDeviceProfileRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceProfileRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceProfileRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceProfileRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceProfileResponse : pb::IMessage<GetDeviceProfileResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceProfileResponse> _parser = new pb::MessageParser<GetDeviceProfileResponse>(() => new GetDeviceProfileResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceProfileResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileResponse(GetDeviceProfileResponse other) : this() {
+      deviceProfile_ = other.deviceProfile_ != null ? other.deviceProfile_.Clone() : null;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileResponse Clone() {
+      return new GetDeviceProfileResponse(this);
+    }
+
+    /// <summary>Field number for the "device_profile" field.</summary>
+    public const int DeviceProfileFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceProfile deviceProfile_;
+    /// <summary>
+    /// Device-profile object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceProfile DeviceProfile {
+      get { return deviceProfile_; }
+      set {
+        deviceProfile_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceProfileResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceProfileResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceProfile, other.DeviceProfile)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceProfile_ != null) hash ^= DeviceProfile.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceProfile_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfile);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceProfile_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfile);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceProfile_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceProfile);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceProfileResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceProfile_ != null) {
+        if (deviceProfile_ == null) {
+          DeviceProfile = new global::Chirpstack.Api.DeviceProfile();
+        }
+        DeviceProfile.MergeFrom(other.DeviceProfile);
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceProfile_ == null) {
+              DeviceProfile = new global::Chirpstack.Api.DeviceProfile();
+            }
+            input.ReadMessage(DeviceProfile);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceProfile_ == null) {
+              DeviceProfile = new global::Chirpstack.Api.DeviceProfile();
+            }
+            input.ReadMessage(DeviceProfile);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateDeviceProfileRequest : pb::IMessage<UpdateDeviceProfileRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateDeviceProfileRequest> _parser = new pb::MessageParser<UpdateDeviceProfileRequest>(() => new UpdateDeviceProfileRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateDeviceProfileRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceProfileRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceProfileRequest(UpdateDeviceProfileRequest other) : this() {
+      deviceProfile_ = other.deviceProfile_ != null ? other.deviceProfile_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceProfileRequest Clone() {
+      return new UpdateDeviceProfileRequest(this);
+    }
+
+    /// <summary>Field number for the "device_profile" field.</summary>
+    public const int DeviceProfileFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceProfile deviceProfile_;
+    /// <summary>
+    /// Device-profile object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceProfile DeviceProfile {
+      get { return deviceProfile_; }
+      set {
+        deviceProfile_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateDeviceProfileRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateDeviceProfileRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceProfile, other.DeviceProfile)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceProfile_ != null) hash ^= DeviceProfile.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceProfile_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfile);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceProfile_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfile);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceProfile_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceProfile);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateDeviceProfileRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceProfile_ != null) {
+        if (deviceProfile_ == null) {
+          DeviceProfile = new global::Chirpstack.Api.DeviceProfile();
+        }
+        DeviceProfile.MergeFrom(other.DeviceProfile);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceProfile_ == null) {
+              DeviceProfile = new global::Chirpstack.Api.DeviceProfile();
+            }
+            input.ReadMessage(DeviceProfile);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceProfile_ == null) {
+              DeviceProfile = new global::Chirpstack.Api.DeviceProfile();
+            }
+            input.ReadMessage(DeviceProfile);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteDeviceProfileRequest : pb::IMessage<DeleteDeviceProfileRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteDeviceProfileRequest> _parser = new pb::MessageParser<DeleteDeviceProfileRequest>(() => new DeleteDeviceProfileRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteDeviceProfileRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceProfileRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceProfileRequest(DeleteDeviceProfileRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceProfileRequest Clone() {
+      return new DeleteDeviceProfileRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteDeviceProfileRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteDeviceProfileRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteDeviceProfileRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListDeviceProfilesRequest : pb::IMessage<ListDeviceProfilesRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListDeviceProfilesRequest> _parser = new pb::MessageParser<ListDeviceProfilesRequest>(() => new ListDeviceProfilesRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListDeviceProfilesRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfilesRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfilesRequest(ListDeviceProfilesRequest other) : this() {
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      search_ = other.search_;
+      tenantId_ = other.tenantId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfilesRequest Clone() {
+      return new ListDeviceProfilesRequest(this);
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 1;
+    private uint limit_;
+    /// <summary>
+    /// Max number of device-profiles to return in the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 2;
+    private uint offset_;
+    /// <summary>
+    /// Offset in the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "search" field.</summary>
+    public const int SearchFieldNumber = 3;
+    private string search_ = "";
+    /// <summary>
+    /// If set, the given string will be used to search on name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Search {
+      get { return search_; }
+      set {
+        search_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 4;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID to list the device-profiles for.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListDeviceProfilesRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListDeviceProfilesRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      if (Search != other.Search) return false;
+      if (TenantId != other.TenantId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Limit != 0) hash ^= Limit.GetHashCode();
+      if (Offset != 0) hash ^= Offset.GetHashCode();
+      if (Search.Length != 0) hash ^= Search.GetHashCode();
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Limit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Limit);
+      }
+      if (Offset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Offset);
+      }
+      if (Search.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Search);
+      }
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListDeviceProfilesRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Limit != 0) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0) {
+        Offset = other.Offset;
+      }
+      if (other.Search.Length != 0) {
+        Search = other.Search;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListDeviceProfilesResponse : pb::IMessage<ListDeviceProfilesResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListDeviceProfilesResponse> _parser = new pb::MessageParser<ListDeviceProfilesResponse>(() => new ListDeviceProfilesResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListDeviceProfilesResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[10]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfilesResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfilesResponse(ListDeviceProfilesResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfilesResponse Clone() {
+      return new ListDeviceProfilesResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of device-profiles.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.DeviceProfileListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.DeviceProfileListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.DeviceProfileListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.DeviceProfileListItem>();
+    /// <summary>
+    /// Result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.DeviceProfileListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListDeviceProfilesResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListDeviceProfilesResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListDeviceProfilesResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListDeviceProfileAdrAlgorithmsResponse : pb::IMessage<ListDeviceProfileAdrAlgorithmsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListDeviceProfileAdrAlgorithmsResponse> _parser = new pb::MessageParser<ListDeviceProfileAdrAlgorithmsResponse>(() => new ListDeviceProfileAdrAlgorithmsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListDeviceProfileAdrAlgorithmsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[11]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfileAdrAlgorithmsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfileAdrAlgorithmsResponse(ListDeviceProfileAdrAlgorithmsResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfileAdrAlgorithmsResponse Clone() {
+      return new ListDeviceProfileAdrAlgorithmsResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of algorithms.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.AdrAlgorithmListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.AdrAlgorithmListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.AdrAlgorithmListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.AdrAlgorithmListItem>();
+    /// <summary>
+    /// Result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.AdrAlgorithmListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListDeviceProfileAdrAlgorithmsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListDeviceProfileAdrAlgorithmsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListDeviceProfileAdrAlgorithmsResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class AdrAlgorithmListItem : pb::IMessage<AdrAlgorithmListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<AdrAlgorithmListItem> _parser = new pb::MessageParser<AdrAlgorithmListItem>(() => new AdrAlgorithmListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<AdrAlgorithmListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.MessageTypes[12]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AdrAlgorithmListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AdrAlgorithmListItem(AdrAlgorithmListItem other) : this() {
+      id_ = other.id_;
+      name_ = other.name_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AdrAlgorithmListItem Clone() {
+      return new AdrAlgorithmListItem(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Algorithm ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 2;
+    private string name_ = "";
+    /// <summary>
+    /// Algorithm name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as AdrAlgorithmListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(AdrAlgorithmListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (Name != other.Name) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(AdrAlgorithmListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/DeviceProfileGrpc.cs b/api/csharp/Chirpstack/api/DeviceProfileGrpc.cs
new file mode 100644
index 00000000..986dc021
--- /dev/null
+++ b/api/csharp/Chirpstack/api/DeviceProfileGrpc.cs
@@ -0,0 +1,558 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/device_profile.proto
+// </auto-generated>
+#pragma warning disable 0414, 1591, 8981
+#region Designer generated code
+
+using grpc = global::Grpc.Core;
+
+namespace Chirpstack.Api {
+  /// <summary>
+  /// DeviceProfileService is the service providing API methods for managing device-profiles.
+  /// </summary>
+  public static partial class DeviceProfileService
+  {
+    static readonly string __ServiceName = "api.DeviceProfileService";
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (message is global::Google.Protobuf.IBufferMessage)
+      {
+        context.SetPayloadLength(message.CalculateSize());
+        global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
+        context.Complete();
+        return;
+      }
+      #endif
+      context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static class __Helper_MessageCache<T>
+    {
+      public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (__Helper_MessageCache<T>.IsBufferMessage)
+      {
+        return parser.ParseFrom(context.PayloadAsReadOnlySequence());
+      }
+      #endif
+      return parser.ParseFrom(context.PayloadAsNewBuffer());
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateDeviceProfileRequest> __Marshaller_api_CreateDeviceProfileRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateDeviceProfileRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateDeviceProfileResponse> __Marshaller_api_CreateDeviceProfileResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateDeviceProfileResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceProfileRequest> __Marshaller_api_GetDeviceProfileRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceProfileRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceProfileResponse> __Marshaller_api_GetDeviceProfileResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceProfileResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateDeviceProfileRequest> __Marshaller_api_UpdateDeviceProfileRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateDeviceProfileRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Google.Protobuf.WellKnownTypes.Empty> __Marshaller_google_protobuf_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Google.Protobuf.WellKnownTypes.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteDeviceProfileRequest> __Marshaller_api_DeleteDeviceProfileRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteDeviceProfileRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListDeviceProfilesRequest> __Marshaller_api_ListDeviceProfilesRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListDeviceProfilesRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListDeviceProfilesResponse> __Marshaller_api_ListDeviceProfilesResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListDeviceProfilesResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse> __Marshaller_api_ListDeviceProfileAdrAlgorithmsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse.Parser));
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateDeviceProfileRequest, global::Chirpstack.Api.CreateDeviceProfileResponse> __Method_Create = new grpc::Method<global::Chirpstack.Api.CreateDeviceProfileRequest, global::Chirpstack.Api.CreateDeviceProfileResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Create",
+        __Marshaller_api_CreateDeviceProfileRequest,
+        __Marshaller_api_CreateDeviceProfileResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetDeviceProfileRequest, global::Chirpstack.Api.GetDeviceProfileResponse> __Method_Get = new grpc::Method<global::Chirpstack.Api.GetDeviceProfileRequest, global::Chirpstack.Api.GetDeviceProfileResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Get",
+        __Marshaller_api_GetDeviceProfileRequest,
+        __Marshaller_api_GetDeviceProfileResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateDeviceProfileRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Update = new grpc::Method<global::Chirpstack.Api.UpdateDeviceProfileRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Update",
+        __Marshaller_api_UpdateDeviceProfileRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteDeviceProfileRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Delete = new grpc::Method<global::Chirpstack.Api.DeleteDeviceProfileRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Delete",
+        __Marshaller_api_DeleteDeviceProfileRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListDeviceProfilesRequest, global::Chirpstack.Api.ListDeviceProfilesResponse> __Method_List = new grpc::Method<global::Chirpstack.Api.ListDeviceProfilesRequest, global::Chirpstack.Api.ListDeviceProfilesResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "List",
+        __Marshaller_api_ListDeviceProfilesRequest,
+        __Marshaller_api_ListDeviceProfilesResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse> __Method_ListAdrAlgorithms = new grpc::Method<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "ListAdrAlgorithms",
+        __Marshaller_google_protobuf_Empty,
+        __Marshaller_api_ListDeviceProfileAdrAlgorithmsResponse);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Chirpstack.Api.DeviceProfileReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of DeviceProfileService</summary>
+    [grpc::BindServiceMethod(typeof(DeviceProfileService), "BindService")]
+    public abstract partial class DeviceProfileServiceBase
+    {
+      /// <summary>
+      /// Create the given device-profile.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.CreateDeviceProfileResponse> Create(global::Chirpstack.Api.CreateDeviceProfileRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the device-profile for the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetDeviceProfileResponse> Get(global::Chirpstack.Api.GetDeviceProfileRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update the given device-profile.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Update(global::Chirpstack.Api.UpdateDeviceProfileRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete the device-profile with the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Delete(global::Chirpstack.Api.DeleteDeviceProfileRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// List the available device-profiles.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListDeviceProfilesResponse> List(global::Chirpstack.Api.ListDeviceProfilesRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// List available ADR algorithms.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse> ListAdrAlgorithms(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for DeviceProfileService</summary>
+    public partial class DeviceProfileServiceClient : grpc::ClientBase<DeviceProfileServiceClient>
+    {
+      /// <summary>Creates a new client for DeviceProfileService</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public DeviceProfileServiceClient(grpc::ChannelBase channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for DeviceProfileService that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public DeviceProfileServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected DeviceProfileServiceClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected DeviceProfileServiceClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      /// Create the given device-profile.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateDeviceProfileResponse Create(global::Chirpstack.Api.CreateDeviceProfileRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Create(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create the given device-profile.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateDeviceProfileResponse Create(global::Chirpstack.Api.CreateDeviceProfileRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Create the given device-profile.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateDeviceProfileResponse> CreateAsync(global::Chirpstack.Api.CreateDeviceProfileRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create the given device-profile.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateDeviceProfileResponse> CreateAsync(global::Chirpstack.Api.CreateDeviceProfileRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Get the device-profile for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceProfileResponse Get(global::Chirpstack.Api.GetDeviceProfileRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Get(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the device-profile for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceProfileResponse Get(global::Chirpstack.Api.GetDeviceProfileRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Get the device-profile for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceProfileResponse> GetAsync(global::Chirpstack.Api.GetDeviceProfileRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the device-profile for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceProfileResponse> GetAsync(global::Chirpstack.Api.GetDeviceProfileRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Update the given device-profile.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateDeviceProfileRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Update(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given device-profile.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateDeviceProfileRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Update the given device-profile.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateDeviceProfileRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given device-profile.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateDeviceProfileRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Delete the device-profile with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteDeviceProfileRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Delete(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the device-profile with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteDeviceProfileRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Delete the device-profile with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteDeviceProfileRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the device-profile with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteDeviceProfileRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// List the available device-profiles.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListDeviceProfilesResponse List(global::Chirpstack.Api.ListDeviceProfilesRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return List(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List the available device-profiles.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListDeviceProfilesResponse List(global::Chirpstack.Api.ListDeviceProfilesRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// List the available device-profiles.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListDeviceProfilesResponse> ListAsync(global::Chirpstack.Api.ListDeviceProfilesRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List the available device-profiles.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListDeviceProfilesResponse> ListAsync(global::Chirpstack.Api.ListDeviceProfilesRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// List available ADR algorithms.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse ListAdrAlgorithms(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListAdrAlgorithms(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List available ADR algorithms.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse ListAdrAlgorithms(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_ListAdrAlgorithms, null, options, request);
+      }
+      /// <summary>
+      /// List available ADR algorithms.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse> ListAdrAlgorithmsAsync(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListAdrAlgorithmsAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List available ADR algorithms.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse> ListAdrAlgorithmsAsync(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_ListAdrAlgorithms, null, options, request);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected override DeviceProfileServiceClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new DeviceProfileServiceClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static grpc::ServerServiceDefinition BindService(DeviceProfileServiceBase serviceImpl)
+    {
+      return grpc::ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_Create, serviceImpl.Create)
+          .AddMethod(__Method_Get, serviceImpl.Get)
+          .AddMethod(__Method_Update, serviceImpl.Update)
+          .AddMethod(__Method_Delete, serviceImpl.Delete)
+          .AddMethod(__Method_List, serviceImpl.List)
+          .AddMethod(__Method_ListAdrAlgorithms, serviceImpl.ListAdrAlgorithms).Build();
+    }
+
+    /// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
+    /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+    /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static void BindService(grpc::ServiceBinderBase serviceBinder, DeviceProfileServiceBase serviceImpl)
+    {
+      serviceBinder.AddMethod(__Method_Create, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateDeviceProfileRequest, global::Chirpstack.Api.CreateDeviceProfileResponse>(serviceImpl.Create));
+      serviceBinder.AddMethod(__Method_Get, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetDeviceProfileRequest, global::Chirpstack.Api.GetDeviceProfileResponse>(serviceImpl.Get));
+      serviceBinder.AddMethod(__Method_Update, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateDeviceProfileRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Update));
+      serviceBinder.AddMethod(__Method_Delete, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteDeviceProfileRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Delete));
+      serviceBinder.AddMethod(__Method_List, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListDeviceProfilesRequest, global::Chirpstack.Api.ListDeviceProfilesResponse>(serviceImpl.List));
+      serviceBinder.AddMethod(__Method_ListAdrAlgorithms, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.ListDeviceProfileAdrAlgorithmsResponse>(serviceImpl.ListAdrAlgorithms));
+    }
+
+  }
+}
+#endregion
diff --git a/api/csharp/Chirpstack/api/DeviceProfileTemplate.cs b/api/csharp/Chirpstack/api/DeviceProfileTemplate.cs
new file mode 100644
index 00000000..31ef838c
--- /dev/null
+++ b/api/csharp/Chirpstack/api/DeviceProfileTemplate.cs
@@ -0,0 +1,3609 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/device_profile_template.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/device_profile_template.proto</summary>
+  public static partial class DeviceProfileTemplateReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/device_profile_template.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static DeviceProfileTemplateReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "CiFhcGkvZGV2aWNlX3Byb2ZpbGVfdGVtcGxhdGUucHJvdG8SA2FwaRocZ29v",
+            "Z2xlL2FwaS9hbm5vdGF0aW9ucy5wcm90bxofZ29vZ2xlL3Byb3RvYnVmL3Rp",
+            "bWVzdGFtcC5wcm90bxobZ29vZ2xlL3Byb3RvYnVmL2VtcHR5LnByb3RvGhNj",
+            "b21tb24vY29tbW9uLnByb3RvGhhhcGkvZGV2aWNlX3Byb2ZpbGUucHJvdG8i",
+            "9gcKFURldmljZVByb2ZpbGVUZW1wbGF0ZRIKCgJpZBgBIAEoCRIMCgRuYW1l",
+            "GAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEg4KBnZlbmRvchgEIAEoCRIQ",
+            "CghmaXJtd2FyZRgFIAEoCRIeCgZyZWdpb24YBiABKA4yDi5jb21tb24uUmVn",
+            "aW9uEicKC21hY192ZXJzaW9uGAcgASgOMhIuY29tbW9uLk1hY1ZlcnNpb24S",
+            "NgoTcmVnX3BhcmFtc19yZXZpc2lvbhgIIAEoDjIZLmNvbW1vbi5SZWdQYXJh",
+            "bXNSZXZpc2lvbhIYChBhZHJfYWxnb3JpdGhtX2lkGAkgASgJEjAKFXBheWxv",
+            "YWRfY29kZWNfcnVudGltZRgKIAEoDjIRLmFwaS5Db2RlY1J1bnRpbWUSHAoU",
+            "cGF5bG9hZF9jb2RlY19zY3JpcHQYCyABKAkSHwoXZmx1c2hfcXVldWVfb25f",
+            "YWN0aXZhdGUYDCABKAgSFwoPdXBsaW5rX2ludGVydmFsGA0gASgNEiIKGmRl",
+            "dmljZV9zdGF0dXNfcmVxX2ludGVydmFsGA4gASgNEhUKDXN1cHBvcnRzX290",
+            "YWEYDyABKAgSGAoQc3VwcG9ydHNfY2xhc3NfYhgQIAEoCBIYChBzdXBwb3J0",
+            "c19jbGFzc19jGBEgASgIEhcKD2NsYXNzX2JfdGltZW91dBgSIAEoDRIeChZj",
+            "bGFzc19iX3Bpbmdfc2xvdF9uYl9rGBMgASgNEhwKFGNsYXNzX2JfcGluZ19z",
+            "bG90X2RyGBQgASgNEh4KFmNsYXNzX2JfcGluZ19zbG90X2ZyZXEYFSABKA0S",
+            "FwoPY2xhc3NfY190aW1lb3V0GBYgASgNEhUKDWFicF9yeDFfZGVsYXkYFyAB",
+            "KA0SGQoRYWJwX3J4MV9kcl9vZmZzZXQYGCABKA0SEgoKYWJwX3J4Ml9kchgZ",
+            "IAEoDRIUCgxhYnBfcngyX2ZyZXEYGiABKA0SMgoEdGFncxgbIAMoCzIkLmFw",
+            "aS5EZXZpY2VQcm9maWxlVGVtcGxhdGUuVGFnc0VudHJ5EkIKDG1lYXN1cmVt",
+            "ZW50cxgcIAMoCzIsLmFwaS5EZXZpY2VQcm9maWxlVGVtcGxhdGUuTWVhc3Vy",
+            "ZW1lbnRzRW50cnkSIAoYYXV0b19kZXRlY3RfbWVhc3VyZW1lbnRzGB0gASgI",
+            "GisKCVRhZ3NFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgB",
+            "GkUKEU1lYXN1cmVtZW50c0VudHJ5EgsKA2tleRgBIAEoCRIfCgV2YWx1ZRgC",
+            "IAEoCzIQLmFwaS5NZWFzdXJlbWVudDoCOAEihwMKHURldmljZVByb2ZpbGVU",
+            "ZW1wbGF0ZUxpc3RJdGVtEgoKAmlkGAEgASgJEi4KCmNyZWF0ZWRfYXQYAiAB",
+            "KAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCnVwZGF0ZWRfYXQY",
+            "AyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEgwKBG5hbWUYBCAB",
+            "KAkSDgoGdmVuZG9yGAUgASgJEhAKCGZpcm13YXJlGAYgASgJEh4KBnJlZ2lv",
+            "bhgHIAEoDjIOLmNvbW1vbi5SZWdpb24SJwoLbWFjX3ZlcnNpb24YCCABKA4y",
+            "Ei5jb21tb24uTWFjVmVyc2lvbhI2ChNyZWdfcGFyYW1zX3JldmlzaW9uGAkg",
+            "ASgOMhkuY29tbW9uLlJlZ1BhcmFtc1JldmlzaW9uEhUKDXN1cHBvcnRzX290",
+            "YWEYCiABKAgSGAoQc3VwcG9ydHNfY2xhc3NfYhgLIAEoCBIYChBzdXBwb3J0",
+            "c19jbGFzc19jGAwgASgIImEKIkNyZWF0ZURldmljZVByb2ZpbGVUZW1wbGF0",
+            "ZVJlcXVlc3QSOwoXZGV2aWNlX3Byb2ZpbGVfdGVtcGxhdGUYASABKAsyGi5h",
+            "cGkuRGV2aWNlUHJvZmlsZVRlbXBsYXRlIi0KH0dldERldmljZVByb2ZpbGVU",
+            "ZW1wbGF0ZVJlcXVlc3QSCgoCaWQYASABKAkivwEKIEdldERldmljZVByb2Zp",
+            "bGVUZW1wbGF0ZVJlc3BvbnNlEjsKF2RldmljZV9wcm9maWxlX3RlbXBsYXRl",
+            "GAEgASgLMhouYXBpLkRldmljZVByb2ZpbGVUZW1wbGF0ZRIuCgpjcmVhdGVk",
+            "X2F0GAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgp1cGRh",
+            "dGVkX2F0GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCJhCiJV",
+            "cGRhdGVEZXZpY2VQcm9maWxlVGVtcGxhdGVSZXF1ZXN0EjsKF2RldmljZV9w",
+            "cm9maWxlX3RlbXBsYXRlGAEgASgLMhouYXBpLkRldmljZVByb2ZpbGVUZW1w",
+            "bGF0ZSIwCiJEZWxldGVEZXZpY2VQcm9maWxlVGVtcGxhdGVSZXF1ZXN0EgoK",
+            "AmlkGAEgASgJIkIKIUxpc3REZXZpY2VQcm9maWxlVGVtcGxhdGVzUmVxdWVz",
+            "dBINCgVsaW1pdBgBIAEoDRIOCgZvZmZzZXQYAiABKA0ibQoiTGlzdERldmlj",
+            "ZVByb2ZpbGVUZW1wbGF0ZXNSZXNwb25zZRITCgt0b3RhbF9jb3VudBgBIAEo",
+            "DRIyCgZyZXN1bHQYAiADKAsyIi5hcGkuRGV2aWNlUHJvZmlsZVRlbXBsYXRl",
+            "TGlzdEl0ZW0ynQUKHERldmljZVByb2ZpbGVUZW1wbGF0ZVNlcnZpY2UScwoG",
+            "Q3JlYXRlEicuYXBpLkNyZWF0ZURldmljZVByb2ZpbGVUZW1wbGF0ZVJlcXVl",
+            "c3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiKILT5JMCIiIdL2FwaS9kZXZp",
+            "Y2UtcHJvZmlsZS10ZW1wbGF0ZXM6ASoSfgoDR2V0EiQuYXBpLkdldERldmlj",
+            "ZVByb2ZpbGVUZW1wbGF0ZVJlcXVlc3QaJS5hcGkuR2V0RGV2aWNlUHJvZmls",
+            "ZVRlbXBsYXRlUmVzcG9uc2UiKoLT5JMCJBIiL2FwaS9kZXZpY2UtcHJvZmls",
+            "ZS10ZW1wbGF0ZXMve2lkfRKQAQoGVXBkYXRlEicuYXBpLlVwZGF0ZURldmlj",
+            "ZVByb2ZpbGVUZW1wbGF0ZVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1w",
+            "dHkiRYLT5JMCPxo6L2FwaS9kZXZpY2UtcHJvZmlsZS10ZW1wbGF0ZXMve2Rl",
+            "dmljZV9wcm9maWxlX3RlbXBsYXRlLmlkfToBKhJ1CgZEZWxldGUSJy5hcGku",
+            "RGVsZXRlRGV2aWNlUHJvZmlsZVRlbXBsYXRlUmVxdWVzdBoWLmdvb2dsZS5w",
+            "cm90b2J1Zi5FbXB0eSIqgtPkkwIkKiIvYXBpL2RldmljZS1wcm9maWxlLXRl",
+            "bXBsYXRlcy97aWR9En4KBExpc3QSJi5hcGkuTGlzdERldmljZVByb2ZpbGVU",
+            "ZW1wbGF0ZXNSZXF1ZXN0GicuYXBpLkxpc3REZXZpY2VQcm9maWxlVGVtcGxh",
+            "dGVzUmVzcG9uc2UiJYLT5JMCHxIdL2FwaS9kZXZpY2UtcHJvZmlsZS10ZW1w",
+            "bGF0ZXNCcgoRaW8uY2hpcnBzdGFjay5hcGlCGkRldmljZVByb2ZpbGVUZW1w",
+            "bGF0ZVByb3RvUAFaLmdpdGh1Yi5jb20vY2hpcnBzdGFjay9jaGlycHN0YWNr",
+            "L2FwaS9nby92NC9hcGmqAg5DaGlycHN0YWNrLkFwaWIGcHJvdG8z"));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Api.AnnotationsReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Chirpstack.Common.CommonReflection.Descriptor, global::Chirpstack.Api.DeviceProfileReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceProfileTemplate), global::Chirpstack.Api.DeviceProfileTemplate.Parser, new[]{ "Id", "Name", "Description", "Vendor", "Firmware", "Region", "MacVersion", "RegParamsRevision", "AdrAlgorithmId", "PayloadCodecRuntime", "PayloadCodecScript", "FlushQueueOnActivate", "UplinkInterval", "DeviceStatusReqInterval", "SupportsOtaa", "SupportsClassB", "SupportsClassC", "ClassBTimeout", "ClassBPingSlotNbK", "ClassBPingSlotDr", "ClassBPingSlotFreq", "ClassCTimeout", "AbpRx1Delay", "AbpRx1DrOffset", "AbpRx2Dr", "AbpRx2Freq", "Tags", "Measurements", "AutoDetectMeasurements" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeviceProfileTemplateListItem), global::Chirpstack.Api.DeviceProfileTemplateListItem.Parser, new[]{ "Id", "CreatedAt", "UpdatedAt", "Name", "Vendor", "Firmware", "Region", "MacVersion", "RegParamsRevision", "SupportsOtaa", "SupportsClassB", "SupportsClassC" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateDeviceProfileTemplateRequest), global::Chirpstack.Api.CreateDeviceProfileTemplateRequest.Parser, new[]{ "DeviceProfileTemplate" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceProfileTemplateRequest), global::Chirpstack.Api.GetDeviceProfileTemplateRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDeviceProfileTemplateResponse), global::Chirpstack.Api.GetDeviceProfileTemplateResponse.Parser, new[]{ "DeviceProfileTemplate", "CreatedAt", "UpdatedAt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest), global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest.Parser, new[]{ "DeviceProfileTemplate" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest), global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListDeviceProfileTemplatesRequest), global::Chirpstack.Api.ListDeviceProfileTemplatesRequest.Parser, new[]{ "Limit", "Offset" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListDeviceProfileTemplatesResponse), global::Chirpstack.Api.ListDeviceProfileTemplatesResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  public sealed partial class DeviceProfileTemplate : pb::IMessage<DeviceProfileTemplate>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceProfileTemplate> _parser = new pb::MessageParser<DeviceProfileTemplate>(() => new DeviceProfileTemplate());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceProfileTemplate> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileTemplateReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfileTemplate() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfileTemplate(DeviceProfileTemplate other) : this() {
+      id_ = other.id_;
+      name_ = other.name_;
+      description_ = other.description_;
+      vendor_ = other.vendor_;
+      firmware_ = other.firmware_;
+      region_ = other.region_;
+      macVersion_ = other.macVersion_;
+      regParamsRevision_ = other.regParamsRevision_;
+      adrAlgorithmId_ = other.adrAlgorithmId_;
+      payloadCodecRuntime_ = other.payloadCodecRuntime_;
+      payloadCodecScript_ = other.payloadCodecScript_;
+      flushQueueOnActivate_ = other.flushQueueOnActivate_;
+      uplinkInterval_ = other.uplinkInterval_;
+      deviceStatusReqInterval_ = other.deviceStatusReqInterval_;
+      supportsOtaa_ = other.supportsOtaa_;
+      supportsClassB_ = other.supportsClassB_;
+      supportsClassC_ = other.supportsClassC_;
+      classBTimeout_ = other.classBTimeout_;
+      classBPingSlotNbK_ = other.classBPingSlotNbK_;
+      classBPingSlotDr_ = other.classBPingSlotDr_;
+      classBPingSlotFreq_ = other.classBPingSlotFreq_;
+      classCTimeout_ = other.classCTimeout_;
+      abpRx1Delay_ = other.abpRx1Delay_;
+      abpRx1DrOffset_ = other.abpRx1DrOffset_;
+      abpRx2Dr_ = other.abpRx2Dr_;
+      abpRx2Freq_ = other.abpRx2Freq_;
+      tags_ = other.tags_.Clone();
+      measurements_ = other.measurements_.Clone();
+      autoDetectMeasurements_ = other.autoDetectMeasurements_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfileTemplate Clone() {
+      return new DeviceProfileTemplate(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Device-profile template ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 2;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 3;
+    private string description_ = "";
+    /// <summary>
+    /// Description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "vendor" field.</summary>
+    public const int VendorFieldNumber = 4;
+    private string vendor_ = "";
+    /// <summary>
+    /// Vendor.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Vendor {
+      get { return vendor_; }
+      set {
+        vendor_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "firmware" field.</summary>
+    public const int FirmwareFieldNumber = 5;
+    private string firmware_ = "";
+    /// <summary>
+    /// Firmware.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Firmware {
+      get { return firmware_; }
+      set {
+        firmware_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "region" field.</summary>
+    public const int RegionFieldNumber = 6;
+    private global::Chirpstack.Common.Region region_ = global::Chirpstack.Common.Region.Eu868;
+    /// <summary>
+    /// Region.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Region Region {
+      get { return region_; }
+      set {
+        region_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "mac_version" field.</summary>
+    public const int MacVersionFieldNumber = 7;
+    private global::Chirpstack.Common.MacVersion macVersion_ = global::Chirpstack.Common.MacVersion.Lorawan100;
+    /// <summary>
+    /// LoRaWAN mac-version.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.MacVersion MacVersion {
+      get { return macVersion_; }
+      set {
+        macVersion_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "reg_params_revision" field.</summary>
+    public const int RegParamsRevisionFieldNumber = 8;
+    private global::Chirpstack.Common.RegParamsRevision regParamsRevision_ = global::Chirpstack.Common.RegParamsRevision.A;
+    /// <summary>
+    /// Regional parameters revision.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.RegParamsRevision RegParamsRevision {
+      get { return regParamsRevision_; }
+      set {
+        regParamsRevision_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "adr_algorithm_id" field.</summary>
+    public const int AdrAlgorithmIdFieldNumber = 9;
+    private string adrAlgorithmId_ = "";
+    /// <summary>
+    /// ADR algorithm ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string AdrAlgorithmId {
+      get { return adrAlgorithmId_; }
+      set {
+        adrAlgorithmId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "payload_codec_runtime" field.</summary>
+    public const int PayloadCodecRuntimeFieldNumber = 10;
+    private global::Chirpstack.Api.CodecRuntime payloadCodecRuntime_ = global::Chirpstack.Api.CodecRuntime.None;
+    /// <summary>
+    /// Payload codec runtime.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.CodecRuntime PayloadCodecRuntime {
+      get { return payloadCodecRuntime_; }
+      set {
+        payloadCodecRuntime_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "payload_codec_script" field.</summary>
+    public const int PayloadCodecScriptFieldNumber = 11;
+    private string payloadCodecScript_ = "";
+    /// <summary>
+    /// Payload codec script.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string PayloadCodecScript {
+      get { return payloadCodecScript_; }
+      set {
+        payloadCodecScript_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "flush_queue_on_activate" field.</summary>
+    public const int FlushQueueOnActivateFieldNumber = 12;
+    private bool flushQueueOnActivate_;
+    /// <summary>
+    /// Flush queue on device activation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool FlushQueueOnActivate {
+      get { return flushQueueOnActivate_; }
+      set {
+        flushQueueOnActivate_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "uplink_interval" field.</summary>
+    public const int UplinkIntervalFieldNumber = 13;
+    private uint uplinkInterval_;
+    /// <summary>
+    /// Uplink interval (seconds).
+    /// This defines the expected uplink interval which the device uses for
+    /// communication. When the uplink interval has expired and no uplink has
+    /// been received, the device is considered inactive.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint UplinkInterval {
+      get { return uplinkInterval_; }
+      set {
+        uplinkInterval_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_status_req_interval" field.</summary>
+    public const int DeviceStatusReqIntervalFieldNumber = 14;
+    private uint deviceStatusReqInterval_;
+    /// <summary>
+    /// Device-status request interval (times / day).
+    /// This defines the times per day that ChirpStack will request the device-status
+    /// from the device.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint DeviceStatusReqInterval {
+      get { return deviceStatusReqInterval_; }
+      set {
+        deviceStatusReqInterval_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_otaa" field.</summary>
+    public const int SupportsOtaaFieldNumber = 15;
+    private bool supportsOtaa_;
+    /// <summary>
+    /// Supports OTAA.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsOtaa {
+      get { return supportsOtaa_; }
+      set {
+        supportsOtaa_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_class_b" field.</summary>
+    public const int SupportsClassBFieldNumber = 16;
+    private bool supportsClassB_;
+    /// <summary>
+    /// Supports Class B.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsClassB {
+      get { return supportsClassB_; }
+      set {
+        supportsClassB_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_class_c" field.</summary>
+    public const int SupportsClassCFieldNumber = 17;
+    private bool supportsClassC_;
+    /// <summary>
+    /// Supports Class-C.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsClassC {
+      get { return supportsClassC_; }
+      set {
+        supportsClassC_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_timeout" field.</summary>
+    public const int ClassBTimeoutFieldNumber = 18;
+    private uint classBTimeout_;
+    /// <summary>
+    /// Class-B timeout (seconds).
+    /// This is the maximum time ChirpStack will wait to receive an acknowledgement from the device (if requested).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBTimeout {
+      get { return classBTimeout_; }
+      set {
+        classBTimeout_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_ping_slot_nb_k" field.</summary>
+    public const int ClassBPingSlotNbKFieldNumber = 19;
+    private uint classBPingSlotNbK_;
+    /// <summary>
+    /// Class-B ping-slots per beacon period.
+    /// Valid options are: 0 - 7.
+    ///
+    /// The actual number of ping-slots per beacon period equals to 2^k.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBPingSlotNbK {
+      get { return classBPingSlotNbK_; }
+      set {
+        classBPingSlotNbK_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_ping_slot_dr" field.</summary>
+    public const int ClassBPingSlotDrFieldNumber = 20;
+    private uint classBPingSlotDr_;
+    /// <summary>
+    /// Class-B ping-slot DR.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBPingSlotDr {
+      get { return classBPingSlotDr_; }
+      set {
+        classBPingSlotDr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_ping_slot_freq" field.</summary>
+    public const int ClassBPingSlotFreqFieldNumber = 21;
+    private uint classBPingSlotFreq_;
+    /// <summary>
+    /// Class-B ping-slot freq (Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBPingSlotFreq {
+      get { return classBPingSlotFreq_; }
+      set {
+        classBPingSlotFreq_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_c_timeout" field.</summary>
+    public const int ClassCTimeoutFieldNumber = 22;
+    private uint classCTimeout_;
+    /// <summary>
+    /// Class-C timeout (seconds).
+    /// This is the maximum time ChirpStack will wait to receive an acknowledgement from the device (if requested).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassCTimeout {
+      get { return classCTimeout_; }
+      set {
+        classCTimeout_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "abp_rx1_delay" field.</summary>
+    public const int AbpRx1DelayFieldNumber = 23;
+    private uint abpRx1Delay_;
+    /// <summary>
+    /// RX1 delay (for ABP).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint AbpRx1Delay {
+      get { return abpRx1Delay_; }
+      set {
+        abpRx1Delay_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "abp_rx1_dr_offset" field.</summary>
+    public const int AbpRx1DrOffsetFieldNumber = 24;
+    private uint abpRx1DrOffset_;
+    /// <summary>
+    /// RX1 DR offset (for ABP).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint AbpRx1DrOffset {
+      get { return abpRx1DrOffset_; }
+      set {
+        abpRx1DrOffset_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "abp_rx2_dr" field.</summary>
+    public const int AbpRx2DrFieldNumber = 25;
+    private uint abpRx2Dr_;
+    /// <summary>
+    /// RX2 DR (for ABP).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint AbpRx2Dr {
+      get { return abpRx2Dr_; }
+      set {
+        abpRx2Dr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "abp_rx2_freq" field.</summary>
+    public const int AbpRx2FreqFieldNumber = 26;
+    private uint abpRx2Freq_;
+    /// <summary>
+    /// RX2 frequency (for ABP, Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint AbpRx2Freq {
+      get { return abpRx2Freq_; }
+      set {
+        abpRx2Freq_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tags" field.</summary>
+    public const int TagsFieldNumber = 27;
+    private static readonly pbc::MapField<string, string>.Codec _map_tags_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 218);
+    private readonly pbc::MapField<string, string> tags_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// User defined tags.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Tags {
+      get { return tags_; }
+    }
+
+    /// <summary>Field number for the "measurements" field.</summary>
+    public const int MeasurementsFieldNumber = 28;
+    private static readonly pbc::MapField<string, global::Chirpstack.Api.Measurement>.Codec _map_measurements_codec
+        = new pbc::MapField<string, global::Chirpstack.Api.Measurement>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.Measurement.Parser), 226);
+    private readonly pbc::MapField<string, global::Chirpstack.Api.Measurement> measurements_ = new pbc::MapField<string, global::Chirpstack.Api.Measurement>();
+    /// <summary>
+    /// Measurements.
+    /// If defined, ChirpStack will visualize these metrics in the web-interface.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, global::Chirpstack.Api.Measurement> Measurements {
+      get { return measurements_; }
+    }
+
+    /// <summary>Field number for the "auto_detect_measurements" field.</summary>
+    public const int AutoDetectMeasurementsFieldNumber = 29;
+    private bool autoDetectMeasurements_;
+    /// <summary>
+    /// Auto-detect measurements.
+    /// If set to true, measurements will be automatically added based on the
+    /// keys of the decoded payload. In cases where the decoded payload contains
+    /// random keys in the data, you want to set this to false.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool AutoDetectMeasurements {
+      get { return autoDetectMeasurements_; }
+      set {
+        autoDetectMeasurements_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceProfileTemplate);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceProfileTemplate other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (Name != other.Name) return false;
+      if (Description != other.Description) return false;
+      if (Vendor != other.Vendor) return false;
+      if (Firmware != other.Firmware) return false;
+      if (Region != other.Region) return false;
+      if (MacVersion != other.MacVersion) return false;
+      if (RegParamsRevision != other.RegParamsRevision) return false;
+      if (AdrAlgorithmId != other.AdrAlgorithmId) return false;
+      if (PayloadCodecRuntime != other.PayloadCodecRuntime) return false;
+      if (PayloadCodecScript != other.PayloadCodecScript) return false;
+      if (FlushQueueOnActivate != other.FlushQueueOnActivate) return false;
+      if (UplinkInterval != other.UplinkInterval) return false;
+      if (DeviceStatusReqInterval != other.DeviceStatusReqInterval) return false;
+      if (SupportsOtaa != other.SupportsOtaa) return false;
+      if (SupportsClassB != other.SupportsClassB) return false;
+      if (SupportsClassC != other.SupportsClassC) return false;
+      if (ClassBTimeout != other.ClassBTimeout) return false;
+      if (ClassBPingSlotNbK != other.ClassBPingSlotNbK) return false;
+      if (ClassBPingSlotDr != other.ClassBPingSlotDr) return false;
+      if (ClassBPingSlotFreq != other.ClassBPingSlotFreq) return false;
+      if (ClassCTimeout != other.ClassCTimeout) return false;
+      if (AbpRx1Delay != other.AbpRx1Delay) return false;
+      if (AbpRx1DrOffset != other.AbpRx1DrOffset) return false;
+      if (AbpRx2Dr != other.AbpRx2Dr) return false;
+      if (AbpRx2Freq != other.AbpRx2Freq) return false;
+      if (!Tags.Equals(other.Tags)) return false;
+      if (!Measurements.Equals(other.Measurements)) return false;
+      if (AutoDetectMeasurements != other.AutoDetectMeasurements) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (Vendor.Length != 0) hash ^= Vendor.GetHashCode();
+      if (Firmware.Length != 0) hash ^= Firmware.GetHashCode();
+      if (Region != global::Chirpstack.Common.Region.Eu868) hash ^= Region.GetHashCode();
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) hash ^= MacVersion.GetHashCode();
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) hash ^= RegParamsRevision.GetHashCode();
+      if (AdrAlgorithmId.Length != 0) hash ^= AdrAlgorithmId.GetHashCode();
+      if (PayloadCodecRuntime != global::Chirpstack.Api.CodecRuntime.None) hash ^= PayloadCodecRuntime.GetHashCode();
+      if (PayloadCodecScript.Length != 0) hash ^= PayloadCodecScript.GetHashCode();
+      if (FlushQueueOnActivate != false) hash ^= FlushQueueOnActivate.GetHashCode();
+      if (UplinkInterval != 0) hash ^= UplinkInterval.GetHashCode();
+      if (DeviceStatusReqInterval != 0) hash ^= DeviceStatusReqInterval.GetHashCode();
+      if (SupportsOtaa != false) hash ^= SupportsOtaa.GetHashCode();
+      if (SupportsClassB != false) hash ^= SupportsClassB.GetHashCode();
+      if (SupportsClassC != false) hash ^= SupportsClassC.GetHashCode();
+      if (ClassBTimeout != 0) hash ^= ClassBTimeout.GetHashCode();
+      if (ClassBPingSlotNbK != 0) hash ^= ClassBPingSlotNbK.GetHashCode();
+      if (ClassBPingSlotDr != 0) hash ^= ClassBPingSlotDr.GetHashCode();
+      if (ClassBPingSlotFreq != 0) hash ^= ClassBPingSlotFreq.GetHashCode();
+      if (ClassCTimeout != 0) hash ^= ClassCTimeout.GetHashCode();
+      if (AbpRx1Delay != 0) hash ^= AbpRx1Delay.GetHashCode();
+      if (AbpRx1DrOffset != 0) hash ^= AbpRx1DrOffset.GetHashCode();
+      if (AbpRx2Dr != 0) hash ^= AbpRx2Dr.GetHashCode();
+      if (AbpRx2Freq != 0) hash ^= AbpRx2Freq.GetHashCode();
+      hash ^= Tags.GetHashCode();
+      hash ^= Measurements.GetHashCode();
+      if (AutoDetectMeasurements != false) hash ^= AutoDetectMeasurements.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (Vendor.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Vendor);
+      }
+      if (Firmware.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Firmware);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        output.WriteRawTag(64);
+        output.WriteEnum((int) RegParamsRevision);
+      }
+      if (AdrAlgorithmId.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(AdrAlgorithmId);
+      }
+      if (PayloadCodecRuntime != global::Chirpstack.Api.CodecRuntime.None) {
+        output.WriteRawTag(80);
+        output.WriteEnum((int) PayloadCodecRuntime);
+      }
+      if (PayloadCodecScript.Length != 0) {
+        output.WriteRawTag(90);
+        output.WriteString(PayloadCodecScript);
+      }
+      if (FlushQueueOnActivate != false) {
+        output.WriteRawTag(96);
+        output.WriteBool(FlushQueueOnActivate);
+      }
+      if (UplinkInterval != 0) {
+        output.WriteRawTag(104);
+        output.WriteUInt32(UplinkInterval);
+      }
+      if (DeviceStatusReqInterval != 0) {
+        output.WriteRawTag(112);
+        output.WriteUInt32(DeviceStatusReqInterval);
+      }
+      if (SupportsOtaa != false) {
+        output.WriteRawTag(120);
+        output.WriteBool(SupportsOtaa);
+      }
+      if (SupportsClassB != false) {
+        output.WriteRawTag(128, 1);
+        output.WriteBool(SupportsClassB);
+      }
+      if (SupportsClassC != false) {
+        output.WriteRawTag(136, 1);
+        output.WriteBool(SupportsClassC);
+      }
+      if (ClassBTimeout != 0) {
+        output.WriteRawTag(144, 1);
+        output.WriteUInt32(ClassBTimeout);
+      }
+      if (ClassBPingSlotNbK != 0) {
+        output.WriteRawTag(152, 1);
+        output.WriteUInt32(ClassBPingSlotNbK);
+      }
+      if (ClassBPingSlotDr != 0) {
+        output.WriteRawTag(160, 1);
+        output.WriteUInt32(ClassBPingSlotDr);
+      }
+      if (ClassBPingSlotFreq != 0) {
+        output.WriteRawTag(168, 1);
+        output.WriteUInt32(ClassBPingSlotFreq);
+      }
+      if (ClassCTimeout != 0) {
+        output.WriteRawTag(176, 1);
+        output.WriteUInt32(ClassCTimeout);
+      }
+      if (AbpRx1Delay != 0) {
+        output.WriteRawTag(184, 1);
+        output.WriteUInt32(AbpRx1Delay);
+      }
+      if (AbpRx1DrOffset != 0) {
+        output.WriteRawTag(192, 1);
+        output.WriteUInt32(AbpRx1DrOffset);
+      }
+      if (AbpRx2Dr != 0) {
+        output.WriteRawTag(200, 1);
+        output.WriteUInt32(AbpRx2Dr);
+      }
+      if (AbpRx2Freq != 0) {
+        output.WriteRawTag(208, 1);
+        output.WriteUInt32(AbpRx2Freq);
+      }
+      tags_.WriteTo(output, _map_tags_codec);
+      measurements_.WriteTo(output, _map_measurements_codec);
+      if (AutoDetectMeasurements != false) {
+        output.WriteRawTag(232, 1);
+        output.WriteBool(AutoDetectMeasurements);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (Vendor.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Vendor);
+      }
+      if (Firmware.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Firmware);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        output.WriteRawTag(64);
+        output.WriteEnum((int) RegParamsRevision);
+      }
+      if (AdrAlgorithmId.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(AdrAlgorithmId);
+      }
+      if (PayloadCodecRuntime != global::Chirpstack.Api.CodecRuntime.None) {
+        output.WriteRawTag(80);
+        output.WriteEnum((int) PayloadCodecRuntime);
+      }
+      if (PayloadCodecScript.Length != 0) {
+        output.WriteRawTag(90);
+        output.WriteString(PayloadCodecScript);
+      }
+      if (FlushQueueOnActivate != false) {
+        output.WriteRawTag(96);
+        output.WriteBool(FlushQueueOnActivate);
+      }
+      if (UplinkInterval != 0) {
+        output.WriteRawTag(104);
+        output.WriteUInt32(UplinkInterval);
+      }
+      if (DeviceStatusReqInterval != 0) {
+        output.WriteRawTag(112);
+        output.WriteUInt32(DeviceStatusReqInterval);
+      }
+      if (SupportsOtaa != false) {
+        output.WriteRawTag(120);
+        output.WriteBool(SupportsOtaa);
+      }
+      if (SupportsClassB != false) {
+        output.WriteRawTag(128, 1);
+        output.WriteBool(SupportsClassB);
+      }
+      if (SupportsClassC != false) {
+        output.WriteRawTag(136, 1);
+        output.WriteBool(SupportsClassC);
+      }
+      if (ClassBTimeout != 0) {
+        output.WriteRawTag(144, 1);
+        output.WriteUInt32(ClassBTimeout);
+      }
+      if (ClassBPingSlotNbK != 0) {
+        output.WriteRawTag(152, 1);
+        output.WriteUInt32(ClassBPingSlotNbK);
+      }
+      if (ClassBPingSlotDr != 0) {
+        output.WriteRawTag(160, 1);
+        output.WriteUInt32(ClassBPingSlotDr);
+      }
+      if (ClassBPingSlotFreq != 0) {
+        output.WriteRawTag(168, 1);
+        output.WriteUInt32(ClassBPingSlotFreq);
+      }
+      if (ClassCTimeout != 0) {
+        output.WriteRawTag(176, 1);
+        output.WriteUInt32(ClassCTimeout);
+      }
+      if (AbpRx1Delay != 0) {
+        output.WriteRawTag(184, 1);
+        output.WriteUInt32(AbpRx1Delay);
+      }
+      if (AbpRx1DrOffset != 0) {
+        output.WriteRawTag(192, 1);
+        output.WriteUInt32(AbpRx1DrOffset);
+      }
+      if (AbpRx2Dr != 0) {
+        output.WriteRawTag(200, 1);
+        output.WriteUInt32(AbpRx2Dr);
+      }
+      if (AbpRx2Freq != 0) {
+        output.WriteRawTag(208, 1);
+        output.WriteUInt32(AbpRx2Freq);
+      }
+      tags_.WriteTo(ref output, _map_tags_codec);
+      measurements_.WriteTo(ref output, _map_measurements_codec);
+      if (AutoDetectMeasurements != false) {
+        output.WriteRawTag(232, 1);
+        output.WriteBool(AutoDetectMeasurements);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (Vendor.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Vendor);
+      }
+      if (Firmware.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Firmware);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) RegParamsRevision);
+      }
+      if (AdrAlgorithmId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(AdrAlgorithmId);
+      }
+      if (PayloadCodecRuntime != global::Chirpstack.Api.CodecRuntime.None) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) PayloadCodecRuntime);
+      }
+      if (PayloadCodecScript.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(PayloadCodecScript);
+      }
+      if (FlushQueueOnActivate != false) {
+        size += 1 + 1;
+      }
+      if (UplinkInterval != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(UplinkInterval);
+      }
+      if (DeviceStatusReqInterval != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(DeviceStatusReqInterval);
+      }
+      if (SupportsOtaa != false) {
+        size += 1 + 1;
+      }
+      if (SupportsClassB != false) {
+        size += 2 + 1;
+      }
+      if (SupportsClassC != false) {
+        size += 2 + 1;
+      }
+      if (ClassBTimeout != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(ClassBTimeout);
+      }
+      if (ClassBPingSlotNbK != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(ClassBPingSlotNbK);
+      }
+      if (ClassBPingSlotDr != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(ClassBPingSlotDr);
+      }
+      if (ClassBPingSlotFreq != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(ClassBPingSlotFreq);
+      }
+      if (ClassCTimeout != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(ClassCTimeout);
+      }
+      if (AbpRx1Delay != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(AbpRx1Delay);
+      }
+      if (AbpRx1DrOffset != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(AbpRx1DrOffset);
+      }
+      if (AbpRx2Dr != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(AbpRx2Dr);
+      }
+      if (AbpRx2Freq != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(AbpRx2Freq);
+      }
+      size += tags_.CalculateSize(_map_tags_codec);
+      size += measurements_.CalculateSize(_map_measurements_codec);
+      if (AutoDetectMeasurements != false) {
+        size += 2 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceProfileTemplate other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      if (other.Vendor.Length != 0) {
+        Vendor = other.Vendor;
+      }
+      if (other.Firmware.Length != 0) {
+        Firmware = other.Firmware;
+      }
+      if (other.Region != global::Chirpstack.Common.Region.Eu868) {
+        Region = other.Region;
+      }
+      if (other.MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        MacVersion = other.MacVersion;
+      }
+      if (other.RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        RegParamsRevision = other.RegParamsRevision;
+      }
+      if (other.AdrAlgorithmId.Length != 0) {
+        AdrAlgorithmId = other.AdrAlgorithmId;
+      }
+      if (other.PayloadCodecRuntime != global::Chirpstack.Api.CodecRuntime.None) {
+        PayloadCodecRuntime = other.PayloadCodecRuntime;
+      }
+      if (other.PayloadCodecScript.Length != 0) {
+        PayloadCodecScript = other.PayloadCodecScript;
+      }
+      if (other.FlushQueueOnActivate != false) {
+        FlushQueueOnActivate = other.FlushQueueOnActivate;
+      }
+      if (other.UplinkInterval != 0) {
+        UplinkInterval = other.UplinkInterval;
+      }
+      if (other.DeviceStatusReqInterval != 0) {
+        DeviceStatusReqInterval = other.DeviceStatusReqInterval;
+      }
+      if (other.SupportsOtaa != false) {
+        SupportsOtaa = other.SupportsOtaa;
+      }
+      if (other.SupportsClassB != false) {
+        SupportsClassB = other.SupportsClassB;
+      }
+      if (other.SupportsClassC != false) {
+        SupportsClassC = other.SupportsClassC;
+      }
+      if (other.ClassBTimeout != 0) {
+        ClassBTimeout = other.ClassBTimeout;
+      }
+      if (other.ClassBPingSlotNbK != 0) {
+        ClassBPingSlotNbK = other.ClassBPingSlotNbK;
+      }
+      if (other.ClassBPingSlotDr != 0) {
+        ClassBPingSlotDr = other.ClassBPingSlotDr;
+      }
+      if (other.ClassBPingSlotFreq != 0) {
+        ClassBPingSlotFreq = other.ClassBPingSlotFreq;
+      }
+      if (other.ClassCTimeout != 0) {
+        ClassCTimeout = other.ClassCTimeout;
+      }
+      if (other.AbpRx1Delay != 0) {
+        AbpRx1Delay = other.AbpRx1Delay;
+      }
+      if (other.AbpRx1DrOffset != 0) {
+        AbpRx1DrOffset = other.AbpRx1DrOffset;
+      }
+      if (other.AbpRx2Dr != 0) {
+        AbpRx2Dr = other.AbpRx2Dr;
+      }
+      if (other.AbpRx2Freq != 0) {
+        AbpRx2Freq = other.AbpRx2Freq;
+      }
+      tags_.Add(other.tags_);
+      measurements_.Add(other.measurements_);
+      if (other.AutoDetectMeasurements != false) {
+        AutoDetectMeasurements = other.AutoDetectMeasurements;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 34: {
+            Vendor = input.ReadString();
+            break;
+          }
+          case 42: {
+            Firmware = input.ReadString();
+            break;
+          }
+          case 48: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 56: {
+            MacVersion = (global::Chirpstack.Common.MacVersion) input.ReadEnum();
+            break;
+          }
+          case 64: {
+            RegParamsRevision = (global::Chirpstack.Common.RegParamsRevision) input.ReadEnum();
+            break;
+          }
+          case 74: {
+            AdrAlgorithmId = input.ReadString();
+            break;
+          }
+          case 80: {
+            PayloadCodecRuntime = (global::Chirpstack.Api.CodecRuntime) input.ReadEnum();
+            break;
+          }
+          case 90: {
+            PayloadCodecScript = input.ReadString();
+            break;
+          }
+          case 96: {
+            FlushQueueOnActivate = input.ReadBool();
+            break;
+          }
+          case 104: {
+            UplinkInterval = input.ReadUInt32();
+            break;
+          }
+          case 112: {
+            DeviceStatusReqInterval = input.ReadUInt32();
+            break;
+          }
+          case 120: {
+            SupportsOtaa = input.ReadBool();
+            break;
+          }
+          case 128: {
+            SupportsClassB = input.ReadBool();
+            break;
+          }
+          case 136: {
+            SupportsClassC = input.ReadBool();
+            break;
+          }
+          case 144: {
+            ClassBTimeout = input.ReadUInt32();
+            break;
+          }
+          case 152: {
+            ClassBPingSlotNbK = input.ReadUInt32();
+            break;
+          }
+          case 160: {
+            ClassBPingSlotDr = input.ReadUInt32();
+            break;
+          }
+          case 168: {
+            ClassBPingSlotFreq = input.ReadUInt32();
+            break;
+          }
+          case 176: {
+            ClassCTimeout = input.ReadUInt32();
+            break;
+          }
+          case 184: {
+            AbpRx1Delay = input.ReadUInt32();
+            break;
+          }
+          case 192: {
+            AbpRx1DrOffset = input.ReadUInt32();
+            break;
+          }
+          case 200: {
+            AbpRx2Dr = input.ReadUInt32();
+            break;
+          }
+          case 208: {
+            AbpRx2Freq = input.ReadUInt32();
+            break;
+          }
+          case 218: {
+            tags_.AddEntriesFrom(input, _map_tags_codec);
+            break;
+          }
+          case 226: {
+            measurements_.AddEntriesFrom(input, _map_measurements_codec);
+            break;
+          }
+          case 232: {
+            AutoDetectMeasurements = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 34: {
+            Vendor = input.ReadString();
+            break;
+          }
+          case 42: {
+            Firmware = input.ReadString();
+            break;
+          }
+          case 48: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 56: {
+            MacVersion = (global::Chirpstack.Common.MacVersion) input.ReadEnum();
+            break;
+          }
+          case 64: {
+            RegParamsRevision = (global::Chirpstack.Common.RegParamsRevision) input.ReadEnum();
+            break;
+          }
+          case 74: {
+            AdrAlgorithmId = input.ReadString();
+            break;
+          }
+          case 80: {
+            PayloadCodecRuntime = (global::Chirpstack.Api.CodecRuntime) input.ReadEnum();
+            break;
+          }
+          case 90: {
+            PayloadCodecScript = input.ReadString();
+            break;
+          }
+          case 96: {
+            FlushQueueOnActivate = input.ReadBool();
+            break;
+          }
+          case 104: {
+            UplinkInterval = input.ReadUInt32();
+            break;
+          }
+          case 112: {
+            DeviceStatusReqInterval = input.ReadUInt32();
+            break;
+          }
+          case 120: {
+            SupportsOtaa = input.ReadBool();
+            break;
+          }
+          case 128: {
+            SupportsClassB = input.ReadBool();
+            break;
+          }
+          case 136: {
+            SupportsClassC = input.ReadBool();
+            break;
+          }
+          case 144: {
+            ClassBTimeout = input.ReadUInt32();
+            break;
+          }
+          case 152: {
+            ClassBPingSlotNbK = input.ReadUInt32();
+            break;
+          }
+          case 160: {
+            ClassBPingSlotDr = input.ReadUInt32();
+            break;
+          }
+          case 168: {
+            ClassBPingSlotFreq = input.ReadUInt32();
+            break;
+          }
+          case 176: {
+            ClassCTimeout = input.ReadUInt32();
+            break;
+          }
+          case 184: {
+            AbpRx1Delay = input.ReadUInt32();
+            break;
+          }
+          case 192: {
+            AbpRx1DrOffset = input.ReadUInt32();
+            break;
+          }
+          case 200: {
+            AbpRx2Dr = input.ReadUInt32();
+            break;
+          }
+          case 208: {
+            AbpRx2Freq = input.ReadUInt32();
+            break;
+          }
+          case 218: {
+            tags_.AddEntriesFrom(ref input, _map_tags_codec);
+            break;
+          }
+          case 226: {
+            measurements_.AddEntriesFrom(ref input, _map_measurements_codec);
+            break;
+          }
+          case 232: {
+            AutoDetectMeasurements = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeviceProfileTemplateListItem : pb::IMessage<DeviceProfileTemplateListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceProfileTemplateListItem> _parser = new pb::MessageParser<DeviceProfileTemplateListItem>(() => new DeviceProfileTemplateListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceProfileTemplateListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileTemplateReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfileTemplateListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfileTemplateListItem(DeviceProfileTemplateListItem other) : this() {
+      id_ = other.id_;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      name_ = other.name_;
+      vendor_ = other.vendor_;
+      firmware_ = other.firmware_;
+      region_ = other.region_;
+      macVersion_ = other.macVersion_;
+      regParamsRevision_ = other.regParamsRevision_;
+      supportsOtaa_ = other.supportsOtaa_;
+      supportsClassB_ = other.supportsClassB_;
+      supportsClassC_ = other.supportsClassC_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceProfileTemplateListItem Clone() {
+      return new DeviceProfileTemplateListItem(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Device-profile template ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 4;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "vendor" field.</summary>
+    public const int VendorFieldNumber = 5;
+    private string vendor_ = "";
+    /// <summary>
+    /// Vendor.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Vendor {
+      get { return vendor_; }
+      set {
+        vendor_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "firmware" field.</summary>
+    public const int FirmwareFieldNumber = 6;
+    private string firmware_ = "";
+    /// <summary>
+    /// Firmware.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Firmware {
+      get { return firmware_; }
+      set {
+        firmware_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "region" field.</summary>
+    public const int RegionFieldNumber = 7;
+    private global::Chirpstack.Common.Region region_ = global::Chirpstack.Common.Region.Eu868;
+    /// <summary>
+    /// Region.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Region Region {
+      get { return region_; }
+      set {
+        region_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "mac_version" field.</summary>
+    public const int MacVersionFieldNumber = 8;
+    private global::Chirpstack.Common.MacVersion macVersion_ = global::Chirpstack.Common.MacVersion.Lorawan100;
+    /// <summary>
+    /// LoRaWAN mac-version.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.MacVersion MacVersion {
+      get { return macVersion_; }
+      set {
+        macVersion_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "reg_params_revision" field.</summary>
+    public const int RegParamsRevisionFieldNumber = 9;
+    private global::Chirpstack.Common.RegParamsRevision regParamsRevision_ = global::Chirpstack.Common.RegParamsRevision.A;
+    /// <summary>
+    /// Regional parameters revision.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.RegParamsRevision RegParamsRevision {
+      get { return regParamsRevision_; }
+      set {
+        regParamsRevision_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_otaa" field.</summary>
+    public const int SupportsOtaaFieldNumber = 10;
+    private bool supportsOtaa_;
+    /// <summary>
+    /// Supports OTAA.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsOtaa {
+      get { return supportsOtaa_; }
+      set {
+        supportsOtaa_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_class_b" field.</summary>
+    public const int SupportsClassBFieldNumber = 11;
+    private bool supportsClassB_;
+    /// <summary>
+    /// Supports Class-B.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsClassB {
+      get { return supportsClassB_; }
+      set {
+        supportsClassB_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "supports_class_c" field.</summary>
+    public const int SupportsClassCFieldNumber = 12;
+    private bool supportsClassC_;
+    /// <summary>
+    /// Supports Class-C.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool SupportsClassC {
+      get { return supportsClassC_; }
+      set {
+        supportsClassC_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceProfileTemplateListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceProfileTemplateListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (Name != other.Name) return false;
+      if (Vendor != other.Vendor) return false;
+      if (Firmware != other.Firmware) return false;
+      if (Region != other.Region) return false;
+      if (MacVersion != other.MacVersion) return false;
+      if (RegParamsRevision != other.RegParamsRevision) return false;
+      if (SupportsOtaa != other.SupportsOtaa) return false;
+      if (SupportsClassB != other.SupportsClassB) return false;
+      if (SupportsClassC != other.SupportsClassC) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Vendor.Length != 0) hash ^= Vendor.GetHashCode();
+      if (Firmware.Length != 0) hash ^= Firmware.GetHashCode();
+      if (Region != global::Chirpstack.Common.Region.Eu868) hash ^= Region.GetHashCode();
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) hash ^= MacVersion.GetHashCode();
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) hash ^= RegParamsRevision.GetHashCode();
+      if (SupportsOtaa != false) hash ^= SupportsOtaa.GetHashCode();
+      if (SupportsClassB != false) hash ^= SupportsClassB.GetHashCode();
+      if (SupportsClassC != false) hash ^= SupportsClassC.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (Vendor.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Vendor);
+      }
+      if (Firmware.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(Firmware);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        output.WriteRawTag(64);
+        output.WriteEnum((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        output.WriteRawTag(72);
+        output.WriteEnum((int) RegParamsRevision);
+      }
+      if (SupportsOtaa != false) {
+        output.WriteRawTag(80);
+        output.WriteBool(SupportsOtaa);
+      }
+      if (SupportsClassB != false) {
+        output.WriteRawTag(88);
+        output.WriteBool(SupportsClassB);
+      }
+      if (SupportsClassC != false) {
+        output.WriteRawTag(96);
+        output.WriteBool(SupportsClassC);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (Vendor.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Vendor);
+      }
+      if (Firmware.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(Firmware);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        output.WriteRawTag(64);
+        output.WriteEnum((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        output.WriteRawTag(72);
+        output.WriteEnum((int) RegParamsRevision);
+      }
+      if (SupportsOtaa != false) {
+        output.WriteRawTag(80);
+        output.WriteBool(SupportsOtaa);
+      }
+      if (SupportsClassB != false) {
+        output.WriteRawTag(88);
+        output.WriteBool(SupportsClassB);
+      }
+      if (SupportsClassC != false) {
+        output.WriteRawTag(96);
+        output.WriteBool(SupportsClassC);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Vendor.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Vendor);
+      }
+      if (Firmware.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Firmware);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Region);
+      }
+      if (MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) MacVersion);
+      }
+      if (RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) RegParamsRevision);
+      }
+      if (SupportsOtaa != false) {
+        size += 1 + 1;
+      }
+      if (SupportsClassB != false) {
+        size += 1 + 1;
+      }
+      if (SupportsClassC != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceProfileTemplateListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Vendor.Length != 0) {
+        Vendor = other.Vendor;
+      }
+      if (other.Firmware.Length != 0) {
+        Firmware = other.Firmware;
+      }
+      if (other.Region != global::Chirpstack.Common.Region.Eu868) {
+        Region = other.Region;
+      }
+      if (other.MacVersion != global::Chirpstack.Common.MacVersion.Lorawan100) {
+        MacVersion = other.MacVersion;
+      }
+      if (other.RegParamsRevision != global::Chirpstack.Common.RegParamsRevision.A) {
+        RegParamsRevision = other.RegParamsRevision;
+      }
+      if (other.SupportsOtaa != false) {
+        SupportsOtaa = other.SupportsOtaa;
+      }
+      if (other.SupportsClassB != false) {
+        SupportsClassB = other.SupportsClassB;
+      }
+      if (other.SupportsClassC != false) {
+        SupportsClassC = other.SupportsClassC;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Name = input.ReadString();
+            break;
+          }
+          case 42: {
+            Vendor = input.ReadString();
+            break;
+          }
+          case 50: {
+            Firmware = input.ReadString();
+            break;
+          }
+          case 56: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 64: {
+            MacVersion = (global::Chirpstack.Common.MacVersion) input.ReadEnum();
+            break;
+          }
+          case 72: {
+            RegParamsRevision = (global::Chirpstack.Common.RegParamsRevision) input.ReadEnum();
+            break;
+          }
+          case 80: {
+            SupportsOtaa = input.ReadBool();
+            break;
+          }
+          case 88: {
+            SupportsClassB = input.ReadBool();
+            break;
+          }
+          case 96: {
+            SupportsClassC = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Name = input.ReadString();
+            break;
+          }
+          case 42: {
+            Vendor = input.ReadString();
+            break;
+          }
+          case 50: {
+            Firmware = input.ReadString();
+            break;
+          }
+          case 56: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 64: {
+            MacVersion = (global::Chirpstack.Common.MacVersion) input.ReadEnum();
+            break;
+          }
+          case 72: {
+            RegParamsRevision = (global::Chirpstack.Common.RegParamsRevision) input.ReadEnum();
+            break;
+          }
+          case 80: {
+            SupportsOtaa = input.ReadBool();
+            break;
+          }
+          case 88: {
+            SupportsClassB = input.ReadBool();
+            break;
+          }
+          case 96: {
+            SupportsClassC = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateDeviceProfileTemplateRequest : pb::IMessage<CreateDeviceProfileTemplateRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateDeviceProfileTemplateRequest> _parser = new pb::MessageParser<CreateDeviceProfileTemplateRequest>(() => new CreateDeviceProfileTemplateRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateDeviceProfileTemplateRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileTemplateReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceProfileTemplateRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceProfileTemplateRequest(CreateDeviceProfileTemplateRequest other) : this() {
+      deviceProfileTemplate_ = other.deviceProfileTemplate_ != null ? other.deviceProfileTemplate_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateDeviceProfileTemplateRequest Clone() {
+      return new CreateDeviceProfileTemplateRequest(this);
+    }
+
+    /// <summary>Field number for the "device_profile_template" field.</summary>
+    public const int DeviceProfileTemplateFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceProfileTemplate deviceProfileTemplate_;
+    /// <summary>
+    /// Object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceProfileTemplate DeviceProfileTemplate {
+      get { return deviceProfileTemplate_; }
+      set {
+        deviceProfileTemplate_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateDeviceProfileTemplateRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateDeviceProfileTemplateRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceProfileTemplate, other.DeviceProfileTemplate)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceProfileTemplate_ != null) hash ^= DeviceProfileTemplate.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceProfileTemplate_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfileTemplate);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceProfileTemplate_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfileTemplate);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceProfileTemplate_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceProfileTemplate);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateDeviceProfileTemplateRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceProfileTemplate_ != null) {
+        if (deviceProfileTemplate_ == null) {
+          DeviceProfileTemplate = new global::Chirpstack.Api.DeviceProfileTemplate();
+        }
+        DeviceProfileTemplate.MergeFrom(other.DeviceProfileTemplate);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceProfileTemplate_ == null) {
+              DeviceProfileTemplate = new global::Chirpstack.Api.DeviceProfileTemplate();
+            }
+            input.ReadMessage(DeviceProfileTemplate);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceProfileTemplate_ == null) {
+              DeviceProfileTemplate = new global::Chirpstack.Api.DeviceProfileTemplate();
+            }
+            input.ReadMessage(DeviceProfileTemplate);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceProfileTemplateRequest : pb::IMessage<GetDeviceProfileTemplateRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceProfileTemplateRequest> _parser = new pb::MessageParser<GetDeviceProfileTemplateRequest>(() => new GetDeviceProfileTemplateRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceProfileTemplateRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileTemplateReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileTemplateRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileTemplateRequest(GetDeviceProfileTemplateRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileTemplateRequest Clone() {
+      return new GetDeviceProfileTemplateRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceProfileTemplateRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceProfileTemplateRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceProfileTemplateRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDeviceProfileTemplateResponse : pb::IMessage<GetDeviceProfileTemplateResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDeviceProfileTemplateResponse> _parser = new pb::MessageParser<GetDeviceProfileTemplateResponse>(() => new GetDeviceProfileTemplateResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDeviceProfileTemplateResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileTemplateReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileTemplateResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileTemplateResponse(GetDeviceProfileTemplateResponse other) : this() {
+      deviceProfileTemplate_ = other.deviceProfileTemplate_ != null ? other.deviceProfileTemplate_.Clone() : null;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDeviceProfileTemplateResponse Clone() {
+      return new GetDeviceProfileTemplateResponse(this);
+    }
+
+    /// <summary>Field number for the "device_profile_template" field.</summary>
+    public const int DeviceProfileTemplateFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceProfileTemplate deviceProfileTemplate_;
+    /// <summary>
+    /// Device-profile template object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceProfileTemplate DeviceProfileTemplate {
+      get { return deviceProfileTemplate_; }
+      set {
+        deviceProfileTemplate_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDeviceProfileTemplateResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDeviceProfileTemplateResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceProfileTemplate, other.DeviceProfileTemplate)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceProfileTemplate_ != null) hash ^= DeviceProfileTemplate.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceProfileTemplate_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfileTemplate);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceProfileTemplate_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfileTemplate);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceProfileTemplate_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceProfileTemplate);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDeviceProfileTemplateResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceProfileTemplate_ != null) {
+        if (deviceProfileTemplate_ == null) {
+          DeviceProfileTemplate = new global::Chirpstack.Api.DeviceProfileTemplate();
+        }
+        DeviceProfileTemplate.MergeFrom(other.DeviceProfileTemplate);
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceProfileTemplate_ == null) {
+              DeviceProfileTemplate = new global::Chirpstack.Api.DeviceProfileTemplate();
+            }
+            input.ReadMessage(DeviceProfileTemplate);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceProfileTemplate_ == null) {
+              DeviceProfileTemplate = new global::Chirpstack.Api.DeviceProfileTemplate();
+            }
+            input.ReadMessage(DeviceProfileTemplate);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateDeviceProfileTemplateRequest : pb::IMessage<UpdateDeviceProfileTemplateRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateDeviceProfileTemplateRequest> _parser = new pb::MessageParser<UpdateDeviceProfileTemplateRequest>(() => new UpdateDeviceProfileTemplateRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateDeviceProfileTemplateRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileTemplateReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceProfileTemplateRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceProfileTemplateRequest(UpdateDeviceProfileTemplateRequest other) : this() {
+      deviceProfileTemplate_ = other.deviceProfileTemplate_ != null ? other.deviceProfileTemplate_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateDeviceProfileTemplateRequest Clone() {
+      return new UpdateDeviceProfileTemplateRequest(this);
+    }
+
+    /// <summary>Field number for the "device_profile_template" field.</summary>
+    public const int DeviceProfileTemplateFieldNumber = 1;
+    private global::Chirpstack.Api.DeviceProfileTemplate deviceProfileTemplate_;
+    /// <summary>
+    /// Object to update.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.DeviceProfileTemplate DeviceProfileTemplate {
+      get { return deviceProfileTemplate_; }
+      set {
+        deviceProfileTemplate_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateDeviceProfileTemplateRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateDeviceProfileTemplateRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(DeviceProfileTemplate, other.DeviceProfileTemplate)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (deviceProfileTemplate_ != null) hash ^= DeviceProfileTemplate.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (deviceProfileTemplate_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfileTemplate);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (deviceProfileTemplate_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(DeviceProfileTemplate);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (deviceProfileTemplate_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceProfileTemplate);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateDeviceProfileTemplateRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.deviceProfileTemplate_ != null) {
+        if (deviceProfileTemplate_ == null) {
+          DeviceProfileTemplate = new global::Chirpstack.Api.DeviceProfileTemplate();
+        }
+        DeviceProfileTemplate.MergeFrom(other.DeviceProfileTemplate);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (deviceProfileTemplate_ == null) {
+              DeviceProfileTemplate = new global::Chirpstack.Api.DeviceProfileTemplate();
+            }
+            input.ReadMessage(DeviceProfileTemplate);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (deviceProfileTemplate_ == null) {
+              DeviceProfileTemplate = new global::Chirpstack.Api.DeviceProfileTemplate();
+            }
+            input.ReadMessage(DeviceProfileTemplate);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteDeviceProfileTemplateRequest : pb::IMessage<DeleteDeviceProfileTemplateRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteDeviceProfileTemplateRequest> _parser = new pb::MessageParser<DeleteDeviceProfileTemplateRequest>(() => new DeleteDeviceProfileTemplateRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteDeviceProfileTemplateRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileTemplateReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceProfileTemplateRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceProfileTemplateRequest(DeleteDeviceProfileTemplateRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteDeviceProfileTemplateRequest Clone() {
+      return new DeleteDeviceProfileTemplateRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteDeviceProfileTemplateRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteDeviceProfileTemplateRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteDeviceProfileTemplateRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListDeviceProfileTemplatesRequest : pb::IMessage<ListDeviceProfileTemplatesRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListDeviceProfileTemplatesRequest> _parser = new pb::MessageParser<ListDeviceProfileTemplatesRequest>(() => new ListDeviceProfileTemplatesRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListDeviceProfileTemplatesRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileTemplateReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfileTemplatesRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfileTemplatesRequest(ListDeviceProfileTemplatesRequest other) : this() {
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfileTemplatesRequest Clone() {
+      return new ListDeviceProfileTemplatesRequest(this);
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 1;
+    private uint limit_;
+    /// <summary>
+    /// Max number of device-profile templates to return in the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 2;
+    private uint offset_;
+    /// <summary>
+    /// Offset in the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListDeviceProfileTemplatesRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListDeviceProfileTemplatesRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Limit != 0) hash ^= Limit.GetHashCode();
+      if (Offset != 0) hash ^= Offset.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Limit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Limit);
+      }
+      if (Offset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Offset);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListDeviceProfileTemplatesRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Limit != 0) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0) {
+        Offset = other.Offset;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListDeviceProfileTemplatesResponse : pb::IMessage<ListDeviceProfileTemplatesResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListDeviceProfileTemplatesResponse> _parser = new pb::MessageParser<ListDeviceProfileTemplatesResponse>(() => new ListDeviceProfileTemplatesResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListDeviceProfileTemplatesResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.DeviceProfileTemplateReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfileTemplatesResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfileTemplatesResponse(ListDeviceProfileTemplatesResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListDeviceProfileTemplatesResponse Clone() {
+      return new ListDeviceProfileTemplatesResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of device-profile templates.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.DeviceProfileTemplateListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.DeviceProfileTemplateListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.DeviceProfileTemplateListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.DeviceProfileTemplateListItem>();
+    /// <summary>
+    /// Result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.DeviceProfileTemplateListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListDeviceProfileTemplatesResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListDeviceProfileTemplatesResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListDeviceProfileTemplatesResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/DeviceProfileTemplateGrpc.cs b/api/csharp/Chirpstack/api/DeviceProfileTemplateGrpc.cs
new file mode 100644
index 00000000..d46eafe0
--- /dev/null
+++ b/api/csharp/Chirpstack/api/DeviceProfileTemplateGrpc.cs
@@ -0,0 +1,484 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/device_profile_template.proto
+// </auto-generated>
+#pragma warning disable 0414, 1591, 8981
+#region Designer generated code
+
+using grpc = global::Grpc.Core;
+
+namespace Chirpstack.Api {
+  /// <summary>
+  /// DeviceProfileTemplateService is the service providing API methods for managing device-profile templates.
+  /// </summary>
+  public static partial class DeviceProfileTemplateService
+  {
+    static readonly string __ServiceName = "api.DeviceProfileTemplateService";
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (message is global::Google.Protobuf.IBufferMessage)
+      {
+        context.SetPayloadLength(message.CalculateSize());
+        global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
+        context.Complete();
+        return;
+      }
+      #endif
+      context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static class __Helper_MessageCache<T>
+    {
+      public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (__Helper_MessageCache<T>.IsBufferMessage)
+      {
+        return parser.ParseFrom(context.PayloadAsReadOnlySequence());
+      }
+      #endif
+      return parser.ParseFrom(context.PayloadAsNewBuffer());
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateDeviceProfileTemplateRequest> __Marshaller_api_CreateDeviceProfileTemplateRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateDeviceProfileTemplateRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Google.Protobuf.WellKnownTypes.Empty> __Marshaller_google_protobuf_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Google.Protobuf.WellKnownTypes.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceProfileTemplateRequest> __Marshaller_api_GetDeviceProfileTemplateRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceProfileTemplateRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDeviceProfileTemplateResponse> __Marshaller_api_GetDeviceProfileTemplateResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDeviceProfileTemplateResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest> __Marshaller_api_UpdateDeviceProfileTemplateRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest> __Marshaller_api_DeleteDeviceProfileTemplateRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListDeviceProfileTemplatesRequest> __Marshaller_api_ListDeviceProfileTemplatesRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListDeviceProfileTemplatesRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListDeviceProfileTemplatesResponse> __Marshaller_api_ListDeviceProfileTemplatesResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListDeviceProfileTemplatesResponse.Parser));
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateDeviceProfileTemplateRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Create = new grpc::Method<global::Chirpstack.Api.CreateDeviceProfileTemplateRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Create",
+        __Marshaller_api_CreateDeviceProfileTemplateRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetDeviceProfileTemplateRequest, global::Chirpstack.Api.GetDeviceProfileTemplateResponse> __Method_Get = new grpc::Method<global::Chirpstack.Api.GetDeviceProfileTemplateRequest, global::Chirpstack.Api.GetDeviceProfileTemplateResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Get",
+        __Marshaller_api_GetDeviceProfileTemplateRequest,
+        __Marshaller_api_GetDeviceProfileTemplateResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Update = new grpc::Method<global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Update",
+        __Marshaller_api_UpdateDeviceProfileTemplateRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Delete = new grpc::Method<global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Delete",
+        __Marshaller_api_DeleteDeviceProfileTemplateRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListDeviceProfileTemplatesRequest, global::Chirpstack.Api.ListDeviceProfileTemplatesResponse> __Method_List = new grpc::Method<global::Chirpstack.Api.ListDeviceProfileTemplatesRequest, global::Chirpstack.Api.ListDeviceProfileTemplatesResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "List",
+        __Marshaller_api_ListDeviceProfileTemplatesRequest,
+        __Marshaller_api_ListDeviceProfileTemplatesResponse);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Chirpstack.Api.DeviceProfileTemplateReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of DeviceProfileTemplateService</summary>
+    [grpc::BindServiceMethod(typeof(DeviceProfileTemplateService), "BindService")]
+    public abstract partial class DeviceProfileTemplateServiceBase
+    {
+      /// <summary>
+      /// Create the given device-profile template.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Create(global::Chirpstack.Api.CreateDeviceProfileTemplateRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the device-profile template for the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetDeviceProfileTemplateResponse> Get(global::Chirpstack.Api.GetDeviceProfileTemplateRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update the given device-profile template.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Update(global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete the device-profile template with the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Delete(global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// List the available device-profile templates.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListDeviceProfileTemplatesResponse> List(global::Chirpstack.Api.ListDeviceProfileTemplatesRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for DeviceProfileTemplateService</summary>
+    public partial class DeviceProfileTemplateServiceClient : grpc::ClientBase<DeviceProfileTemplateServiceClient>
+    {
+      /// <summary>Creates a new client for DeviceProfileTemplateService</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public DeviceProfileTemplateServiceClient(grpc::ChannelBase channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for DeviceProfileTemplateService that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public DeviceProfileTemplateServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected DeviceProfileTemplateServiceClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected DeviceProfileTemplateServiceClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      /// Create the given device-profile template.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Create(global::Chirpstack.Api.CreateDeviceProfileTemplateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Create(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create the given device-profile template.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Create(global::Chirpstack.Api.CreateDeviceProfileTemplateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Create the given device-profile template.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateAsync(global::Chirpstack.Api.CreateDeviceProfileTemplateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create the given device-profile template.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateAsync(global::Chirpstack.Api.CreateDeviceProfileTemplateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Get the device-profile template for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceProfileTemplateResponse Get(global::Chirpstack.Api.GetDeviceProfileTemplateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Get(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the device-profile template for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDeviceProfileTemplateResponse Get(global::Chirpstack.Api.GetDeviceProfileTemplateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Get the device-profile template for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceProfileTemplateResponse> GetAsync(global::Chirpstack.Api.GetDeviceProfileTemplateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the device-profile template for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDeviceProfileTemplateResponse> GetAsync(global::Chirpstack.Api.GetDeviceProfileTemplateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Update the given device-profile template.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Update(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given device-profile template.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Update the given device-profile template.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given device-profile template.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Delete the device-profile template with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Delete(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the device-profile template with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Delete the device-profile template with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the device-profile template with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// List the available device-profile templates.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListDeviceProfileTemplatesResponse List(global::Chirpstack.Api.ListDeviceProfileTemplatesRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return List(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List the available device-profile templates.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListDeviceProfileTemplatesResponse List(global::Chirpstack.Api.ListDeviceProfileTemplatesRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// List the available device-profile templates.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListDeviceProfileTemplatesResponse> ListAsync(global::Chirpstack.Api.ListDeviceProfileTemplatesRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List the available device-profile templates.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListDeviceProfileTemplatesResponse> ListAsync(global::Chirpstack.Api.ListDeviceProfileTemplatesRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected override DeviceProfileTemplateServiceClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new DeviceProfileTemplateServiceClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static grpc::ServerServiceDefinition BindService(DeviceProfileTemplateServiceBase serviceImpl)
+    {
+      return grpc::ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_Create, serviceImpl.Create)
+          .AddMethod(__Method_Get, serviceImpl.Get)
+          .AddMethod(__Method_Update, serviceImpl.Update)
+          .AddMethod(__Method_Delete, serviceImpl.Delete)
+          .AddMethod(__Method_List, serviceImpl.List).Build();
+    }
+
+    /// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
+    /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+    /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static void BindService(grpc::ServiceBinderBase serviceBinder, DeviceProfileTemplateServiceBase serviceImpl)
+    {
+      serviceBinder.AddMethod(__Method_Create, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateDeviceProfileTemplateRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Create));
+      serviceBinder.AddMethod(__Method_Get, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetDeviceProfileTemplateRequest, global::Chirpstack.Api.GetDeviceProfileTemplateResponse>(serviceImpl.Get));
+      serviceBinder.AddMethod(__Method_Update, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateDeviceProfileTemplateRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Update));
+      serviceBinder.AddMethod(__Method_Delete, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteDeviceProfileTemplateRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Delete));
+      serviceBinder.AddMethod(__Method_List, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListDeviceProfileTemplatesRequest, global::Chirpstack.Api.ListDeviceProfileTemplatesResponse>(serviceImpl.List));
+    }
+
+  }
+}
+#endregion
diff --git a/api/csharp/Chirpstack/api/FrameLog.cs b/api/csharp/Chirpstack/api/FrameLog.cs
new file mode 100644
index 00000000..ebcebbb7
--- /dev/null
+++ b/api/csharp/Chirpstack/api/FrameLog.cs
@@ -0,0 +1,1067 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/frame_log.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/frame_log.proto</summary>
+  public static partial class FrameLogReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/frame_log.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static FrameLogReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChNhcGkvZnJhbWVfbG9nLnByb3RvEgNhcGkaH2dvb2dsZS9wcm90b2J1Zi90",
+            "aW1lc3RhbXAucHJvdG8aE2NvbW1vbi9jb21tb24ucHJvdG8aC2d3L2d3LnBy",
+            "b3RvIvcBCg5VcGxpbmtGcmFtZUxvZxITCgtwaHlfcGF5bG9hZBgBIAEoDBIh",
+            "Cgd0eF9pbmZvGAIgASgLMhAuZ3cuVXBsaW5rVHhJbmZvEiEKB3J4X2luZm8Y",
+            "AyADKAsyEC5ndy5VcGxpbmtSeEluZm8SHQoGbV90eXBlGAQgASgOMg0uY29t",
+            "bW9uLk1UeXBlEhAKCGRldl9hZGRyGAUgASgJEg8KB2Rldl9ldWkYBiABKAkS",
+            "KAoEdGltZRgHIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASHgoW",
+            "cGxhaW50ZXh0X21hY19jb21tYW5kcxgIIAEoCCKBAgoQRG93bmxpbmtGcmFt",
+            "ZUxvZxIoCgR0aW1lGAEgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFt",
+            "cBITCgtwaHlfcGF5bG9hZBgCIAEoDBIjCgd0eF9pbmZvGAMgASgLMhIuZ3cu",
+            "RG93bmxpbmtUeEluZm8SEwoLZG93bmxpbmtfaWQYBCABKA0SEgoKZ2F0ZXdh",
+            "eV9pZBgFIAEoCRIdCgZtX3R5cGUYBiABKA4yDS5jb21tb24uTVR5cGUSEAoI",
+            "ZGV2X2FkZHIYByABKAkSDwoHZGV2X2V1aRgIIAEoCRIeChZwbGFpbnRleHRf",
+            "bWFjX2NvbW1hbmRzGAkgASgIQmUKEWlvLmNoaXJwc3RhY2suYXBpQg1GcmFt",
+            "ZUxvZ1Byb3RvUAFaLmdpdGh1Yi5jb20vY2hpcnBzdGFjay9jaGlycHN0YWNr",
+            "L2FwaS9nby92NC9hcGmqAg5DaGlycHN0YWNrLkFwaWIGcHJvdG8z"));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Chirpstack.Common.CommonReflection.Descriptor, global::Chirpstack.Gateway.GwReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UplinkFrameLog), global::Chirpstack.Api.UplinkFrameLog.Parser, new[]{ "PhyPayload", "TxInfo", "RxInfo", "MType", "DevAddr", "DevEui", "Time", "PlaintextMacCommands" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DownlinkFrameLog), global::Chirpstack.Api.DownlinkFrameLog.Parser, new[]{ "Time", "PhyPayload", "TxInfo", "DownlinkId", "GatewayId", "MType", "DevAddr", "DevEui", "PlaintextMacCommands" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  public sealed partial class UplinkFrameLog : pb::IMessage<UplinkFrameLog>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UplinkFrameLog> _parser = new pb::MessageParser<UplinkFrameLog>(() => new UplinkFrameLog());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UplinkFrameLog> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.FrameLogReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkFrameLog() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkFrameLog(UplinkFrameLog other) : this() {
+      phyPayload_ = other.phyPayload_;
+      txInfo_ = other.txInfo_ != null ? other.txInfo_.Clone() : null;
+      rxInfo_ = other.rxInfo_.Clone();
+      mType_ = other.mType_;
+      devAddr_ = other.devAddr_;
+      devEui_ = other.devEui_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      plaintextMacCommands_ = other.plaintextMacCommands_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkFrameLog Clone() {
+      return new UplinkFrameLog(this);
+    }
+
+    /// <summary>Field number for the "phy_payload" field.</summary>
+    public const int PhyPayloadFieldNumber = 1;
+    private pb::ByteString phyPayload_ = pb::ByteString.Empty;
+    /// <summary>
+    /// PHYPayload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString PhyPayload {
+      get { return phyPayload_; }
+      set {
+        phyPayload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tx_info" field.</summary>
+    public const int TxInfoFieldNumber = 2;
+    private global::Chirpstack.Gateway.UplinkTxInfo txInfo_;
+    /// <summary>
+    /// TX meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.UplinkTxInfo TxInfo {
+      get { return txInfo_; }
+      set {
+        txInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_info" field.</summary>
+    public const int RxInfoFieldNumber = 3;
+    private static readonly pb::FieldCodec<global::Chirpstack.Gateway.UplinkRxInfo> _repeated_rxInfo_codec
+        = pb::FieldCodec.ForMessage(26, global::Chirpstack.Gateway.UplinkRxInfo.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo> rxInfo_ = new pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo>();
+    /// <summary>
+    /// RX meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo> RxInfo {
+      get { return rxInfo_; }
+    }
+
+    /// <summary>Field number for the "m_type" field.</summary>
+    public const int MTypeFieldNumber = 4;
+    private global::Chirpstack.Common.MType mType_ = global::Chirpstack.Common.MType.JoinRequest;
+    /// <summary>
+    /// Message type.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.MType MType {
+      get { return mType_; }
+      set {
+        mType_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "dev_addr" field.</summary>
+    public const int DevAddrFieldNumber = 5;
+    private string devAddr_ = "";
+    /// <summary>
+    /// Device address (optional).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevAddr {
+      get { return devAddr_; }
+      set {
+        devAddr_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 6;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (optional).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 7;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Time.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "plaintext_mac_commands" field.</summary>
+    public const int PlaintextMacCommandsFieldNumber = 8;
+    private bool plaintextMacCommands_;
+    /// <summary>
+    /// Plaintext mac-commands.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool PlaintextMacCommands {
+      get { return plaintextMacCommands_; }
+      set {
+        plaintextMacCommands_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UplinkFrameLog);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UplinkFrameLog other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (PhyPayload != other.PhyPayload) return false;
+      if (!object.Equals(TxInfo, other.TxInfo)) return false;
+      if(!rxInfo_.Equals(other.rxInfo_)) return false;
+      if (MType != other.MType) return false;
+      if (DevAddr != other.DevAddr) return false;
+      if (DevEui != other.DevEui) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (PlaintextMacCommands != other.PlaintextMacCommands) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (PhyPayload.Length != 0) hash ^= PhyPayload.GetHashCode();
+      if (txInfo_ != null) hash ^= TxInfo.GetHashCode();
+      hash ^= rxInfo_.GetHashCode();
+      if (MType != global::Chirpstack.Common.MType.JoinRequest) hash ^= MType.GetHashCode();
+      if (DevAddr.Length != 0) hash ^= DevAddr.GetHashCode();
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (PlaintextMacCommands != false) hash ^= PlaintextMacCommands.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (PhyPayload.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(PhyPayload);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxInfo);
+      }
+      rxInfo_.WriteTo(output, _repeated_rxInfo_codec);
+      if (MType != global::Chirpstack.Common.MType.JoinRequest) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) MType);
+      }
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(DevAddr);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(DevEui);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(58);
+        output.WriteMessage(Time);
+      }
+      if (PlaintextMacCommands != false) {
+        output.WriteRawTag(64);
+        output.WriteBool(PlaintextMacCommands);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (PhyPayload.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(PhyPayload);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxInfo);
+      }
+      rxInfo_.WriteTo(ref output, _repeated_rxInfo_codec);
+      if (MType != global::Chirpstack.Common.MType.JoinRequest) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) MType);
+      }
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(DevAddr);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(DevEui);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(58);
+        output.WriteMessage(Time);
+      }
+      if (PlaintextMacCommands != false) {
+        output.WriteRawTag(64);
+        output.WriteBool(PlaintextMacCommands);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (PhyPayload.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(PhyPayload);
+      }
+      if (txInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfo);
+      }
+      size += rxInfo_.CalculateSize(_repeated_rxInfo_codec);
+      if (MType != global::Chirpstack.Common.MType.JoinRequest) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) MType);
+      }
+      if (DevAddr.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevAddr);
+      }
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (PlaintextMacCommands != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UplinkFrameLog other) {
+      if (other == null) {
+        return;
+      }
+      if (other.PhyPayload.Length != 0) {
+        PhyPayload = other.PhyPayload;
+      }
+      if (other.txInfo_ != null) {
+        if (txInfo_ == null) {
+          TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+        }
+        TxInfo.MergeFrom(other.TxInfo);
+      }
+      rxInfo_.Add(other.rxInfo_);
+      if (other.MType != global::Chirpstack.Common.MType.JoinRequest) {
+        MType = other.MType;
+      }
+      if (other.DevAddr.Length != 0) {
+        DevAddr = other.DevAddr;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.PlaintextMacCommands != false) {
+        PlaintextMacCommands = other.PlaintextMacCommands;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            PhyPayload = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 26: {
+            rxInfo_.AddEntriesFrom(input, _repeated_rxInfo_codec);
+            break;
+          }
+          case 32: {
+            MType = (global::Chirpstack.Common.MType) input.ReadEnum();
+            break;
+          }
+          case 42: {
+            DevAddr = input.ReadString();
+            break;
+          }
+          case 50: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 58: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 64: {
+            PlaintextMacCommands = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            PhyPayload = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 26: {
+            rxInfo_.AddEntriesFrom(ref input, _repeated_rxInfo_codec);
+            break;
+          }
+          case 32: {
+            MType = (global::Chirpstack.Common.MType) input.ReadEnum();
+            break;
+          }
+          case 42: {
+            DevAddr = input.ReadString();
+            break;
+          }
+          case 50: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 58: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 64: {
+            PlaintextMacCommands = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DownlinkFrameLog : pb::IMessage<DownlinkFrameLog>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DownlinkFrameLog> _parser = new pb::MessageParser<DownlinkFrameLog>(() => new DownlinkFrameLog());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DownlinkFrameLog> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.FrameLogReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkFrameLog() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkFrameLog(DownlinkFrameLog other) : this() {
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      phyPayload_ = other.phyPayload_;
+      txInfo_ = other.txInfo_ != null ? other.txInfo_.Clone() : null;
+      downlinkId_ = other.downlinkId_;
+      gatewayId_ = other.gatewayId_;
+      mType_ = other.mType_;
+      devAddr_ = other.devAddr_;
+      devEui_ = other.devEui_;
+      plaintextMacCommands_ = other.plaintextMacCommands_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkFrameLog Clone() {
+      return new DownlinkFrameLog(this);
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 1;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Time.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "phy_payload" field.</summary>
+    public const int PhyPayloadFieldNumber = 2;
+    private pb::ByteString phyPayload_ = pb::ByteString.Empty;
+    /// <summary>
+    /// PHYPayload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString PhyPayload {
+      get { return phyPayload_; }
+      set {
+        phyPayload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tx_info" field.</summary>
+    public const int TxInfoFieldNumber = 3;
+    private global::Chirpstack.Gateway.DownlinkTxInfo txInfo_;
+    /// <summary>
+    /// TX meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.DownlinkTxInfo TxInfo {
+      get { return txInfo_; }
+      set {
+        txInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "downlink_id" field.</summary>
+    public const int DownlinkIdFieldNumber = 4;
+    private uint downlinkId_;
+    /// <summary>
+    /// Downlink ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint DownlinkId {
+      get { return downlinkId_; }
+      set {
+        downlinkId_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 5;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "m_type" field.</summary>
+    public const int MTypeFieldNumber = 6;
+    private global::Chirpstack.Common.MType mType_ = global::Chirpstack.Common.MType.JoinRequest;
+    /// <summary>
+    /// Message type.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.MType MType {
+      get { return mType_; }
+      set {
+        mType_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "dev_addr" field.</summary>
+    public const int DevAddrFieldNumber = 7;
+    private string devAddr_ = "";
+    /// <summary>
+    /// Device address (optional).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevAddr {
+      get { return devAddr_; }
+      set {
+        devAddr_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 8;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (optional).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "plaintext_mac_commands" field.</summary>
+    public const int PlaintextMacCommandsFieldNumber = 9;
+    private bool plaintextMacCommands_;
+    /// <summary>
+    /// Plaintext mac-commands.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool PlaintextMacCommands {
+      get { return plaintextMacCommands_; }
+      set {
+        plaintextMacCommands_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DownlinkFrameLog);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DownlinkFrameLog other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Time, other.Time)) return false;
+      if (PhyPayload != other.PhyPayload) return false;
+      if (!object.Equals(TxInfo, other.TxInfo)) return false;
+      if (DownlinkId != other.DownlinkId) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (MType != other.MType) return false;
+      if (DevAddr != other.DevAddr) return false;
+      if (DevEui != other.DevEui) return false;
+      if (PlaintextMacCommands != other.PlaintextMacCommands) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (PhyPayload.Length != 0) hash ^= PhyPayload.GetHashCode();
+      if (txInfo_ != null) hash ^= TxInfo.GetHashCode();
+      if (DownlinkId != 0) hash ^= DownlinkId.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (MType != global::Chirpstack.Common.MType.JoinRequest) hash ^= MType.GetHashCode();
+      if (DevAddr.Length != 0) hash ^= DevAddr.GetHashCode();
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (PlaintextMacCommands != false) hash ^= PlaintextMacCommands.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (time_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Time);
+      }
+      if (PhyPayload.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteBytes(PhyPayload);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(TxInfo);
+      }
+      if (DownlinkId != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(DownlinkId);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(GatewayId);
+      }
+      if (MType != global::Chirpstack.Common.MType.JoinRequest) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) MType);
+      }
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(DevAddr);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(DevEui);
+      }
+      if (PlaintextMacCommands != false) {
+        output.WriteRawTag(72);
+        output.WriteBool(PlaintextMacCommands);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (time_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Time);
+      }
+      if (PhyPayload.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteBytes(PhyPayload);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(TxInfo);
+      }
+      if (DownlinkId != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(DownlinkId);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(GatewayId);
+      }
+      if (MType != global::Chirpstack.Common.MType.JoinRequest) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) MType);
+      }
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(DevAddr);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(DevEui);
+      }
+      if (PlaintextMacCommands != false) {
+        output.WriteRawTag(72);
+        output.WriteBool(PlaintextMacCommands);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (PhyPayload.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(PhyPayload);
+      }
+      if (txInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfo);
+      }
+      if (DownlinkId != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(DownlinkId);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (MType != global::Chirpstack.Common.MType.JoinRequest) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) MType);
+      }
+      if (DevAddr.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevAddr);
+      }
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (PlaintextMacCommands != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DownlinkFrameLog other) {
+      if (other == null) {
+        return;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.PhyPayload.Length != 0) {
+        PhyPayload = other.PhyPayload;
+      }
+      if (other.txInfo_ != null) {
+        if (txInfo_ == null) {
+          TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+        }
+        TxInfo.MergeFrom(other.TxInfo);
+      }
+      if (other.DownlinkId != 0) {
+        DownlinkId = other.DownlinkId;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.MType != global::Chirpstack.Common.MType.JoinRequest) {
+        MType = other.MType;
+      }
+      if (other.DevAddr.Length != 0) {
+        DevAddr = other.DevAddr;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.PlaintextMacCommands != false) {
+        PlaintextMacCommands = other.PlaintextMacCommands;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 18: {
+            PhyPayload = input.ReadBytes();
+            break;
+          }
+          case 26: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 32: {
+            DownlinkId = input.ReadUInt32();
+            break;
+          }
+          case 42: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 48: {
+            MType = (global::Chirpstack.Common.MType) input.ReadEnum();
+            break;
+          }
+          case 58: {
+            DevAddr = input.ReadString();
+            break;
+          }
+          case 66: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 72: {
+            PlaintextMacCommands = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 18: {
+            PhyPayload = input.ReadBytes();
+            break;
+          }
+          case 26: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 32: {
+            DownlinkId = input.ReadUInt32();
+            break;
+          }
+          case 42: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 48: {
+            MType = (global::Chirpstack.Common.MType) input.ReadEnum();
+            break;
+          }
+          case 58: {
+            DevAddr = input.ReadString();
+            break;
+          }
+          case 66: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 72: {
+            PlaintextMacCommands = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/Gateway.cs b/api/csharp/Chirpstack/api/Gateway.cs
new file mode 100644
index 00000000..5353c800
--- /dev/null
+++ b/api/csharp/Chirpstack/api/Gateway.cs
@@ -0,0 +1,4227 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/gateway.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/gateway.proto</summary>
+  public static partial class GatewayReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/gateway.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static GatewayReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChFhcGkvZ2F0ZXdheS5wcm90bxIDYXBpGhxnb29nbGUvYXBpL2Fubm90YXRp",
+            "b25zLnByb3RvGh9nb29nbGUvcHJvdG9idWYvdGltZXN0YW1wLnByb3RvGhtn",
+            "b29nbGUvcHJvdG9idWYvZW1wdHkucHJvdG8aE2NvbW1vbi9jb21tb24ucHJv",
+            "dG8iwQIKB0dhdGV3YXkSEgoKZ2F0ZXdheV9pZBgBIAEoCRIMCgRuYW1lGAIg",
+            "ASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEiIKCGxvY2F0aW9uGAQgASgLMhAu",
+            "Y29tbW9uLkxvY2F0aW9uEhEKCXRlbmFudF9pZBgFIAEoCRIkCgR0YWdzGAYg",
+            "AygLMhYuYXBpLkdhdGV3YXkuVGFnc0VudHJ5EiwKCG1ldGFkYXRhGAcgAygL",
+            "MhouYXBpLkdhdGV3YXkuTWV0YWRhdGFFbnRyeRIWCg5zdGF0c19pbnRlcnZh",
+            "bBgIIAEoDRorCglUYWdzRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIg",
+            "ASgJOgI4ARovCg1NZXRhZGF0YUVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1",
+            "ZRgCIAEoCToCOAEioAMKD0dhdGV3YXlMaXN0SXRlbRIRCgl0ZW5hbnRfaWQY",
+            "ASABKAkSEgoKZ2F0ZXdheV9pZBgCIAEoCRIMCgRuYW1lGAMgASgJEhMKC2Rl",
+            "c2NyaXB0aW9uGAQgASgJEiIKCGxvY2F0aW9uGAUgASgLMhAuY29tbW9uLkxv",
+            "Y2F0aW9uEjgKCnByb3BlcnRpZXMYBiADKAsyJC5hcGkuR2F0ZXdheUxpc3RJ",
+            "dGVtLlByb3BlcnRpZXNFbnRyeRIuCgpjcmVhdGVkX2F0GAcgASgLMhouZ29v",
+            "Z2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVkX2F0GAggASgLMhou",
+            "Z29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIwCgxsYXN0X3NlZW5fYXQYCSAB",
+            "KAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEiAKBXN0YXRlGAogASgO",
+            "MhEuYXBpLkdhdGV3YXlTdGF0ZRoxCg9Qcm9wZXJ0aWVzRW50cnkSCwoDa2V5",
+            "GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASI1ChRDcmVhdGVHYXRld2F5UmVx",
+            "dWVzdBIdCgdnYXRld2F5GAEgASgLMgwuYXBpLkdhdGV3YXkiJwoRR2V0R2F0",
+            "ZXdheVJlcXVlc3QSEgoKZ2F0ZXdheV9pZBgBIAEoCSLFAQoSR2V0R2F0ZXdh",
+            "eVJlc3BvbnNlEh0KB2dhdGV3YXkYASABKAsyDC5hcGkuR2F0ZXdheRIuCgpj",
+            "cmVhdGVkX2F0GAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIu",
+            "Cgp1cGRhdGVkX2F0GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFt",
+            "cBIwCgxsYXN0X3NlZW5fYXQYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGlt",
+            "ZXN0YW1wIjUKFFVwZGF0ZUdhdGV3YXlSZXF1ZXN0Eh0KB2dhdGV3YXkYASAB",
+            "KAsyDC5hcGkuR2F0ZXdheSIqChREZWxldGVHYXRld2F5UmVxdWVzdBISCgpn",
+            "YXRld2F5X2lkGAEgASgJInMKE0xpc3RHYXRld2F5c1JlcXVlc3QSDQoFbGlt",
+            "aXQYASABKA0SDgoGb2Zmc2V0GAIgASgNEg4KBnNlYXJjaBgDIAEoCRIRCgl0",
+            "ZW5hbnRfaWQYBCABKAkSGgoSbXVsdGljYXN0X2dyb3VwX2lkGAUgASgJIlEK",
+            "FExpc3RHYXRld2F5c1Jlc3BvbnNlEhMKC3RvdGFsX2NvdW50GAEgASgNEiQK",
+            "BnJlc3VsdBgCIAMoCzIULmFwaS5HYXRld2F5TGlzdEl0ZW0iPQonR2VuZXJh",
+            "dGVHYXRld2F5Q2xpZW50Q2VydGlmaWNhdGVSZXF1ZXN0EhIKCmdhdGV3YXlf",
+            "aWQYASABKAkijgEKKEdlbmVyYXRlR2F0ZXdheUNsaWVudENlcnRpZmljYXRl",
+            "UmVzcG9uc2USEAoIdGxzX2NlcnQYASABKAkSDwoHdGxzX2tleRgCIAEoCRIP",
+            "CgdjYV9jZXJ0GAMgASgJEi4KCmV4cGlyZXNfYXQYBCABKAsyGi5nb29nbGUu",
+            "cHJvdG9idWYuVGltZXN0YW1wIqwBChhHZXRHYXRld2F5TWV0cmljc1JlcXVl",
+            "c3QSEgoKZ2F0ZXdheV9pZBgBIAEoCRIpCgVzdGFydBgCIAEoCzIaLmdvb2ds",
+            "ZS5wcm90b2J1Zi5UaW1lc3RhbXASJwoDZW5kGAMgASgLMhouZ29vZ2xlLnBy",
+            "b3RvYnVmLlRpbWVzdGFtcBIoCgthZ2dyZWdhdGlvbhgEIAEoDjITLmNvbW1v",
+            "bi5BZ2dyZWdhdGlvbiLCAgoZR2V0R2F0ZXdheU1ldHJpY3NSZXNwb25zZRIi",
+            "CgpyeF9wYWNrZXRzGAEgASgLMg4uY29tbW9uLk1ldHJpYxIiCgp0eF9wYWNr",
+            "ZXRzGAIgASgLMg4uY29tbW9uLk1ldHJpYxIrChN0eF9wYWNrZXRzX3Blcl9m",
+            "cmVxGAMgASgLMg4uY29tbW9uLk1ldHJpYxIrChNyeF9wYWNrZXRzX3Blcl9m",
+            "cmVxGAQgASgLMg4uY29tbW9uLk1ldHJpYxIpChF0eF9wYWNrZXRzX3Blcl9k",
+            "chgFIAEoCzIOLmNvbW1vbi5NZXRyaWMSKQoRcnhfcGFja2V0c19wZXJfZHIY",
+            "BiABKAsyDi5jb21tb24uTWV0cmljEi0KFXR4X3BhY2tldHNfcGVyX3N0YXR1",
+            "cxgHIAEoCzIOLmNvbW1vbi5NZXRyaWMqNwoMR2F0ZXdheVN0YXRlEg4KCk5F",
+            "VkVSX1NFRU4QABIKCgZPTkxJTkUQARILCgdPRkZMSU5FEAIykQYKDkdhdGV3",
+            "YXlTZXJ2aWNlElUKBkNyZWF0ZRIZLmFwaS5DcmVhdGVHYXRld2F5UmVxdWVz",
+            "dBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIYgtPkkwISIg0vYXBpL2dhdGV3",
+            "YXlzOgEqEloKA0dldBIWLmFwaS5HZXRHYXRld2F5UmVxdWVzdBoXLmFwaS5H",
+            "ZXRHYXRld2F5UmVzcG9uc2UiIoLT5JMCHBIaL2FwaS9nYXRld2F5cy97Z2F0",
+            "ZXdheV9pZH0SagoGVXBkYXRlEhkuYXBpLlVwZGF0ZUdhdGV3YXlSZXF1ZXN0",
+            "GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5Ii2C0+STAicaIi9hcGkvZ2F0ZXdh",
+            "eXMve2dhdGV3YXkuZ2F0ZXdheV9pZH06ASoSXwoGRGVsZXRlEhkuYXBpLkRl",
+            "bGV0ZUdhdGV3YXlSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IiKC",
+            "0+STAhwqGi9hcGkvZ2F0ZXdheXMve2dhdGV3YXlfaWR9ElIKBExpc3QSGC5h",
+            "cGkuTGlzdEdhdGV3YXlzUmVxdWVzdBoZLmFwaS5MaXN0R2F0ZXdheXNSZXNw",
+            "b25zZSIVgtPkkwIPEg0vYXBpL2dhdGV3YXlzErEBChlHZW5lcmF0ZUNsaWVu",
+            "dENlcnRpZmljYXRlEiwuYXBpLkdlbmVyYXRlR2F0ZXdheUNsaWVudENlcnRp",
+            "ZmljYXRlUmVxdWVzdBotLmFwaS5HZW5lcmF0ZUdhdGV3YXlDbGllbnRDZXJ0",
+            "aWZpY2F0ZVJlc3BvbnNlIjeC0+STAjEiLy9hcGkvZ2F0ZXdheXMve2dhdGV3",
+            "YXlfaWR9L2dlbmVyYXRlLWNlcnRpZmljYXRlEncKCkdldE1ldHJpY3MSHS5h",
+            "cGkuR2V0R2F0ZXdheU1ldHJpY3NSZXF1ZXN0Gh4uYXBpLkdldEdhdGV3YXlN",
+            "ZXRyaWNzUmVzcG9uc2UiKoLT5JMCJBIiL2FwaS9nYXRld2F5cy97Z2F0ZXdh",
+            "eV9pZH0vbWV0cmljc0JkChFpby5jaGlycHN0YWNrLmFwaUIMR2F0ZXdheVBy",
+            "b3RvUAFaLmdpdGh1Yi5jb20vY2hpcnBzdGFjay9jaGlycHN0YWNrL2FwaS9n",
+            "by92NC9hcGmqAg5DaGlycHN0YWNrLkFwaWIGcHJvdG8z"));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Api.AnnotationsReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Chirpstack.Common.CommonReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Chirpstack.Api.GatewayState), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.Gateway), global::Chirpstack.Api.Gateway.Parser, new[]{ "GatewayId", "Name", "Description", "Location", "TenantId", "Tags", "Metadata", "StatsInterval" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GatewayListItem), global::Chirpstack.Api.GatewayListItem.Parser, new[]{ "TenantId", "GatewayId", "Name", "Description", "Location", "Properties", "CreatedAt", "UpdatedAt", "LastSeenAt", "State" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateGatewayRequest), global::Chirpstack.Api.CreateGatewayRequest.Parser, new[]{ "Gateway" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetGatewayRequest), global::Chirpstack.Api.GetGatewayRequest.Parser, new[]{ "GatewayId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetGatewayResponse), global::Chirpstack.Api.GetGatewayResponse.Parser, new[]{ "Gateway", "CreatedAt", "UpdatedAt", "LastSeenAt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateGatewayRequest), global::Chirpstack.Api.UpdateGatewayRequest.Parser, new[]{ "Gateway" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteGatewayRequest), global::Chirpstack.Api.DeleteGatewayRequest.Parser, new[]{ "GatewayId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListGatewaysRequest), global::Chirpstack.Api.ListGatewaysRequest.Parser, new[]{ "Limit", "Offset", "Search", "TenantId", "MulticastGroupId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListGatewaysResponse), global::Chirpstack.Api.ListGatewaysResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GenerateGatewayClientCertificateRequest), global::Chirpstack.Api.GenerateGatewayClientCertificateRequest.Parser, new[]{ "GatewayId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GenerateGatewayClientCertificateResponse), global::Chirpstack.Api.GenerateGatewayClientCertificateResponse.Parser, new[]{ "TlsCert", "TlsKey", "CaCert", "ExpiresAt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetGatewayMetricsRequest), global::Chirpstack.Api.GetGatewayMetricsRequest.Parser, new[]{ "GatewayId", "Start", "End", "Aggregation" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetGatewayMetricsResponse), global::Chirpstack.Api.GetGatewayMetricsResponse.Parser, new[]{ "RxPackets", "TxPackets", "TxPacketsPerFreq", "RxPacketsPerFreq", "TxPacketsPerDr", "RxPacketsPerDr", "TxPacketsPerStatus" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Enums
+  public enum GatewayState {
+    /// <summary>
+    /// The gateway has never sent any stats.
+    /// </summary>
+    [pbr::OriginalName("NEVER_SEEN")] NeverSeen = 0,
+    /// <summary>
+    /// Online.
+    /// </summary>
+    [pbr::OriginalName("ONLINE")] Online = 1,
+    /// <summary>
+    /// Offline.
+    /// </summary>
+    [pbr::OriginalName("OFFLINE")] Offline = 2,
+  }
+
+  #endregion
+
+  #region Messages
+  public sealed partial class Gateway : pb::IMessage<Gateway>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<Gateway> _parser = new pb::MessageParser<Gateway>(() => new Gateway());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<Gateway> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Gateway() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Gateway(Gateway other) : this() {
+      gatewayId_ = other.gatewayId_;
+      name_ = other.name_;
+      description_ = other.description_;
+      location_ = other.location_ != null ? other.location_.Clone() : null;
+      tenantId_ = other.tenantId_;
+      tags_ = other.tags_.Clone();
+      metadata_ = other.metadata_.Clone();
+      statsInterval_ = other.statsInterval_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Gateway Clone() {
+      return new Gateway(this);
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 1;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 2;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 3;
+    private string description_ = "";
+    /// <summary>
+    /// Description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "location" field.</summary>
+    public const int LocationFieldNumber = 4;
+    private global::Chirpstack.Common.Location location_;
+    /// <summary>
+    /// Gateway location.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Location Location {
+      get { return location_; }
+      set {
+        location_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 5;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tags" field.</summary>
+    public const int TagsFieldNumber = 6;
+    private static readonly pbc::MapField<string, string>.Codec _map_tags_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 50);
+    private readonly pbc::MapField<string, string> tags_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Tags.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Tags {
+      get { return tags_; }
+    }
+
+    /// <summary>Field number for the "metadata" field.</summary>
+    public const int MetadataFieldNumber = 7;
+    private static readonly pbc::MapField<string, string>.Codec _map_metadata_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 58);
+    private readonly pbc::MapField<string, string> metadata_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Metadata (provided by the gateway).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Metadata {
+      get { return metadata_; }
+    }
+
+    /// <summary>Field number for the "stats_interval" field.</summary>
+    public const int StatsIntervalFieldNumber = 8;
+    private uint statsInterval_;
+    /// <summary>
+    /// Stats interval (seconds).
+    /// This defines the expected interval in which the gateway sends its
+    /// statistics.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint StatsInterval {
+      get { return statsInterval_; }
+      set {
+        statsInterval_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as Gateway);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(Gateway other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayId != other.GatewayId) return false;
+      if (Name != other.Name) return false;
+      if (Description != other.Description) return false;
+      if (!object.Equals(Location, other.Location)) return false;
+      if (TenantId != other.TenantId) return false;
+      if (!Tags.Equals(other.Tags)) return false;
+      if (!Metadata.Equals(other.Metadata)) return false;
+      if (StatsInterval != other.StatsInterval) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (location_ != null) hash ^= Location.GetHashCode();
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      hash ^= Tags.GetHashCode();
+      hash ^= Metadata.GetHashCode();
+      if (StatsInterval != 0) hash ^= StatsInterval.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(Location);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(TenantId);
+      }
+      tags_.WriteTo(output, _map_tags_codec);
+      metadata_.WriteTo(output, _map_metadata_codec);
+      if (StatsInterval != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(StatsInterval);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(Location);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(TenantId);
+      }
+      tags_.WriteTo(ref output, _map_tags_codec);
+      metadata_.WriteTo(ref output, _map_metadata_codec);
+      if (StatsInterval != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(StatsInterval);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (location_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Location);
+      }
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      size += tags_.CalculateSize(_map_tags_codec);
+      size += metadata_.CalculateSize(_map_metadata_codec);
+      if (StatsInterval != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(StatsInterval);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(Gateway other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      if (other.location_ != null) {
+        if (location_ == null) {
+          Location = new global::Chirpstack.Common.Location();
+        }
+        Location.MergeFrom(other.Location);
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      tags_.Add(other.tags_);
+      metadata_.Add(other.metadata_);
+      if (other.StatsInterval != 0) {
+        StatsInterval = other.StatsInterval;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 34: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+          case 42: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 50: {
+            tags_.AddEntriesFrom(input, _map_tags_codec);
+            break;
+          }
+          case 58: {
+            metadata_.AddEntriesFrom(input, _map_metadata_codec);
+            break;
+          }
+          case 64: {
+            StatsInterval = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 34: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+          case 42: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 50: {
+            tags_.AddEntriesFrom(ref input, _map_tags_codec);
+            break;
+          }
+          case 58: {
+            metadata_.AddEntriesFrom(ref input, _map_metadata_codec);
+            break;
+          }
+          case 64: {
+            StatsInterval = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GatewayListItem : pb::IMessage<GatewayListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GatewayListItem> _parser = new pb::MessageParser<GatewayListItem>(() => new GatewayListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GatewayListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayListItem(GatewayListItem other) : this() {
+      tenantId_ = other.tenantId_;
+      gatewayId_ = other.gatewayId_;
+      name_ = other.name_;
+      description_ = other.description_;
+      location_ = other.location_ != null ? other.location_.Clone() : null;
+      properties_ = other.properties_.Clone();
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      lastSeenAt_ = other.lastSeenAt_ != null ? other.lastSeenAt_.Clone() : null;
+      state_ = other.state_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayListItem Clone() {
+      return new GatewayListItem(this);
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 1;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 2;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 3;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 4;
+    private string description_ = "";
+    /// <summary>
+    /// Description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "location" field.</summary>
+    public const int LocationFieldNumber = 5;
+    private global::Chirpstack.Common.Location location_;
+    /// <summary>
+    /// Location.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Location Location {
+      get { return location_; }
+      set {
+        location_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "properties" field.</summary>
+    public const int PropertiesFieldNumber = 6;
+    private static readonly pbc::MapField<string, string>.Codec _map_properties_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 50);
+    private readonly pbc::MapField<string, string> properties_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Gateway properties.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Properties {
+      get { return properties_; }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 7;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 8;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "last_seen_at" field.</summary>
+    public const int LastSeenAtFieldNumber = 9;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp lastSeenAt_;
+    /// <summary>
+    /// Last seen at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp LastSeenAt {
+      get { return lastSeenAt_; }
+      set {
+        lastSeenAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "state" field.</summary>
+    public const int StateFieldNumber = 10;
+    private global::Chirpstack.Api.GatewayState state_ = global::Chirpstack.Api.GatewayState.NeverSeen;
+    /// <summary>
+    /// Gateway state.
+    /// Please note that the state of the gateway is driven by the stats
+    /// packages that are sent by the gateway.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.GatewayState State {
+      get { return state_; }
+      set {
+        state_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GatewayListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GatewayListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TenantId != other.TenantId) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (Name != other.Name) return false;
+      if (Description != other.Description) return false;
+      if (!object.Equals(Location, other.Location)) return false;
+      if (!Properties.Equals(other.Properties)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (!object.Equals(LastSeenAt, other.LastSeenAt)) return false;
+      if (State != other.State) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (location_ != null) hash ^= Location.GetHashCode();
+      hash ^= Properties.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (lastSeenAt_ != null) hash ^= LastSeenAt.GetHashCode();
+      if (State != global::Chirpstack.Api.GatewayState.NeverSeen) hash ^= State.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(GatewayId);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Description);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(Location);
+      }
+      properties_.WriteTo(output, _map_properties_codec);
+      if (createdAt_ != null) {
+        output.WriteRawTag(58);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(66);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        output.WriteRawTag(74);
+        output.WriteMessage(LastSeenAt);
+      }
+      if (State != global::Chirpstack.Api.GatewayState.NeverSeen) {
+        output.WriteRawTag(80);
+        output.WriteEnum((int) State);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(GatewayId);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Description);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(Location);
+      }
+      properties_.WriteTo(ref output, _map_properties_codec);
+      if (createdAt_ != null) {
+        output.WriteRawTag(58);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(66);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        output.WriteRawTag(74);
+        output.WriteMessage(LastSeenAt);
+      }
+      if (State != global::Chirpstack.Api.GatewayState.NeverSeen) {
+        output.WriteRawTag(80);
+        output.WriteEnum((int) State);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (location_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Location);
+      }
+      size += properties_.CalculateSize(_map_properties_codec);
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LastSeenAt);
+      }
+      if (State != global::Chirpstack.Api.GatewayState.NeverSeen) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) State);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GatewayListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      if (other.location_ != null) {
+        if (location_ == null) {
+          Location = new global::Chirpstack.Common.Location();
+        }
+        Location.MergeFrom(other.Location);
+      }
+      properties_.Add(other.properties_);
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.lastSeenAt_ != null) {
+        if (lastSeenAt_ == null) {
+          LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        LastSeenAt.MergeFrom(other.LastSeenAt);
+      }
+      if (other.State != global::Chirpstack.Api.GatewayState.NeverSeen) {
+        State = other.State;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 26: {
+            Name = input.ReadString();
+            break;
+          }
+          case 34: {
+            Description = input.ReadString();
+            break;
+          }
+          case 42: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+          case 50: {
+            properties_.AddEntriesFrom(input, _map_properties_codec);
+            break;
+          }
+          case 58: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 66: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 74: {
+            if (lastSeenAt_ == null) {
+              LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(LastSeenAt);
+            break;
+          }
+          case 80: {
+            State = (global::Chirpstack.Api.GatewayState) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 26: {
+            Name = input.ReadString();
+            break;
+          }
+          case 34: {
+            Description = input.ReadString();
+            break;
+          }
+          case 42: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+          case 50: {
+            properties_.AddEntriesFrom(ref input, _map_properties_codec);
+            break;
+          }
+          case 58: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 66: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 74: {
+            if (lastSeenAt_ == null) {
+              LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(LastSeenAt);
+            break;
+          }
+          case 80: {
+            State = (global::Chirpstack.Api.GatewayState) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateGatewayRequest : pb::IMessage<CreateGatewayRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateGatewayRequest> _parser = new pb::MessageParser<CreateGatewayRequest>(() => new CreateGatewayRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateGatewayRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateGatewayRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateGatewayRequest(CreateGatewayRequest other) : this() {
+      gateway_ = other.gateway_ != null ? other.gateway_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateGatewayRequest Clone() {
+      return new CreateGatewayRequest(this);
+    }
+
+    /// <summary>Field number for the "gateway" field.</summary>
+    public const int GatewayFieldNumber = 1;
+    private global::Chirpstack.Api.Gateway gateway_;
+    /// <summary>
+    /// Gateway object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Gateway Gateway {
+      get { return gateway_; }
+      set {
+        gateway_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateGatewayRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateGatewayRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Gateway, other.Gateway)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (gateway_ != null) hash ^= Gateway.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (gateway_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Gateway);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (gateway_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Gateway);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (gateway_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Gateway);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateGatewayRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.gateway_ != null) {
+        if (gateway_ == null) {
+          Gateway = new global::Chirpstack.Api.Gateway();
+        }
+        Gateway.MergeFrom(other.Gateway);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (gateway_ == null) {
+              Gateway = new global::Chirpstack.Api.Gateway();
+            }
+            input.ReadMessage(Gateway);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (gateway_ == null) {
+              Gateway = new global::Chirpstack.Api.Gateway();
+            }
+            input.ReadMessage(Gateway);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetGatewayRequest : pb::IMessage<GetGatewayRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetGatewayRequest> _parser = new pb::MessageParser<GetGatewayRequest>(() => new GetGatewayRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetGatewayRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayRequest(GetGatewayRequest other) : this() {
+      gatewayId_ = other.gatewayId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayRequest Clone() {
+      return new GetGatewayRequest(this);
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 1;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetGatewayRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetGatewayRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayId != other.GatewayId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetGatewayRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetGatewayResponse : pb::IMessage<GetGatewayResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetGatewayResponse> _parser = new pb::MessageParser<GetGatewayResponse>(() => new GetGatewayResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetGatewayResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayResponse(GetGatewayResponse other) : this() {
+      gateway_ = other.gateway_ != null ? other.gateway_.Clone() : null;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      lastSeenAt_ = other.lastSeenAt_ != null ? other.lastSeenAt_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayResponse Clone() {
+      return new GetGatewayResponse(this);
+    }
+
+    /// <summary>Field number for the "gateway" field.</summary>
+    public const int GatewayFieldNumber = 1;
+    private global::Chirpstack.Api.Gateway gateway_;
+    /// <summary>
+    /// Gateway object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Gateway Gateway {
+      get { return gateway_; }
+      set {
+        gateway_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "last_seen_at" field.</summary>
+    public const int LastSeenAtFieldNumber = 4;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp lastSeenAt_;
+    /// <summary>
+    /// Last seen at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp LastSeenAt {
+      get { return lastSeenAt_; }
+      set {
+        lastSeenAt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetGatewayResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetGatewayResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Gateway, other.Gateway)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (!object.Equals(LastSeenAt, other.LastSeenAt)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (gateway_ != null) hash ^= Gateway.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (lastSeenAt_ != null) hash ^= LastSeenAt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (gateway_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Gateway);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(LastSeenAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (gateway_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Gateway);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(LastSeenAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (gateway_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Gateway);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (lastSeenAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LastSeenAt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetGatewayResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.gateway_ != null) {
+        if (gateway_ == null) {
+          Gateway = new global::Chirpstack.Api.Gateway();
+        }
+        Gateway.MergeFrom(other.Gateway);
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.lastSeenAt_ != null) {
+        if (lastSeenAt_ == null) {
+          LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        LastSeenAt.MergeFrom(other.LastSeenAt);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (gateway_ == null) {
+              Gateway = new global::Chirpstack.Api.Gateway();
+            }
+            input.ReadMessage(Gateway);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            if (lastSeenAt_ == null) {
+              LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(LastSeenAt);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (gateway_ == null) {
+              Gateway = new global::Chirpstack.Api.Gateway();
+            }
+            input.ReadMessage(Gateway);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            if (lastSeenAt_ == null) {
+              LastSeenAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(LastSeenAt);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateGatewayRequest : pb::IMessage<UpdateGatewayRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateGatewayRequest> _parser = new pb::MessageParser<UpdateGatewayRequest>(() => new UpdateGatewayRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateGatewayRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateGatewayRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateGatewayRequest(UpdateGatewayRequest other) : this() {
+      gateway_ = other.gateway_ != null ? other.gateway_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateGatewayRequest Clone() {
+      return new UpdateGatewayRequest(this);
+    }
+
+    /// <summary>Field number for the "gateway" field.</summary>
+    public const int GatewayFieldNumber = 1;
+    private global::Chirpstack.Api.Gateway gateway_;
+    /// <summary>
+    /// Gateway object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Gateway Gateway {
+      get { return gateway_; }
+      set {
+        gateway_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateGatewayRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateGatewayRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Gateway, other.Gateway)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (gateway_ != null) hash ^= Gateway.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (gateway_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Gateway);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (gateway_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Gateway);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (gateway_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Gateway);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateGatewayRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.gateway_ != null) {
+        if (gateway_ == null) {
+          Gateway = new global::Chirpstack.Api.Gateway();
+        }
+        Gateway.MergeFrom(other.Gateway);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (gateway_ == null) {
+              Gateway = new global::Chirpstack.Api.Gateway();
+            }
+            input.ReadMessage(Gateway);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (gateway_ == null) {
+              Gateway = new global::Chirpstack.Api.Gateway();
+            }
+            input.ReadMessage(Gateway);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteGatewayRequest : pb::IMessage<DeleteGatewayRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteGatewayRequest> _parser = new pb::MessageParser<DeleteGatewayRequest>(() => new DeleteGatewayRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteGatewayRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteGatewayRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteGatewayRequest(DeleteGatewayRequest other) : this() {
+      gatewayId_ = other.gatewayId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteGatewayRequest Clone() {
+      return new DeleteGatewayRequest(this);
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 1;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteGatewayRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteGatewayRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayId != other.GatewayId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteGatewayRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListGatewaysRequest : pb::IMessage<ListGatewaysRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListGatewaysRequest> _parser = new pb::MessageParser<ListGatewaysRequest>(() => new ListGatewaysRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListGatewaysRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListGatewaysRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListGatewaysRequest(ListGatewaysRequest other) : this() {
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      search_ = other.search_;
+      tenantId_ = other.tenantId_;
+      multicastGroupId_ = other.multicastGroupId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListGatewaysRequest Clone() {
+      return new ListGatewaysRequest(this);
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 1;
+    private uint limit_;
+    /// <summary>
+    /// Max number of gateways to return in the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 2;
+    private uint offset_;
+    /// <summary>
+    /// Offset in the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "search" field.</summary>
+    public const int SearchFieldNumber = 3;
+    private string search_ = "";
+    /// <summary>
+    /// If set, the given string will be used to search on name (optional).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Search {
+      get { return search_; }
+      set {
+        search_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 4;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID) to filter gateways on.
+    /// To list all gateways as a global admin user, this field can be left blank.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "multicast_group_id" field.</summary>
+    public const int MulticastGroupIdFieldNumber = 5;
+    private string multicastGroupId_ = "";
+    /// <summary>
+    /// Multicast-group ID (UUID) to filter gateways on.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string MulticastGroupId {
+      get { return multicastGroupId_; }
+      set {
+        multicastGroupId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListGatewaysRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListGatewaysRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      if (Search != other.Search) return false;
+      if (TenantId != other.TenantId) return false;
+      if (MulticastGroupId != other.MulticastGroupId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Limit != 0) hash ^= Limit.GetHashCode();
+      if (Offset != 0) hash ^= Offset.GetHashCode();
+      if (Search.Length != 0) hash ^= Search.GetHashCode();
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (MulticastGroupId.Length != 0) hash ^= MulticastGroupId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Limit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Limit);
+      }
+      if (Offset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Offset);
+      }
+      if (Search.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Search);
+      }
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (MulticastGroupId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListGatewaysRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Limit != 0) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0) {
+        Offset = other.Offset;
+      }
+      if (other.Search.Length != 0) {
+        Search = other.Search;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.MulticastGroupId.Length != 0) {
+        MulticastGroupId = other.MulticastGroupId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 42: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 42: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListGatewaysResponse : pb::IMessage<ListGatewaysResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListGatewaysResponse> _parser = new pb::MessageParser<ListGatewaysResponse>(() => new ListGatewaysResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListGatewaysResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListGatewaysResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListGatewaysResponse(ListGatewaysResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListGatewaysResponse Clone() {
+      return new ListGatewaysResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of gateways.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.GatewayListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.GatewayListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.GatewayListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.GatewayListItem>();
+    /// <summary>
+    /// Result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.GatewayListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListGatewaysResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListGatewaysResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListGatewaysResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GenerateGatewayClientCertificateRequest : pb::IMessage<GenerateGatewayClientCertificateRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GenerateGatewayClientCertificateRequest> _parser = new pb::MessageParser<GenerateGatewayClientCertificateRequest>(() => new GenerateGatewayClientCertificateRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GenerateGatewayClientCertificateRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateGatewayClientCertificateRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateGatewayClientCertificateRequest(GenerateGatewayClientCertificateRequest other) : this() {
+      gatewayId_ = other.gatewayId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateGatewayClientCertificateRequest Clone() {
+      return new GenerateGatewayClientCertificateRequest(this);
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 1;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GenerateGatewayClientCertificateRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GenerateGatewayClientCertificateRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayId != other.GatewayId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GenerateGatewayClientCertificateRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GenerateGatewayClientCertificateResponse : pb::IMessage<GenerateGatewayClientCertificateResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GenerateGatewayClientCertificateResponse> _parser = new pb::MessageParser<GenerateGatewayClientCertificateResponse>(() => new GenerateGatewayClientCertificateResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GenerateGatewayClientCertificateResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[10]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateGatewayClientCertificateResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateGatewayClientCertificateResponse(GenerateGatewayClientCertificateResponse other) : this() {
+      tlsCert_ = other.tlsCert_;
+      tlsKey_ = other.tlsKey_;
+      caCert_ = other.caCert_;
+      expiresAt_ = other.expiresAt_ != null ? other.expiresAt_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GenerateGatewayClientCertificateResponse Clone() {
+      return new GenerateGatewayClientCertificateResponse(this);
+    }
+
+    /// <summary>Field number for the "tls_cert" field.</summary>
+    public const int TlsCertFieldNumber = 1;
+    private string tlsCert_ = "";
+    /// <summary>
+    /// TLS certificate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TlsCert {
+      get { return tlsCert_; }
+      set {
+        tlsCert_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tls_key" field.</summary>
+    public const int TlsKeyFieldNumber = 2;
+    private string tlsKey_ = "";
+    /// <summary>
+    /// TLS key.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TlsKey {
+      get { return tlsKey_; }
+      set {
+        tlsKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "ca_cert" field.</summary>
+    public const int CaCertFieldNumber = 3;
+    private string caCert_ = "";
+    /// <summary>
+    /// CA certificate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string CaCert {
+      get { return caCert_; }
+      set {
+        caCert_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "expires_at" field.</summary>
+    public const int ExpiresAtFieldNumber = 4;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp expiresAt_;
+    /// <summary>
+    /// Expires at defines the expiration date of the certificate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp ExpiresAt {
+      get { return expiresAt_; }
+      set {
+        expiresAt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GenerateGatewayClientCertificateResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GenerateGatewayClientCertificateResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TlsCert != other.TlsCert) return false;
+      if (TlsKey != other.TlsKey) return false;
+      if (CaCert != other.CaCert) return false;
+      if (!object.Equals(ExpiresAt, other.ExpiresAt)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TlsCert.Length != 0) hash ^= TlsCert.GetHashCode();
+      if (TlsKey.Length != 0) hash ^= TlsKey.GetHashCode();
+      if (CaCert.Length != 0) hash ^= CaCert.GetHashCode();
+      if (expiresAt_ != null) hash ^= ExpiresAt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TlsCert.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TlsCert);
+      }
+      if (TlsKey.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(TlsKey);
+      }
+      if (CaCert.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(CaCert);
+      }
+      if (expiresAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(ExpiresAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TlsCert.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TlsCert);
+      }
+      if (TlsKey.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(TlsKey);
+      }
+      if (CaCert.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(CaCert);
+      }
+      if (expiresAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(ExpiresAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TlsCert.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TlsCert);
+      }
+      if (TlsKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TlsKey);
+      }
+      if (CaCert.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(CaCert);
+      }
+      if (expiresAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ExpiresAt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GenerateGatewayClientCertificateResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TlsCert.Length != 0) {
+        TlsCert = other.TlsCert;
+      }
+      if (other.TlsKey.Length != 0) {
+        TlsKey = other.TlsKey;
+      }
+      if (other.CaCert.Length != 0) {
+        CaCert = other.CaCert;
+      }
+      if (other.expiresAt_ != null) {
+        if (expiresAt_ == null) {
+          ExpiresAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        ExpiresAt.MergeFrom(other.ExpiresAt);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TlsCert = input.ReadString();
+            break;
+          }
+          case 18: {
+            TlsKey = input.ReadString();
+            break;
+          }
+          case 26: {
+            CaCert = input.ReadString();
+            break;
+          }
+          case 34: {
+            if (expiresAt_ == null) {
+              ExpiresAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(ExpiresAt);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TlsCert = input.ReadString();
+            break;
+          }
+          case 18: {
+            TlsKey = input.ReadString();
+            break;
+          }
+          case 26: {
+            CaCert = input.ReadString();
+            break;
+          }
+          case 34: {
+            if (expiresAt_ == null) {
+              ExpiresAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(ExpiresAt);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetGatewayMetricsRequest : pb::IMessage<GetGatewayMetricsRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetGatewayMetricsRequest> _parser = new pb::MessageParser<GetGatewayMetricsRequest>(() => new GetGatewayMetricsRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetGatewayMetricsRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[11]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayMetricsRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayMetricsRequest(GetGatewayMetricsRequest other) : this() {
+      gatewayId_ = other.gatewayId_;
+      start_ = other.start_ != null ? other.start_.Clone() : null;
+      end_ = other.end_ != null ? other.end_.Clone() : null;
+      aggregation_ = other.aggregation_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayMetricsRequest Clone() {
+      return new GetGatewayMetricsRequest(this);
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 1;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "start" field.</summary>
+    public const int StartFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp start_;
+    /// <summary>
+    /// Interval start timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Start {
+      get { return start_; }
+      set {
+        start_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "end" field.</summary>
+    public const int EndFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp end_;
+    /// <summary>
+    /// Interval end timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp End {
+      get { return end_; }
+      set {
+        end_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "aggregation" field.</summary>
+    public const int AggregationFieldNumber = 4;
+    private global::Chirpstack.Common.Aggregation aggregation_ = global::Chirpstack.Common.Aggregation.Hour;
+    /// <summary>
+    /// Aggregation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Aggregation Aggregation {
+      get { return aggregation_; }
+      set {
+        aggregation_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetGatewayMetricsRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetGatewayMetricsRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayId != other.GatewayId) return false;
+      if (!object.Equals(Start, other.Start)) return false;
+      if (!object.Equals(End, other.End)) return false;
+      if (Aggregation != other.Aggregation) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (start_ != null) hash ^= Start.GetHashCode();
+      if (end_ != null) hash ^= End.GetHashCode();
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) hash ^= Aggregation.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (start_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Start);
+      }
+      if (end_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(End);
+      }
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Aggregation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (start_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Start);
+      }
+      if (end_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(End);
+      }
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Aggregation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (start_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Start);
+      }
+      if (end_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(End);
+      }
+      if (Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Aggregation);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetGatewayMetricsRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.start_ != null) {
+        if (start_ == null) {
+          Start = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Start.MergeFrom(other.Start);
+      }
+      if (other.end_ != null) {
+        if (end_ == null) {
+          End = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        End.MergeFrom(other.End);
+      }
+      if (other.Aggregation != global::Chirpstack.Common.Aggregation.Hour) {
+        Aggregation = other.Aggregation;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (start_ == null) {
+              Start = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Start);
+            break;
+          }
+          case 26: {
+            if (end_ == null) {
+              End = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(End);
+            break;
+          }
+          case 32: {
+            Aggregation = (global::Chirpstack.Common.Aggregation) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (start_ == null) {
+              Start = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Start);
+            break;
+          }
+          case 26: {
+            if (end_ == null) {
+              End = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(End);
+            break;
+          }
+          case 32: {
+            Aggregation = (global::Chirpstack.Common.Aggregation) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetGatewayMetricsResponse : pb::IMessage<GetGatewayMetricsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetGatewayMetricsResponse> _parser = new pb::MessageParser<GetGatewayMetricsResponse>(() => new GetGatewayMetricsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetGatewayMetricsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.MessageTypes[12]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayMetricsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayMetricsResponse(GetGatewayMetricsResponse other) : this() {
+      rxPackets_ = other.rxPackets_ != null ? other.rxPackets_.Clone() : null;
+      txPackets_ = other.txPackets_ != null ? other.txPackets_.Clone() : null;
+      txPacketsPerFreq_ = other.txPacketsPerFreq_ != null ? other.txPacketsPerFreq_.Clone() : null;
+      rxPacketsPerFreq_ = other.rxPacketsPerFreq_ != null ? other.rxPacketsPerFreq_.Clone() : null;
+      txPacketsPerDr_ = other.txPacketsPerDr_ != null ? other.txPacketsPerDr_.Clone() : null;
+      rxPacketsPerDr_ = other.rxPacketsPerDr_ != null ? other.rxPacketsPerDr_.Clone() : null;
+      txPacketsPerStatus_ = other.txPacketsPerStatus_ != null ? other.txPacketsPerStatus_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewayMetricsResponse Clone() {
+      return new GetGatewayMetricsResponse(this);
+    }
+
+    /// <summary>Field number for the "rx_packets" field.</summary>
+    public const int RxPacketsFieldNumber = 1;
+    private global::Chirpstack.Common.Metric rxPackets_;
+    /// <summary>
+    /// RX packets.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric RxPackets {
+      get { return rxPackets_; }
+      set {
+        rxPackets_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tx_packets" field.</summary>
+    public const int TxPacketsFieldNumber = 2;
+    private global::Chirpstack.Common.Metric txPackets_;
+    /// <summary>
+    /// TX packets.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric TxPackets {
+      get { return txPackets_; }
+      set {
+        txPackets_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tx_packets_per_freq" field.</summary>
+    public const int TxPacketsPerFreqFieldNumber = 3;
+    private global::Chirpstack.Common.Metric txPacketsPerFreq_;
+    /// <summary>
+    /// TX packets / frequency.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric TxPacketsPerFreq {
+      get { return txPacketsPerFreq_; }
+      set {
+        txPacketsPerFreq_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_packets_per_freq" field.</summary>
+    public const int RxPacketsPerFreqFieldNumber = 4;
+    private global::Chirpstack.Common.Metric rxPacketsPerFreq_;
+    /// <summary>
+    /// RX packets / frequency.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric RxPacketsPerFreq {
+      get { return rxPacketsPerFreq_; }
+      set {
+        rxPacketsPerFreq_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tx_packets_per_dr" field.</summary>
+    public const int TxPacketsPerDrFieldNumber = 5;
+    private global::Chirpstack.Common.Metric txPacketsPerDr_;
+    /// <summary>
+    /// TX packets / DR.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric TxPacketsPerDr {
+      get { return txPacketsPerDr_; }
+      set {
+        txPacketsPerDr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_packets_per_dr" field.</summary>
+    public const int RxPacketsPerDrFieldNumber = 6;
+    private global::Chirpstack.Common.Metric rxPacketsPerDr_;
+    /// <summary>
+    /// RX packets / DR.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric RxPacketsPerDr {
+      get { return rxPacketsPerDr_; }
+      set {
+        rxPacketsPerDr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tx_packets_per_status" field.</summary>
+    public const int TxPacketsPerStatusFieldNumber = 7;
+    private global::Chirpstack.Common.Metric txPacketsPerStatus_;
+    /// <summary>
+    /// TX packets per status.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Metric TxPacketsPerStatus {
+      get { return txPacketsPerStatus_; }
+      set {
+        txPacketsPerStatus_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetGatewayMetricsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetGatewayMetricsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(RxPackets, other.RxPackets)) return false;
+      if (!object.Equals(TxPackets, other.TxPackets)) return false;
+      if (!object.Equals(TxPacketsPerFreq, other.TxPacketsPerFreq)) return false;
+      if (!object.Equals(RxPacketsPerFreq, other.RxPacketsPerFreq)) return false;
+      if (!object.Equals(TxPacketsPerDr, other.TxPacketsPerDr)) return false;
+      if (!object.Equals(RxPacketsPerDr, other.RxPacketsPerDr)) return false;
+      if (!object.Equals(TxPacketsPerStatus, other.TxPacketsPerStatus)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (rxPackets_ != null) hash ^= RxPackets.GetHashCode();
+      if (txPackets_ != null) hash ^= TxPackets.GetHashCode();
+      if (txPacketsPerFreq_ != null) hash ^= TxPacketsPerFreq.GetHashCode();
+      if (rxPacketsPerFreq_ != null) hash ^= RxPacketsPerFreq.GetHashCode();
+      if (txPacketsPerDr_ != null) hash ^= TxPacketsPerDr.GetHashCode();
+      if (rxPacketsPerDr_ != null) hash ^= RxPacketsPerDr.GetHashCode();
+      if (txPacketsPerStatus_ != null) hash ^= TxPacketsPerStatus.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (rxPackets_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(RxPackets);
+      }
+      if (txPackets_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxPackets);
+      }
+      if (txPacketsPerFreq_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(TxPacketsPerFreq);
+      }
+      if (rxPacketsPerFreq_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(RxPacketsPerFreq);
+      }
+      if (txPacketsPerDr_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(TxPacketsPerDr);
+      }
+      if (rxPacketsPerDr_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(RxPacketsPerDr);
+      }
+      if (txPacketsPerStatus_ != null) {
+        output.WriteRawTag(58);
+        output.WriteMessage(TxPacketsPerStatus);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (rxPackets_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(RxPackets);
+      }
+      if (txPackets_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxPackets);
+      }
+      if (txPacketsPerFreq_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(TxPacketsPerFreq);
+      }
+      if (rxPacketsPerFreq_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(RxPacketsPerFreq);
+      }
+      if (txPacketsPerDr_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(TxPacketsPerDr);
+      }
+      if (rxPacketsPerDr_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(RxPacketsPerDr);
+      }
+      if (txPacketsPerStatus_ != null) {
+        output.WriteRawTag(58);
+        output.WriteMessage(TxPacketsPerStatus);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (rxPackets_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(RxPackets);
+      }
+      if (txPackets_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxPackets);
+      }
+      if (txPacketsPerFreq_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxPacketsPerFreq);
+      }
+      if (rxPacketsPerFreq_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(RxPacketsPerFreq);
+      }
+      if (txPacketsPerDr_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxPacketsPerDr);
+      }
+      if (rxPacketsPerDr_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(RxPacketsPerDr);
+      }
+      if (txPacketsPerStatus_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxPacketsPerStatus);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetGatewayMetricsResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.rxPackets_ != null) {
+        if (rxPackets_ == null) {
+          RxPackets = new global::Chirpstack.Common.Metric();
+        }
+        RxPackets.MergeFrom(other.RxPackets);
+      }
+      if (other.txPackets_ != null) {
+        if (txPackets_ == null) {
+          TxPackets = new global::Chirpstack.Common.Metric();
+        }
+        TxPackets.MergeFrom(other.TxPackets);
+      }
+      if (other.txPacketsPerFreq_ != null) {
+        if (txPacketsPerFreq_ == null) {
+          TxPacketsPerFreq = new global::Chirpstack.Common.Metric();
+        }
+        TxPacketsPerFreq.MergeFrom(other.TxPacketsPerFreq);
+      }
+      if (other.rxPacketsPerFreq_ != null) {
+        if (rxPacketsPerFreq_ == null) {
+          RxPacketsPerFreq = new global::Chirpstack.Common.Metric();
+        }
+        RxPacketsPerFreq.MergeFrom(other.RxPacketsPerFreq);
+      }
+      if (other.txPacketsPerDr_ != null) {
+        if (txPacketsPerDr_ == null) {
+          TxPacketsPerDr = new global::Chirpstack.Common.Metric();
+        }
+        TxPacketsPerDr.MergeFrom(other.TxPacketsPerDr);
+      }
+      if (other.rxPacketsPerDr_ != null) {
+        if (rxPacketsPerDr_ == null) {
+          RxPacketsPerDr = new global::Chirpstack.Common.Metric();
+        }
+        RxPacketsPerDr.MergeFrom(other.RxPacketsPerDr);
+      }
+      if (other.txPacketsPerStatus_ != null) {
+        if (txPacketsPerStatus_ == null) {
+          TxPacketsPerStatus = new global::Chirpstack.Common.Metric();
+        }
+        TxPacketsPerStatus.MergeFrom(other.TxPacketsPerStatus);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (rxPackets_ == null) {
+              RxPackets = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPackets);
+            break;
+          }
+          case 18: {
+            if (txPackets_ == null) {
+              TxPackets = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(TxPackets);
+            break;
+          }
+          case 26: {
+            if (txPacketsPerFreq_ == null) {
+              TxPacketsPerFreq = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(TxPacketsPerFreq);
+            break;
+          }
+          case 34: {
+            if (rxPacketsPerFreq_ == null) {
+              RxPacketsPerFreq = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPacketsPerFreq);
+            break;
+          }
+          case 42: {
+            if (txPacketsPerDr_ == null) {
+              TxPacketsPerDr = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(TxPacketsPerDr);
+            break;
+          }
+          case 50: {
+            if (rxPacketsPerDr_ == null) {
+              RxPacketsPerDr = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPacketsPerDr);
+            break;
+          }
+          case 58: {
+            if (txPacketsPerStatus_ == null) {
+              TxPacketsPerStatus = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(TxPacketsPerStatus);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (rxPackets_ == null) {
+              RxPackets = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPackets);
+            break;
+          }
+          case 18: {
+            if (txPackets_ == null) {
+              TxPackets = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(TxPackets);
+            break;
+          }
+          case 26: {
+            if (txPacketsPerFreq_ == null) {
+              TxPacketsPerFreq = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(TxPacketsPerFreq);
+            break;
+          }
+          case 34: {
+            if (rxPacketsPerFreq_ == null) {
+              RxPacketsPerFreq = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPacketsPerFreq);
+            break;
+          }
+          case 42: {
+            if (txPacketsPerDr_ == null) {
+              TxPacketsPerDr = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(TxPacketsPerDr);
+            break;
+          }
+          case 50: {
+            if (rxPacketsPerDr_ == null) {
+              RxPacketsPerDr = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(RxPacketsPerDr);
+            break;
+          }
+          case 58: {
+            if (txPacketsPerStatus_ == null) {
+              TxPacketsPerStatus = new global::Chirpstack.Common.Metric();
+            }
+            input.ReadMessage(TxPacketsPerStatus);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/GatewayGrpc.cs b/api/csharp/Chirpstack/api/GatewayGrpc.cs
new file mode 100644
index 00000000..1e5d5b61
--- /dev/null
+++ b/api/csharp/Chirpstack/api/GatewayGrpc.cs
@@ -0,0 +1,632 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/gateway.proto
+// </auto-generated>
+#pragma warning disable 0414, 1591, 8981
+#region Designer generated code
+
+using grpc = global::Grpc.Core;
+
+namespace Chirpstack.Api {
+  /// <summary>
+  /// GatewayService is the service providing API methods for managing gateways.
+  /// </summary>
+  public static partial class GatewayService
+  {
+    static readonly string __ServiceName = "api.GatewayService";
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (message is global::Google.Protobuf.IBufferMessage)
+      {
+        context.SetPayloadLength(message.CalculateSize());
+        global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
+        context.Complete();
+        return;
+      }
+      #endif
+      context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static class __Helper_MessageCache<T>
+    {
+      public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (__Helper_MessageCache<T>.IsBufferMessage)
+      {
+        return parser.ParseFrom(context.PayloadAsReadOnlySequence());
+      }
+      #endif
+      return parser.ParseFrom(context.PayloadAsNewBuffer());
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateGatewayRequest> __Marshaller_api_CreateGatewayRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateGatewayRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Google.Protobuf.WellKnownTypes.Empty> __Marshaller_google_protobuf_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Google.Protobuf.WellKnownTypes.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetGatewayRequest> __Marshaller_api_GetGatewayRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetGatewayRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetGatewayResponse> __Marshaller_api_GetGatewayResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetGatewayResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateGatewayRequest> __Marshaller_api_UpdateGatewayRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateGatewayRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteGatewayRequest> __Marshaller_api_DeleteGatewayRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteGatewayRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListGatewaysRequest> __Marshaller_api_ListGatewaysRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListGatewaysRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListGatewaysResponse> __Marshaller_api_ListGatewaysResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListGatewaysResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GenerateGatewayClientCertificateRequest> __Marshaller_api_GenerateGatewayClientCertificateRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GenerateGatewayClientCertificateRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GenerateGatewayClientCertificateResponse> __Marshaller_api_GenerateGatewayClientCertificateResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GenerateGatewayClientCertificateResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetGatewayMetricsRequest> __Marshaller_api_GetGatewayMetricsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetGatewayMetricsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetGatewayMetricsResponse> __Marshaller_api_GetGatewayMetricsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetGatewayMetricsResponse.Parser));
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateGatewayRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Create = new grpc::Method<global::Chirpstack.Api.CreateGatewayRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Create",
+        __Marshaller_api_CreateGatewayRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetGatewayRequest, global::Chirpstack.Api.GetGatewayResponse> __Method_Get = new grpc::Method<global::Chirpstack.Api.GetGatewayRequest, global::Chirpstack.Api.GetGatewayResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Get",
+        __Marshaller_api_GetGatewayRequest,
+        __Marshaller_api_GetGatewayResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateGatewayRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Update = new grpc::Method<global::Chirpstack.Api.UpdateGatewayRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Update",
+        __Marshaller_api_UpdateGatewayRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteGatewayRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Delete = new grpc::Method<global::Chirpstack.Api.DeleteGatewayRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Delete",
+        __Marshaller_api_DeleteGatewayRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListGatewaysRequest, global::Chirpstack.Api.ListGatewaysResponse> __Method_List = new grpc::Method<global::Chirpstack.Api.ListGatewaysRequest, global::Chirpstack.Api.ListGatewaysResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "List",
+        __Marshaller_api_ListGatewaysRequest,
+        __Marshaller_api_ListGatewaysResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GenerateGatewayClientCertificateRequest, global::Chirpstack.Api.GenerateGatewayClientCertificateResponse> __Method_GenerateClientCertificate = new grpc::Method<global::Chirpstack.Api.GenerateGatewayClientCertificateRequest, global::Chirpstack.Api.GenerateGatewayClientCertificateResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GenerateClientCertificate",
+        __Marshaller_api_GenerateGatewayClientCertificateRequest,
+        __Marshaller_api_GenerateGatewayClientCertificateResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetGatewayMetricsRequest, global::Chirpstack.Api.GetGatewayMetricsResponse> __Method_GetMetrics = new grpc::Method<global::Chirpstack.Api.GetGatewayMetricsRequest, global::Chirpstack.Api.GetGatewayMetricsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetMetrics",
+        __Marshaller_api_GetGatewayMetricsRequest,
+        __Marshaller_api_GetGatewayMetricsResponse);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Chirpstack.Api.GatewayReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of GatewayService</summary>
+    [grpc::BindServiceMethod(typeof(GatewayService), "BindService")]
+    public abstract partial class GatewayServiceBase
+    {
+      /// <summary>
+      /// Create creates the given gateway.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Create(global::Chirpstack.Api.CreateGatewayRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get returns the gateway for the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetGatewayResponse> Get(global::Chirpstack.Api.GetGatewayRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update updates the given gateway.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Update(global::Chirpstack.Api.UpdateGatewayRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete deletes the gateway matching the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Delete(global::Chirpstack.Api.DeleteGatewayRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the list of gateways.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListGatewaysResponse> List(global::Chirpstack.Api.ListGatewaysRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Generate client-certificate for the gateway.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GenerateGatewayClientCertificateResponse> GenerateClientCertificate(global::Chirpstack.Api.GenerateGatewayClientCertificateRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// GetMetrics returns the gateway metrics.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetGatewayMetricsResponse> GetMetrics(global::Chirpstack.Api.GetGatewayMetricsRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for GatewayService</summary>
+    public partial class GatewayServiceClient : grpc::ClientBase<GatewayServiceClient>
+    {
+      /// <summary>Creates a new client for GatewayService</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public GatewayServiceClient(grpc::ChannelBase channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for GatewayService that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public GatewayServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected GatewayServiceClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected GatewayServiceClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      /// Create creates the given gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Create(global::Chirpstack.Api.CreateGatewayRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Create(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create creates the given gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Create(global::Chirpstack.Api.CreateGatewayRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Create creates the given gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateAsync(global::Chirpstack.Api.CreateGatewayRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create creates the given gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> CreateAsync(global::Chirpstack.Api.CreateGatewayRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Get returns the gateway for the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetGatewayResponse Get(global::Chirpstack.Api.GetGatewayRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Get(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get returns the gateway for the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetGatewayResponse Get(global::Chirpstack.Api.GetGatewayRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Get returns the gateway for the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetGatewayResponse> GetAsync(global::Chirpstack.Api.GetGatewayRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get returns the gateway for the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetGatewayResponse> GetAsync(global::Chirpstack.Api.GetGatewayRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Update updates the given gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateGatewayRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Update(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update updates the given gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateGatewayRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Update updates the given gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateGatewayRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update updates the given gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateGatewayRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Delete deletes the gateway matching the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteGatewayRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Delete(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete deletes the gateway matching the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteGatewayRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Delete deletes the gateway matching the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteGatewayRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete deletes the gateway matching the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteGatewayRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of gateways.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListGatewaysResponse List(global::Chirpstack.Api.ListGatewaysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return List(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of gateways.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListGatewaysResponse List(global::Chirpstack.Api.ListGatewaysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of gateways.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListGatewaysResponse> ListAsync(global::Chirpstack.Api.ListGatewaysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of gateways.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListGatewaysResponse> ListAsync(global::Chirpstack.Api.ListGatewaysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// Generate client-certificate for the gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GenerateGatewayClientCertificateResponse GenerateClientCertificate(global::Chirpstack.Api.GenerateGatewayClientCertificateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GenerateClientCertificate(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Generate client-certificate for the gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GenerateGatewayClientCertificateResponse GenerateClientCertificate(global::Chirpstack.Api.GenerateGatewayClientCertificateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GenerateClientCertificate, null, options, request);
+      }
+      /// <summary>
+      /// Generate client-certificate for the gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GenerateGatewayClientCertificateResponse> GenerateClientCertificateAsync(global::Chirpstack.Api.GenerateGatewayClientCertificateRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GenerateClientCertificateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Generate client-certificate for the gateway.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GenerateGatewayClientCertificateResponse> GenerateClientCertificateAsync(global::Chirpstack.Api.GenerateGatewayClientCertificateRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GenerateClientCertificate, null, options, request);
+      }
+      /// <summary>
+      /// GetMetrics returns the gateway metrics.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetGatewayMetricsResponse GetMetrics(global::Chirpstack.Api.GetGatewayMetricsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetMetrics(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetMetrics returns the gateway metrics.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetGatewayMetricsResponse GetMetrics(global::Chirpstack.Api.GetGatewayMetricsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetMetrics, null, options, request);
+      }
+      /// <summary>
+      /// GetMetrics returns the gateway metrics.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetGatewayMetricsResponse> GetMetricsAsync(global::Chirpstack.Api.GetGatewayMetricsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetMetricsAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetMetrics returns the gateway metrics.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetGatewayMetricsResponse> GetMetricsAsync(global::Chirpstack.Api.GetGatewayMetricsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetMetrics, null, options, request);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected override GatewayServiceClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new GatewayServiceClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static grpc::ServerServiceDefinition BindService(GatewayServiceBase serviceImpl)
+    {
+      return grpc::ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_Create, serviceImpl.Create)
+          .AddMethod(__Method_Get, serviceImpl.Get)
+          .AddMethod(__Method_Update, serviceImpl.Update)
+          .AddMethod(__Method_Delete, serviceImpl.Delete)
+          .AddMethod(__Method_List, serviceImpl.List)
+          .AddMethod(__Method_GenerateClientCertificate, serviceImpl.GenerateClientCertificate)
+          .AddMethod(__Method_GetMetrics, serviceImpl.GetMetrics).Build();
+    }
+
+    /// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
+    /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+    /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static void BindService(grpc::ServiceBinderBase serviceBinder, GatewayServiceBase serviceImpl)
+    {
+      serviceBinder.AddMethod(__Method_Create, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateGatewayRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Create));
+      serviceBinder.AddMethod(__Method_Get, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetGatewayRequest, global::Chirpstack.Api.GetGatewayResponse>(serviceImpl.Get));
+      serviceBinder.AddMethod(__Method_Update, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateGatewayRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Update));
+      serviceBinder.AddMethod(__Method_Delete, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteGatewayRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Delete));
+      serviceBinder.AddMethod(__Method_List, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListGatewaysRequest, global::Chirpstack.Api.ListGatewaysResponse>(serviceImpl.List));
+      serviceBinder.AddMethod(__Method_GenerateClientCertificate, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GenerateGatewayClientCertificateRequest, global::Chirpstack.Api.GenerateGatewayClientCertificateResponse>(serviceImpl.GenerateClientCertificate));
+      serviceBinder.AddMethod(__Method_GetMetrics, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetGatewayMetricsRequest, global::Chirpstack.Api.GetGatewayMetricsResponse>(serviceImpl.GetMetrics));
+    }
+
+  }
+}
+#endregion
diff --git a/api/csharp/Chirpstack/api/Internal.cs b/api/csharp/Chirpstack/api/Internal.cs
new file mode 100644
index 00000000..2acad822
--- /dev/null
+++ b/api/csharp/Chirpstack/api/Internal.cs
@@ -0,0 +1,8018 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/internal.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/internal.proto</summary>
+  public static partial class InternalReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/internal.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static InternalReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChJhcGkvaW50ZXJuYWwucHJvdG8SA2FwaRofZ29vZ2xlL3Byb3RvYnVmL3Rp",
+            "bWVzdGFtcC5wcm90bxobZ29vZ2xlL3Byb3RvYnVmL2VtcHR5LnByb3RvGhNj",
+            "b21tb24vY29tbW9uLnByb3RvGg5hcGkvdXNlci5wcm90byJHCgZBcGlLZXkS",
+            "CgoCaWQYASABKAkSDAoEbmFtZRgCIAEoCRIQCghpc19hZG1pbhgDIAEoCBIR",
+            "Cgl0ZW5hbnRfaWQYBCABKAkiMwoTQ3JlYXRlQXBpS2V5UmVxdWVzdBIcCgdh",
+            "cGlfa2V5GAEgASgLMgsuYXBpLkFwaUtleSIxChRDcmVhdGVBcGlLZXlSZXNw",
+            "b25zZRIKCgJpZBgBIAEoCRINCgV0b2tlbhgCIAEoCSIhChNEZWxldGVBcGlL",
+            "ZXlSZXF1ZXN0EgoKAmlkGAEgASgJIlgKEkxpc3RBcGlLZXlzUmVxdWVzdBIN",
+            "CgVsaW1pdBgBIAEoDRIOCgZvZmZzZXQYAiABKA0SEAoIaXNfYWRtaW4YAyAB",
+            "KAgSEQoJdGVuYW50X2lkGAQgASgJIkcKE0xpc3RBcGlLZXlzUmVzcG9uc2US",
+            "EwoLdG90YWxfY291bnQYASABKA0SGwoGcmVzdWx0GAIgAygLMgsuYXBpLkFw",
+            "aUtleSLIAQoOVXNlclRlbmFudExpbmsSLgoKY3JlYXRlZF9hdBgBIAEoCzIa",
+            "Lmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLgoKdXBkYXRlZF9hdBgCIAEo",
+            "CzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASEQoJdGVuYW50X2lkGAMg",
+            "ASgJEhAKCGlzX2FkbWluGAQgASgIEhcKD2lzX2RldmljZV9hZG1pbhgFIAEo",
+            "CBIYChBpc19nYXRld2F5X2FkbWluGAYgASgIIi8KDExvZ2luUmVxdWVzdBIN",
+            "CgVlbWFpbBgBIAEoCRIQCghwYXNzd29yZBgCIAEoCSIcCg1Mb2dpblJlc3Bv",
+            "bnNlEgsKA2p3dBgBIAEoCSJQCg9Qcm9maWxlUmVzcG9uc2USFwoEdXNlchgB",
+            "IAEoCzIJLmFwaS5Vc2VyEiQKB3RlbmFudHMYAyADKAsyEy5hcGkuVXNlclRl",
+            "bmFudExpbmsiRAoTR2xvYmFsU2VhcmNoUmVxdWVzdBIOCgZzZWFyY2gYASAB",
+            "KAkSDQoFbGltaXQYAiABKAMSDgoGb2Zmc2V0GAMgASgDIj8KFEdsb2JhbFNl",
+            "YXJjaFJlc3BvbnNlEicKBnJlc3VsdBgBIAMoCzIXLmFwaS5HbG9iYWxTZWFy",
+            "Y2hSZXN1bHQi4gEKEkdsb2JhbFNlYXJjaFJlc3VsdBIMCgRraW5kGAEgASgJ",
+            "Eg0KBXNjb3JlGAIgASgCEhEKCXRlbmFudF9pZBgDIAEoCRITCgt0ZW5hbnRf",
+            "bmFtZRgEIAEoCRIWCg5hcHBsaWNhdGlvbl9pZBgFIAEoCRIYChBhcHBsaWNh",
+            "dGlvbl9uYW1lGAYgASgJEhYKDmRldmljZV9kZXZfZXVpGAcgASgJEhMKC2Rl",
+            "dmljZV9uYW1lGAggASgJEhIKCmdhdGV3YXlfaWQYCSABKAkSFAoMZ2F0ZXdh",
+            "eV9uYW1lGAogASgJIj4KEFNldHRpbmdzUmVzcG9uc2USKgoOb3BlbmlkX2Nv",
+            "bm5lY3QYASABKAsyEi5hcGkuT3BlbklkQ29ubmVjdCJxCg1PcGVuSWRDb25u",
+            "ZWN0Eg8KB2VuYWJsZWQYASABKAgSGwoJbG9naW5fdXJsGAIgASgJUghsb2dp",
+            "blVSTBITCgtsb2dpbl9sYWJlbBgDIAEoCRIdCgpsb2dvdXRfdXJsGAQgASgJ",
+            "Uglsb2dvdXRVUkwiOAoZT3BlbklkQ29ubmVjdExvZ2luUmVxdWVzdBIMCgRj",
+            "b2RlGAEgASgJEg0KBXN0YXRlGAIgASgJIisKGk9wZW5JZENvbm5lY3RMb2dp",
+            "blJlc3BvbnNlEg0KBXRva2VuGAEgASgJIi0KGEdldERldmljZXNTdW1tYXJ5",
+            "UmVxdWVzdBIRCgl0ZW5hbnRfaWQYASABKAki0gEKGUdldERldmljZXNTdW1t",
+            "YXJ5UmVzcG9uc2USFAoMYWN0aXZlX2NvdW50GAEgASgNEhYKDmluYWN0aXZl",
+            "X2NvdW50GAIgASgNEj0KCGRyX2NvdW50GAMgAygLMisuYXBpLkdldERldmlj",
+            "ZXNTdW1tYXJ5UmVzcG9uc2UuRHJDb3VudEVudHJ5EhgKEG5ldmVyX3NlZW5f",
+            "Y291bnQYBCABKA0aLgoMRHJDb3VudEVudHJ5EgsKA2tleRgBIAEoDRINCgV2",
+            "YWx1ZRgCIAEoDToCOAEiLgoZR2V0R2F0ZXdheXNTdW1tYXJ5UmVxdWVzdBIR",
+            "Cgl0ZW5hbnRfaWQYASABKAkiYwoaR2V0R2F0ZXdheXNTdW1tYXJ5UmVzcG9u",
+            "c2USFAoMb25saW5lX2NvdW50GAEgASgNEhUKDW9mZmxpbmVfY291bnQYAiAB",
+            "KA0SGAoQbmV2ZXJfc2Vlbl9jb3VudBgDIAEoDSLHAQoHTG9nSXRlbRIKCgJp",
+            "ZBgBIAEoCRIoCgR0aW1lGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVz",
+            "dGFtcBITCgtkZXNjcmlwdGlvbhgDIAEoCRIMCgRib2R5GAQgASgJEjAKCnBy",
+            "b3BlcnRpZXMYBSADKAsyHC5hcGkuTG9nSXRlbS5Qcm9wZXJ0aWVzRW50cnka",
+            "MQoPUHJvcGVydGllc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEo",
+            "CToCOAEiMAoaU3RyZWFtR2F0ZXdheUZyYW1lc1JlcXVlc3QSEgoKZ2F0ZXdh",
+            "eV9pZBgBIAEoCSIsChlTdHJlYW1EZXZpY2VGcmFtZXNSZXF1ZXN0Eg8KB2Rl",
+            "dl9ldWkYASABKAkiLAoZU3RyZWFtRGV2aWNlRXZlbnRzUmVxdWVzdBIPCgdk",
+            "ZXZfZXVpGAEgASgJIjsKE0xpc3RSZWdpb25zUmVzcG9uc2USJAoHcmVnaW9u",
+            "cxgBIAMoCzITLmFwaS5SZWdpb25MaXN0SXRlbSJRCg5SZWdpb25MaXN0SXRl",
+            "bRIKCgJpZBgBIAEoCRIeCgZyZWdpb24YAiABKA4yDi5jb21tb24uUmVnaW9u",
+            "EhMKC2Rlc2NyaXB0aW9uGAMgASgJIh4KEEdldFJlZ2lvblJlcXVlc3QSCgoC",
+            "aWQYASABKAkiqAIKEUdldFJlZ2lvblJlc3BvbnNlEgoKAmlkGAEgASgJEh4K",
+            "BnJlZ2lvbhgCIAEoDjIOLmNvbW1vbi5SZWdpb24SEQoJdXNlcl9pbmZvGAMg",
+            "ASgJEisKD3VwbGlua19jaGFubmVscxgEIAMoCzISLmFwaS5SZWdpb25DaGFu",
+            "bmVsEhEKCXJ4MV9kZWxheRgFIAEoDRIVCg1yeDFfZHJfb2Zmc2V0GAYgASgN",
+            "Eg4KBnJ4Ml9kchgHIAEoDRIVCg1yeDJfZnJlcXVlbmN5GAggASgNEhwKFGNs",
+            "YXNzX2JfcGluZ19zbG90X2RyGAkgASgNEiMKG2NsYXNzX2JfcGluZ19zbG90",
+            "X2ZyZXF1ZW5jeRgKIAEoDRITCgtkZXNjcmlwdGlvbhgLIAEoCSJCCg1SZWdp",
+            "b25DaGFubmVsEhEKCWZyZXF1ZW5jeRgBIAEoDRIOCgZkcl9taW4YAiABKA0S",
+            "DgoGZHJfbWF4GAMgASgNMrQICg9JbnRlcm5hbFNlcnZpY2USMAoFTG9naW4S",
+            "ES5hcGkuTG9naW5SZXF1ZXN0GhIuYXBpLkxvZ2luUmVzcG9uc2UiABI5CgdQ",
+            "cm9maWxlEhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5GhQuYXBpLlByb2ZpbGVS",
+            "ZXNwb25zZSIAEkUKDEdsb2JhbFNlYXJjaBIYLmFwaS5HbG9iYWxTZWFyY2hS",
+            "ZXF1ZXN0GhkuYXBpLkdsb2JhbFNlYXJjaFJlc3BvbnNlIgASRQoMQ3JlYXRl",
+            "QXBpS2V5EhguYXBpLkNyZWF0ZUFwaUtleVJlcXVlc3QaGS5hcGkuQ3JlYXRl",
+            "QXBpS2V5UmVzcG9uc2UiABJCCgxEZWxldGVBcGlLZXkSGC5hcGkuRGVsZXRl",
+            "QXBpS2V5UmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIAEkIKC0xp",
+            "c3RBcGlLZXlzEhcuYXBpLkxpc3RBcGlLZXlzUmVxdWVzdBoYLmFwaS5MaXN0",
+            "QXBpS2V5c1Jlc3BvbnNlIgASOwoIU2V0dGluZ3MSFi5nb29nbGUucHJvdG9i",
+            "dWYuRW1wdHkaFS5hcGkuU2V0dGluZ3NSZXNwb25zZSIAElcKEk9wZW5JZENv",
+            "bm5lY3RMb2dpbhIeLmFwaS5PcGVuSWRDb25uZWN0TG9naW5SZXF1ZXN0Gh8u",
+            "YXBpLk9wZW5JZENvbm5lY3RMb2dpblJlc3BvbnNlIgASVAoRR2V0RGV2aWNl",
+            "c1N1bW1hcnkSHS5hcGkuR2V0RGV2aWNlc1N1bW1hcnlSZXF1ZXN0Gh4uYXBp",
+            "LkdldERldmljZXNTdW1tYXJ5UmVzcG9uc2UiABJXChJHZXRHYXRld2F5c1N1",
+            "bW1hcnkSHi5hcGkuR2V0R2F0ZXdheXNTdW1tYXJ5UmVxdWVzdBofLmFwaS5H",
+            "ZXRHYXRld2F5c1N1bW1hcnlSZXNwb25zZSIAEkgKE1N0cmVhbUdhdGV3YXlG",
+            "cmFtZXMSHy5hcGkuU3RyZWFtR2F0ZXdheUZyYW1lc1JlcXVlc3QaDC5hcGku",
+            "TG9nSXRlbSIAMAESRgoSU3RyZWFtRGV2aWNlRnJhbWVzEh4uYXBpLlN0cmVh",
+            "bURldmljZUZyYW1lc1JlcXVlc3QaDC5hcGkuTG9nSXRlbSIAMAESRgoSU3Ry",
+            "ZWFtRGV2aWNlRXZlbnRzEh4uYXBpLlN0cmVhbURldmljZUV2ZW50c1JlcXVl",
+            "c3QaDC5hcGkuTG9nSXRlbSIAMAESQQoLTGlzdFJlZ2lvbnMSFi5nb29nbGUu",
+            "cHJvdG9idWYuRW1wdHkaGC5hcGkuTGlzdFJlZ2lvbnNSZXNwb25zZSIAEjwK",
+            "CUdldFJlZ2lvbhIVLmFwaS5HZXRSZWdpb25SZXF1ZXN0GhYuYXBpLkdldFJl",
+            "Z2lvblJlc3BvbnNlIgBCZQoRaW8uY2hpcnBzdGFjay5hcGlCDUludGVybmFs",
+            "UHJvdG9QAVouZ2l0aHViLmNvbS9jaGlycHN0YWNrL2NoaXJwc3RhY2svYXBp",
+            "L2dvL3Y0L2FwaaoCDkNoaXJwc3RhY2suQXBpYgZwcm90bzM="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Chirpstack.Common.CommonReflection.Descriptor, global::Chirpstack.Api.UserReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ApiKey), global::Chirpstack.Api.ApiKey.Parser, new[]{ "Id", "Name", "IsAdmin", "TenantId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateApiKeyRequest), global::Chirpstack.Api.CreateApiKeyRequest.Parser, new[]{ "ApiKey" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateApiKeyResponse), global::Chirpstack.Api.CreateApiKeyResponse.Parser, new[]{ "Id", "Token" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteApiKeyRequest), global::Chirpstack.Api.DeleteApiKeyRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListApiKeysRequest), global::Chirpstack.Api.ListApiKeysRequest.Parser, new[]{ "Limit", "Offset", "IsAdmin", "TenantId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListApiKeysResponse), global::Chirpstack.Api.ListApiKeysResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UserTenantLink), global::Chirpstack.Api.UserTenantLink.Parser, new[]{ "CreatedAt", "UpdatedAt", "TenantId", "IsAdmin", "IsDeviceAdmin", "IsGatewayAdmin" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.LoginRequest), global::Chirpstack.Api.LoginRequest.Parser, new[]{ "Email", "Password" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.LoginResponse), global::Chirpstack.Api.LoginResponse.Parser, new[]{ "Jwt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ProfileResponse), global::Chirpstack.Api.ProfileResponse.Parser, new[]{ "User", "Tenants" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GlobalSearchRequest), global::Chirpstack.Api.GlobalSearchRequest.Parser, new[]{ "Search", "Limit", "Offset" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GlobalSearchResponse), global::Chirpstack.Api.GlobalSearchResponse.Parser, new[]{ "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GlobalSearchResult), global::Chirpstack.Api.GlobalSearchResult.Parser, new[]{ "Kind", "Score", "TenantId", "TenantName", "ApplicationId", "ApplicationName", "DeviceDevEui", "DeviceName", "GatewayId", "GatewayName" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.SettingsResponse), global::Chirpstack.Api.SettingsResponse.Parser, new[]{ "OpenidConnect" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.OpenIdConnect), global::Chirpstack.Api.OpenIdConnect.Parser, new[]{ "Enabled", "LoginUrl", "LoginLabel", "LogoutUrl" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.OpenIdConnectLoginRequest), global::Chirpstack.Api.OpenIdConnectLoginRequest.Parser, new[]{ "Code", "State" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.OpenIdConnectLoginResponse), global::Chirpstack.Api.OpenIdConnectLoginResponse.Parser, new[]{ "Token" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDevicesSummaryRequest), global::Chirpstack.Api.GetDevicesSummaryRequest.Parser, new[]{ "TenantId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetDevicesSummaryResponse), global::Chirpstack.Api.GetDevicesSummaryResponse.Parser, new[]{ "ActiveCount", "InactiveCount", "DrCount", "NeverSeenCount" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetGatewaysSummaryRequest), global::Chirpstack.Api.GetGatewaysSummaryRequest.Parser, new[]{ "TenantId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetGatewaysSummaryResponse), global::Chirpstack.Api.GetGatewaysSummaryResponse.Parser, new[]{ "OnlineCount", "OfflineCount", "NeverSeenCount" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.LogItem), global::Chirpstack.Api.LogItem.Parser, new[]{ "Id", "Time", "Description", "Body", "Properties" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.StreamGatewayFramesRequest), global::Chirpstack.Api.StreamGatewayFramesRequest.Parser, new[]{ "GatewayId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.StreamDeviceFramesRequest), global::Chirpstack.Api.StreamDeviceFramesRequest.Parser, new[]{ "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.StreamDeviceEventsRequest), global::Chirpstack.Api.StreamDeviceEventsRequest.Parser, new[]{ "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListRegionsResponse), global::Chirpstack.Api.ListRegionsResponse.Parser, new[]{ "Regions" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.RegionListItem), global::Chirpstack.Api.RegionListItem.Parser, new[]{ "Id", "Region", "Description" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetRegionRequest), global::Chirpstack.Api.GetRegionRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetRegionResponse), global::Chirpstack.Api.GetRegionResponse.Parser, new[]{ "Id", "Region", "UserInfo", "UplinkChannels", "Rx1Delay", "Rx1DrOffset", "Rx2Dr", "Rx2Frequency", "ClassBPingSlotDr", "ClassBPingSlotFrequency", "Description" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.RegionChannel), global::Chirpstack.Api.RegionChannel.Parser, new[]{ "Frequency", "DrMin", "DrMax" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  public sealed partial class ApiKey : pb::IMessage<ApiKey>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ApiKey> _parser = new pb::MessageParser<ApiKey>(() => new ApiKey());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ApiKey> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ApiKey() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ApiKey(ApiKey other) : this() {
+      id_ = other.id_;
+      name_ = other.name_;
+      isAdmin_ = other.isAdmin_;
+      tenantId_ = other.tenantId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ApiKey Clone() {
+      return new ApiKey(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// API key ID.
+    /// This value will be automatically generated on create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 2;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "is_admin" field.</summary>
+    public const int IsAdminFieldNumber = 3;
+    private bool isAdmin_;
+    /// <summary>
+    /// Is global admin key.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsAdmin {
+      get { return isAdmin_; }
+      set {
+        isAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 4;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID.
+    /// In case the API key is intended to manage resources under a single tenant.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ApiKey);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ApiKey other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (Name != other.Name) return false;
+      if (IsAdmin != other.IsAdmin) return false;
+      if (TenantId != other.TenantId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (IsAdmin != false) hash ^= IsAdmin.GetHashCode();
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(IsAdmin);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(IsAdmin);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (IsAdmin != false) {
+        size += 1 + 1;
+      }
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ApiKey other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.IsAdmin != false) {
+        IsAdmin = other.IsAdmin;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 24: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 24: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateApiKeyRequest : pb::IMessage<CreateApiKeyRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateApiKeyRequest> _parser = new pb::MessageParser<CreateApiKeyRequest>(() => new CreateApiKeyRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateApiKeyRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApiKeyRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApiKeyRequest(CreateApiKeyRequest other) : this() {
+      apiKey_ = other.apiKey_ != null ? other.apiKey_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApiKeyRequest Clone() {
+      return new CreateApiKeyRequest(this);
+    }
+
+    /// <summary>Field number for the "api_key" field.</summary>
+    public const int ApiKeyFieldNumber = 1;
+    private global::Chirpstack.Api.ApiKey apiKey_;
+    /// <summary>
+    /// The API key to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.ApiKey ApiKey {
+      get { return apiKey_; }
+      set {
+        apiKey_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateApiKeyRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateApiKeyRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(ApiKey, other.ApiKey)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (apiKey_ != null) hash ^= ApiKey.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (apiKey_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(ApiKey);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (apiKey_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(ApiKey);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (apiKey_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ApiKey);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateApiKeyRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.apiKey_ != null) {
+        if (apiKey_ == null) {
+          ApiKey = new global::Chirpstack.Api.ApiKey();
+        }
+        ApiKey.MergeFrom(other.ApiKey);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (apiKey_ == null) {
+              ApiKey = new global::Chirpstack.Api.ApiKey();
+            }
+            input.ReadMessage(ApiKey);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (apiKey_ == null) {
+              ApiKey = new global::Chirpstack.Api.ApiKey();
+            }
+            input.ReadMessage(ApiKey);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateApiKeyResponse : pb::IMessage<CreateApiKeyResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateApiKeyResponse> _parser = new pb::MessageParser<CreateApiKeyResponse>(() => new CreateApiKeyResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateApiKeyResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApiKeyResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApiKeyResponse(CreateApiKeyResponse other) : this() {
+      id_ = other.id_;
+      token_ = other.token_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateApiKeyResponse Clone() {
+      return new CreateApiKeyResponse(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// API key ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "token" field.</summary>
+    public const int TokenFieldNumber = 2;
+    private string token_ = "";
+    /// <summary>
+    /// API token for authentication API requests.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Token {
+      get { return token_; }
+      set {
+        token_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateApiKeyResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateApiKeyResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (Token != other.Token) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (Token.Length != 0) hash ^= Token.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Token.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Token);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Token.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Token);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (Token.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Token);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateApiKeyResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.Token.Length != 0) {
+        Token = other.Token;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Token = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Token = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteApiKeyRequest : pb::IMessage<DeleteApiKeyRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteApiKeyRequest> _parser = new pb::MessageParser<DeleteApiKeyRequest>(() => new DeleteApiKeyRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteApiKeyRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteApiKeyRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteApiKeyRequest(DeleteApiKeyRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteApiKeyRequest Clone() {
+      return new DeleteApiKeyRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// API key ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteApiKeyRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteApiKeyRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteApiKeyRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListApiKeysRequest : pb::IMessage<ListApiKeysRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListApiKeysRequest> _parser = new pb::MessageParser<ListApiKeysRequest>(() => new ListApiKeysRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListApiKeysRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApiKeysRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApiKeysRequest(ListApiKeysRequest other) : this() {
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      isAdmin_ = other.isAdmin_;
+      tenantId_ = other.tenantId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApiKeysRequest Clone() {
+      return new ListApiKeysRequest(this);
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 1;
+    private uint limit_;
+    /// <summary>
+    /// Max number of items to return.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 2;
+    private uint offset_;
+    /// <summary>
+    /// Offset in the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_admin" field.</summary>
+    public const int IsAdminFieldNumber = 3;
+    private bool isAdmin_;
+    /// <summary>
+    /// Return only admin keys.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsAdmin {
+      get { return isAdmin_; }
+      set {
+        isAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 4;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Filter on tenant ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListApiKeysRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListApiKeysRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      if (IsAdmin != other.IsAdmin) return false;
+      if (TenantId != other.TenantId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Limit != 0) hash ^= Limit.GetHashCode();
+      if (Offset != 0) hash ^= Offset.GetHashCode();
+      if (IsAdmin != false) hash ^= IsAdmin.GetHashCode();
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(IsAdmin);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(IsAdmin);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Limit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Limit);
+      }
+      if (Offset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Offset);
+      }
+      if (IsAdmin != false) {
+        size += 1 + 1;
+      }
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListApiKeysRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Limit != 0) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0) {
+        Offset = other.Offset;
+      }
+      if (other.IsAdmin != false) {
+        IsAdmin = other.IsAdmin;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 34: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListApiKeysResponse : pb::IMessage<ListApiKeysResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListApiKeysResponse> _parser = new pb::MessageParser<ListApiKeysResponse>(() => new ListApiKeysResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListApiKeysResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApiKeysResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApiKeysResponse(ListApiKeysResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListApiKeysResponse Clone() {
+      return new ListApiKeysResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of API keys.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.ApiKey> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.ApiKey.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.ApiKey> result_ = new pbc::RepeatedField<global::Chirpstack.Api.ApiKey>();
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.ApiKey> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListApiKeysResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListApiKeysResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListApiKeysResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// Defines a tenant to which the user is associated.
+  /// </summary>
+  public sealed partial class UserTenantLink : pb::IMessage<UserTenantLink>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UserTenantLink> _parser = new pb::MessageParser<UserTenantLink>(() => new UserTenantLink());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UserTenantLink> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UserTenantLink() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UserTenantLink(UserTenantLink other) : this() {
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      tenantId_ = other.tenantId_;
+      isAdmin_ = other.isAdmin_;
+      isDeviceAdmin_ = other.isDeviceAdmin_;
+      isGatewayAdmin_ = other.isGatewayAdmin_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UserTenantLink Clone() {
+      return new UserTenantLink(this);
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 1;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 3;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "is_admin" field.</summary>
+    public const int IsAdminFieldNumber = 4;
+    private bool isAdmin_;
+    /// <summary>
+    /// User is admin within the context of this tenant.
+    /// There is no need to set the is_device_admin and is_gateway_admin flags.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsAdmin {
+      get { return isAdmin_; }
+      set {
+        isAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_device_admin" field.</summary>
+    public const int IsDeviceAdminFieldNumber = 5;
+    private bool isDeviceAdmin_;
+    /// <summary>
+    /// User is able to modify device related resources (applications,
+    /// device-profiles, devices, multicast-groups).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsDeviceAdmin {
+      get { return isDeviceAdmin_; }
+      set {
+        isDeviceAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_gateway_admin" field.</summary>
+    public const int IsGatewayAdminFieldNumber = 6;
+    private bool isGatewayAdmin_;
+    /// <summary>
+    /// User is able to modify gateways.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsGatewayAdmin {
+      get { return isGatewayAdmin_; }
+      set {
+        isGatewayAdmin_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UserTenantLink);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UserTenantLink other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (TenantId != other.TenantId) return false;
+      if (IsAdmin != other.IsAdmin) return false;
+      if (IsDeviceAdmin != other.IsDeviceAdmin) return false;
+      if (IsGatewayAdmin != other.IsGatewayAdmin) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (IsAdmin != false) hash ^= IsAdmin.GetHashCode();
+      if (IsDeviceAdmin != false) hash ^= IsDeviceAdmin.GetHashCode();
+      if (IsGatewayAdmin != false) hash ^= IsGatewayAdmin.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (createdAt_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(TenantId);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsDeviceAdmin != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(IsDeviceAdmin);
+      }
+      if (IsGatewayAdmin != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(IsGatewayAdmin);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (createdAt_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(TenantId);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsDeviceAdmin != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(IsDeviceAdmin);
+      }
+      if (IsGatewayAdmin != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(IsGatewayAdmin);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (IsAdmin != false) {
+        size += 1 + 1;
+      }
+      if (IsDeviceAdmin != false) {
+        size += 1 + 1;
+      }
+      if (IsGatewayAdmin != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UserTenantLink other) {
+      if (other == null) {
+        return;
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.IsAdmin != false) {
+        IsAdmin = other.IsAdmin;
+      }
+      if (other.IsDeviceAdmin != false) {
+        IsDeviceAdmin = other.IsDeviceAdmin;
+      }
+      if (other.IsGatewayAdmin != false) {
+        IsGatewayAdmin = other.IsGatewayAdmin;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 18: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 26: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 32: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 40: {
+            IsDeviceAdmin = input.ReadBool();
+            break;
+          }
+          case 48: {
+            IsGatewayAdmin = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 18: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 26: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 32: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 40: {
+            IsDeviceAdmin = input.ReadBool();
+            break;
+          }
+          case 48: {
+            IsGatewayAdmin = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class LoginRequest : pb::IMessage<LoginRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<LoginRequest> _parser = new pb::MessageParser<LoginRequest>(() => new LoginRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<LoginRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoginRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoginRequest(LoginRequest other) : this() {
+      email_ = other.email_;
+      password_ = other.password_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoginRequest Clone() {
+      return new LoginRequest(this);
+    }
+
+    /// <summary>Field number for the "email" field.</summary>
+    public const int EmailFieldNumber = 1;
+    private string email_ = "";
+    /// <summary>
+    /// Email of the user.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Email {
+      get { return email_; }
+      set {
+        email_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "password" field.</summary>
+    public const int PasswordFieldNumber = 2;
+    private string password_ = "";
+    /// <summary>
+    /// Password of the user.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Password {
+      get { return password_; }
+      set {
+        password_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as LoginRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(LoginRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Email != other.Email) return false;
+      if (Password != other.Password) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Email.Length != 0) hash ^= Email.GetHashCode();
+      if (Password.Length != 0) hash ^= Password.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Email.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Email);
+      }
+      if (Password.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Password);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Email.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Email);
+      }
+      if (Password.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Password);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Email.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);
+      }
+      if (Password.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Password);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(LoginRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Email.Length != 0) {
+        Email = other.Email;
+      }
+      if (other.Password.Length != 0) {
+        Password = other.Password;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Email = input.ReadString();
+            break;
+          }
+          case 18: {
+            Password = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Email = input.ReadString();
+            break;
+          }
+          case 18: {
+            Password = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class LoginResponse : pb::IMessage<LoginResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<LoginResponse> _parser = new pb::MessageParser<LoginResponse>(() => new LoginResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<LoginResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoginResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoginResponse(LoginResponse other) : this() {
+      jwt_ = other.jwt_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoginResponse Clone() {
+      return new LoginResponse(this);
+    }
+
+    /// <summary>Field number for the "jwt" field.</summary>
+    public const int JwtFieldNumber = 1;
+    private string jwt_ = "";
+    /// <summary>
+    /// The JWT tag to be used to access chirpstack-application-server interfaces.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Jwt {
+      get { return jwt_; }
+      set {
+        jwt_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as LoginResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(LoginResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Jwt != other.Jwt) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Jwt.Length != 0) hash ^= Jwt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Jwt.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Jwt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Jwt.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Jwt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Jwt.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Jwt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(LoginResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Jwt.Length != 0) {
+        Jwt = other.Jwt;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Jwt = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Jwt = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ProfileResponse : pb::IMessage<ProfileResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ProfileResponse> _parser = new pb::MessageParser<ProfileResponse>(() => new ProfileResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ProfileResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ProfileResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ProfileResponse(ProfileResponse other) : this() {
+      user_ = other.user_ != null ? other.user_.Clone() : null;
+      tenants_ = other.tenants_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ProfileResponse Clone() {
+      return new ProfileResponse(this);
+    }
+
+    /// <summary>Field number for the "user" field.</summary>
+    public const int UserFieldNumber = 1;
+    private global::Chirpstack.Api.User user_;
+    /// <summary>
+    /// User object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.User User {
+      get { return user_; }
+      set {
+        user_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tenants" field.</summary>
+    public const int TenantsFieldNumber = 3;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.UserTenantLink> _repeated_tenants_codec
+        = pb::FieldCodec.ForMessage(26, global::Chirpstack.Api.UserTenantLink.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.UserTenantLink> tenants_ = new pbc::RepeatedField<global::Chirpstack.Api.UserTenantLink>();
+    /// <summary>
+    /// Tenants to which the user is associated.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.UserTenantLink> Tenants {
+      get { return tenants_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ProfileResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ProfileResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(User, other.User)) return false;
+      if(!tenants_.Equals(other.tenants_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (user_ != null) hash ^= User.GetHashCode();
+      hash ^= tenants_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (user_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(User);
+      }
+      tenants_.WriteTo(output, _repeated_tenants_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (user_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(User);
+      }
+      tenants_.WriteTo(ref output, _repeated_tenants_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (user_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(User);
+      }
+      size += tenants_.CalculateSize(_repeated_tenants_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ProfileResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.user_ != null) {
+        if (user_ == null) {
+          User = new global::Chirpstack.Api.User();
+        }
+        User.MergeFrom(other.User);
+      }
+      tenants_.Add(other.tenants_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (user_ == null) {
+              User = new global::Chirpstack.Api.User();
+            }
+            input.ReadMessage(User);
+            break;
+          }
+          case 26: {
+            tenants_.AddEntriesFrom(input, _repeated_tenants_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (user_ == null) {
+              User = new global::Chirpstack.Api.User();
+            }
+            input.ReadMessage(User);
+            break;
+          }
+          case 26: {
+            tenants_.AddEntriesFrom(ref input, _repeated_tenants_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GlobalSearchRequest : pb::IMessage<GlobalSearchRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GlobalSearchRequest> _parser = new pb::MessageParser<GlobalSearchRequest>(() => new GlobalSearchRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GlobalSearchRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[10]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GlobalSearchRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GlobalSearchRequest(GlobalSearchRequest other) : this() {
+      search_ = other.search_;
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GlobalSearchRequest Clone() {
+      return new GlobalSearchRequest(this);
+    }
+
+    /// <summary>Field number for the "search" field.</summary>
+    public const int SearchFieldNumber = 1;
+    private string search_ = "";
+    /// <summary>
+    /// Search query.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Search {
+      get { return search_; }
+      set {
+        search_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 2;
+    private long limit_;
+    /// <summary>
+    /// Max number of results to return.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public long Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 3;
+    private long offset_;
+    /// <summary>
+    /// Offset offset of the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public long Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GlobalSearchRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GlobalSearchRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Search != other.Search) return false;
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Search.Length != 0) hash ^= Search.GetHashCode();
+      if (Limit != 0L) hash ^= Limit.GetHashCode();
+      if (Offset != 0L) hash ^= Offset.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Search.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Search);
+      }
+      if (Limit != 0L) {
+        output.WriteRawTag(16);
+        output.WriteInt64(Limit);
+      }
+      if (Offset != 0L) {
+        output.WriteRawTag(24);
+        output.WriteInt64(Offset);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Search.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Search);
+      }
+      if (Limit != 0L) {
+        output.WriteRawTag(16);
+        output.WriteInt64(Limit);
+      }
+      if (Offset != 0L) {
+        output.WriteRawTag(24);
+        output.WriteInt64(Offset);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Search.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Search);
+      }
+      if (Limit != 0L) {
+        size += 1 + pb::CodedOutputStream.ComputeInt64Size(Limit);
+      }
+      if (Offset != 0L) {
+        size += 1 + pb::CodedOutputStream.ComputeInt64Size(Offset);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GlobalSearchRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Search.Length != 0) {
+        Search = other.Search;
+      }
+      if (other.Limit != 0L) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0L) {
+        Offset = other.Offset;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Search = input.ReadString();
+            break;
+          }
+          case 16: {
+            Limit = input.ReadInt64();
+            break;
+          }
+          case 24: {
+            Offset = input.ReadInt64();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Search = input.ReadString();
+            break;
+          }
+          case 16: {
+            Limit = input.ReadInt64();
+            break;
+          }
+          case 24: {
+            Offset = input.ReadInt64();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GlobalSearchResponse : pb::IMessage<GlobalSearchResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GlobalSearchResponse> _parser = new pb::MessageParser<GlobalSearchResponse>(() => new GlobalSearchResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GlobalSearchResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[11]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GlobalSearchResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GlobalSearchResponse(GlobalSearchResponse other) : this() {
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GlobalSearchResponse Clone() {
+      return new GlobalSearchResponse(this);
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 1;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.GlobalSearchResult> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(10, global::Chirpstack.Api.GlobalSearchResult.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.GlobalSearchResult> result_ = new pbc::RepeatedField<global::Chirpstack.Api.GlobalSearchResult>();
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.GlobalSearchResult> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GlobalSearchResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GlobalSearchResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GlobalSearchResponse other) {
+      if (other == null) {
+        return;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GlobalSearchResult : pb::IMessage<GlobalSearchResult>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GlobalSearchResult> _parser = new pb::MessageParser<GlobalSearchResult>(() => new GlobalSearchResult());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GlobalSearchResult> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[12]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GlobalSearchResult() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GlobalSearchResult(GlobalSearchResult other) : this() {
+      kind_ = other.kind_;
+      score_ = other.score_;
+      tenantId_ = other.tenantId_;
+      tenantName_ = other.tenantName_;
+      applicationId_ = other.applicationId_;
+      applicationName_ = other.applicationName_;
+      deviceDevEui_ = other.deviceDevEui_;
+      deviceName_ = other.deviceName_;
+      gatewayId_ = other.gatewayId_;
+      gatewayName_ = other.gatewayName_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GlobalSearchResult Clone() {
+      return new GlobalSearchResult(this);
+    }
+
+    /// <summary>Field number for the "kind" field.</summary>
+    public const int KindFieldNumber = 1;
+    private string kind_ = "";
+    /// <summary>
+    /// Record kind.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Kind {
+      get { return kind_; }
+      set {
+        kind_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "score" field.</summary>
+    public const int ScoreFieldNumber = 2;
+    private float score_;
+    /// <summary>
+    /// Search score.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public float Score {
+      get { return score_; }
+      set {
+        score_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 3;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Organization id.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tenant_name" field.</summary>
+    public const int TenantNameFieldNumber = 4;
+    private string tenantName_ = "";
+    /// <summary>
+    /// Organization name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantName {
+      get { return tenantName_; }
+      set {
+        tenantName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 5;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application id.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "application_name" field.</summary>
+    public const int ApplicationNameFieldNumber = 6;
+    private string applicationName_ = "";
+    /// <summary>
+    /// Application name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationName {
+      get { return applicationName_; }
+      set {
+        applicationName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "device_dev_eui" field.</summary>
+    public const int DeviceDevEuiFieldNumber = 7;
+    private string deviceDevEui_ = "";
+    /// <summary>
+    /// Device DevEUI (hex encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeviceDevEui {
+      get { return deviceDevEui_; }
+      set {
+        deviceDevEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "device_name" field.</summary>
+    public const int DeviceNameFieldNumber = 8;
+    private string deviceName_ = "";
+    /// <summary>
+    /// Device name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeviceName {
+      get { return deviceName_; }
+      set {
+        deviceName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 9;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway MAC (hex encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_name" field.</summary>
+    public const int GatewayNameFieldNumber = 10;
+    private string gatewayName_ = "";
+    /// <summary>
+    /// Gateway name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayName {
+      get { return gatewayName_; }
+      set {
+        gatewayName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GlobalSearchResult);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GlobalSearchResult other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Kind != other.Kind) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(Score, other.Score)) return false;
+      if (TenantId != other.TenantId) return false;
+      if (TenantName != other.TenantName) return false;
+      if (ApplicationId != other.ApplicationId) return false;
+      if (ApplicationName != other.ApplicationName) return false;
+      if (DeviceDevEui != other.DeviceDevEui) return false;
+      if (DeviceName != other.DeviceName) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (GatewayName != other.GatewayName) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Kind.Length != 0) hash ^= Kind.GetHashCode();
+      if (Score != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(Score);
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (TenantName.Length != 0) hash ^= TenantName.GetHashCode();
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (ApplicationName.Length != 0) hash ^= ApplicationName.GetHashCode();
+      if (DeviceDevEui.Length != 0) hash ^= DeviceDevEui.GetHashCode();
+      if (DeviceName.Length != 0) hash ^= DeviceName.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (GatewayName.Length != 0) hash ^= GatewayName.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Kind.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Kind);
+      }
+      if (Score != 0F) {
+        output.WriteRawTag(21);
+        output.WriteFloat(Score);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(TenantId);
+      }
+      if (TenantName.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantName);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(ApplicationId);
+      }
+      if (ApplicationName.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(ApplicationName);
+      }
+      if (DeviceDevEui.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(DeviceDevEui);
+      }
+      if (DeviceName.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(DeviceName);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(GatewayId);
+      }
+      if (GatewayName.Length != 0) {
+        output.WriteRawTag(82);
+        output.WriteString(GatewayName);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Kind.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Kind);
+      }
+      if (Score != 0F) {
+        output.WriteRawTag(21);
+        output.WriteFloat(Score);
+      }
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(TenantId);
+      }
+      if (TenantName.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(TenantName);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(ApplicationId);
+      }
+      if (ApplicationName.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(ApplicationName);
+      }
+      if (DeviceDevEui.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(DeviceDevEui);
+      }
+      if (DeviceName.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(DeviceName);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(74);
+        output.WriteString(GatewayId);
+      }
+      if (GatewayName.Length != 0) {
+        output.WriteRawTag(82);
+        output.WriteString(GatewayName);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Kind.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Kind);
+      }
+      if (Score != 0F) {
+        size += 1 + 4;
+      }
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (TenantName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantName);
+      }
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (ApplicationName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationName);
+      }
+      if (DeviceDevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeviceDevEui);
+      }
+      if (DeviceName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeviceName);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (GatewayName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayName);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GlobalSearchResult other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Kind.Length != 0) {
+        Kind = other.Kind;
+      }
+      if (other.Score != 0F) {
+        Score = other.Score;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.TenantName.Length != 0) {
+        TenantName = other.TenantName;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.ApplicationName.Length != 0) {
+        ApplicationName = other.ApplicationName;
+      }
+      if (other.DeviceDevEui.Length != 0) {
+        DeviceDevEui = other.DeviceDevEui;
+      }
+      if (other.DeviceName.Length != 0) {
+        DeviceName = other.DeviceName;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.GatewayName.Length != 0) {
+        GatewayName = other.GatewayName;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Kind = input.ReadString();
+            break;
+          }
+          case 21: {
+            Score = input.ReadFloat();
+            break;
+          }
+          case 26: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 34: {
+            TenantName = input.ReadString();
+            break;
+          }
+          case 42: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 50: {
+            ApplicationName = input.ReadString();
+            break;
+          }
+          case 58: {
+            DeviceDevEui = input.ReadString();
+            break;
+          }
+          case 66: {
+            DeviceName = input.ReadString();
+            break;
+          }
+          case 74: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 82: {
+            GatewayName = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Kind = input.ReadString();
+            break;
+          }
+          case 21: {
+            Score = input.ReadFloat();
+            break;
+          }
+          case 26: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 34: {
+            TenantName = input.ReadString();
+            break;
+          }
+          case 42: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 50: {
+            ApplicationName = input.ReadString();
+            break;
+          }
+          case 58: {
+            DeviceDevEui = input.ReadString();
+            break;
+          }
+          case 66: {
+            DeviceName = input.ReadString();
+            break;
+          }
+          case 74: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 82: {
+            GatewayName = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class SettingsResponse : pb::IMessage<SettingsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<SettingsResponse> _parser = new pb::MessageParser<SettingsResponse>(() => new SettingsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<SettingsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[13]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public SettingsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public SettingsResponse(SettingsResponse other) : this() {
+      openidConnect_ = other.openidConnect_ != null ? other.openidConnect_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public SettingsResponse Clone() {
+      return new SettingsResponse(this);
+    }
+
+    /// <summary>Field number for the "openid_connect" field.</summary>
+    public const int OpenidConnectFieldNumber = 1;
+    private global::Chirpstack.Api.OpenIdConnect openidConnect_;
+    /// <summary>
+    /// OpenId Connect settings.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.OpenIdConnect OpenidConnect {
+      get { return openidConnect_; }
+      set {
+        openidConnect_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as SettingsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(SettingsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(OpenidConnect, other.OpenidConnect)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (openidConnect_ != null) hash ^= OpenidConnect.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (openidConnect_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(OpenidConnect);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (openidConnect_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(OpenidConnect);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (openidConnect_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(OpenidConnect);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(SettingsResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.openidConnect_ != null) {
+        if (openidConnect_ == null) {
+          OpenidConnect = new global::Chirpstack.Api.OpenIdConnect();
+        }
+        OpenidConnect.MergeFrom(other.OpenidConnect);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (openidConnect_ == null) {
+              OpenidConnect = new global::Chirpstack.Api.OpenIdConnect();
+            }
+            input.ReadMessage(OpenidConnect);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (openidConnect_ == null) {
+              OpenidConnect = new global::Chirpstack.Api.OpenIdConnect();
+            }
+            input.ReadMessage(OpenidConnect);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class OpenIdConnect : pb::IMessage<OpenIdConnect>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<OpenIdConnect> _parser = new pb::MessageParser<OpenIdConnect>(() => new OpenIdConnect());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<OpenIdConnect> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[14]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OpenIdConnect() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OpenIdConnect(OpenIdConnect other) : this() {
+      enabled_ = other.enabled_;
+      loginUrl_ = other.loginUrl_;
+      loginLabel_ = other.loginLabel_;
+      logoutUrl_ = other.logoutUrl_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OpenIdConnect Clone() {
+      return new OpenIdConnect(this);
+    }
+
+    /// <summary>Field number for the "enabled" field.</summary>
+    public const int EnabledFieldNumber = 1;
+    private bool enabled_;
+    /// <summary>
+    /// Enable OpenId Connect authentication.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Enabled {
+      get { return enabled_; }
+      set {
+        enabled_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "login_url" field.</summary>
+    public const int LoginUrlFieldNumber = 2;
+    private string loginUrl_ = "";
+    /// <summary>
+    /// Login url.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string LoginUrl {
+      get { return loginUrl_; }
+      set {
+        loginUrl_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "login_label" field.</summary>
+    public const int LoginLabelFieldNumber = 3;
+    private string loginLabel_ = "";
+    /// <summary>
+    /// Login label.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string LoginLabel {
+      get { return loginLabel_; }
+      set {
+        loginLabel_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "logout_url" field.</summary>
+    public const int LogoutUrlFieldNumber = 4;
+    private string logoutUrl_ = "";
+    /// <summary>
+    /// Logout url.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string LogoutUrl {
+      get { return logoutUrl_; }
+      set {
+        logoutUrl_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as OpenIdConnect);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(OpenIdConnect other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Enabled != other.Enabled) return false;
+      if (LoginUrl != other.LoginUrl) return false;
+      if (LoginLabel != other.LoginLabel) return false;
+      if (LogoutUrl != other.LogoutUrl) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Enabled != false) hash ^= Enabled.GetHashCode();
+      if (LoginUrl.Length != 0) hash ^= LoginUrl.GetHashCode();
+      if (LoginLabel.Length != 0) hash ^= LoginLabel.GetHashCode();
+      if (LogoutUrl.Length != 0) hash ^= LogoutUrl.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Enabled != false) {
+        output.WriteRawTag(8);
+        output.WriteBool(Enabled);
+      }
+      if (LoginUrl.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(LoginUrl);
+      }
+      if (LoginLabel.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(LoginLabel);
+      }
+      if (LogoutUrl.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(LogoutUrl);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Enabled != false) {
+        output.WriteRawTag(8);
+        output.WriteBool(Enabled);
+      }
+      if (LoginUrl.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(LoginUrl);
+      }
+      if (LoginLabel.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(LoginLabel);
+      }
+      if (LogoutUrl.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(LogoutUrl);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Enabled != false) {
+        size += 1 + 1;
+      }
+      if (LoginUrl.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(LoginUrl);
+      }
+      if (LoginLabel.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(LoginLabel);
+      }
+      if (LogoutUrl.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(LogoutUrl);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(OpenIdConnect other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Enabled != false) {
+        Enabled = other.Enabled;
+      }
+      if (other.LoginUrl.Length != 0) {
+        LoginUrl = other.LoginUrl;
+      }
+      if (other.LoginLabel.Length != 0) {
+        LoginLabel = other.LoginLabel;
+      }
+      if (other.LogoutUrl.Length != 0) {
+        LogoutUrl = other.LogoutUrl;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Enabled = input.ReadBool();
+            break;
+          }
+          case 18: {
+            LoginUrl = input.ReadString();
+            break;
+          }
+          case 26: {
+            LoginLabel = input.ReadString();
+            break;
+          }
+          case 34: {
+            LogoutUrl = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Enabled = input.ReadBool();
+            break;
+          }
+          case 18: {
+            LoginUrl = input.ReadString();
+            break;
+          }
+          case 26: {
+            LoginLabel = input.ReadString();
+            break;
+          }
+          case 34: {
+            LogoutUrl = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class OpenIdConnectLoginRequest : pb::IMessage<OpenIdConnectLoginRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<OpenIdConnectLoginRequest> _parser = new pb::MessageParser<OpenIdConnectLoginRequest>(() => new OpenIdConnectLoginRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<OpenIdConnectLoginRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[15]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OpenIdConnectLoginRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OpenIdConnectLoginRequest(OpenIdConnectLoginRequest other) : this() {
+      code_ = other.code_;
+      state_ = other.state_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OpenIdConnectLoginRequest Clone() {
+      return new OpenIdConnectLoginRequest(this);
+    }
+
+    /// <summary>Field number for the "code" field.</summary>
+    public const int CodeFieldNumber = 1;
+    private string code_ = "";
+    /// <summary>
+    /// OpenId Connect callback code.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Code {
+      get { return code_; }
+      set {
+        code_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "state" field.</summary>
+    public const int StateFieldNumber = 2;
+    private string state_ = "";
+    /// <summary>
+    /// OpenId Connect callback state.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string State {
+      get { return state_; }
+      set {
+        state_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as OpenIdConnectLoginRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(OpenIdConnectLoginRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Code != other.Code) return false;
+      if (State != other.State) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Code.Length != 0) hash ^= Code.GetHashCode();
+      if (State.Length != 0) hash ^= State.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Code.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Code);
+      }
+      if (State.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(State);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Code.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Code);
+      }
+      if (State.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(State);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Code.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Code);
+      }
+      if (State.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(State);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(OpenIdConnectLoginRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Code.Length != 0) {
+        Code = other.Code;
+      }
+      if (other.State.Length != 0) {
+        State = other.State;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Code = input.ReadString();
+            break;
+          }
+          case 18: {
+            State = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Code = input.ReadString();
+            break;
+          }
+          case 18: {
+            State = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class OpenIdConnectLoginResponse : pb::IMessage<OpenIdConnectLoginResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<OpenIdConnectLoginResponse> _parser = new pb::MessageParser<OpenIdConnectLoginResponse>(() => new OpenIdConnectLoginResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<OpenIdConnectLoginResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[16]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OpenIdConnectLoginResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OpenIdConnectLoginResponse(OpenIdConnectLoginResponse other) : this() {
+      token_ = other.token_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public OpenIdConnectLoginResponse Clone() {
+      return new OpenIdConnectLoginResponse(this);
+    }
+
+    /// <summary>Field number for the "token" field.</summary>
+    public const int TokenFieldNumber = 1;
+    private string token_ = "";
+    /// <summary>
+    /// Token to use for authentication.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Token {
+      get { return token_; }
+      set {
+        token_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as OpenIdConnectLoginResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(OpenIdConnectLoginResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Token != other.Token) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Token.Length != 0) hash ^= Token.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Token.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Token);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Token.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Token);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Token.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Token);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(OpenIdConnectLoginResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Token.Length != 0) {
+        Token = other.Token;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Token = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Token = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDevicesSummaryRequest : pb::IMessage<GetDevicesSummaryRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDevicesSummaryRequest> _parser = new pb::MessageParser<GetDevicesSummaryRequest>(() => new GetDevicesSummaryRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDevicesSummaryRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[17]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDevicesSummaryRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDevicesSummaryRequest(GetDevicesSummaryRequest other) : this() {
+      tenantId_ = other.tenantId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDevicesSummaryRequest Clone() {
+      return new GetDevicesSummaryRequest(this);
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 1;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDevicesSummaryRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDevicesSummaryRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TenantId != other.TenantId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDevicesSummaryRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetDevicesSummaryResponse : pb::IMessage<GetDevicesSummaryResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetDevicesSummaryResponse> _parser = new pb::MessageParser<GetDevicesSummaryResponse>(() => new GetDevicesSummaryResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetDevicesSummaryResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[18]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDevicesSummaryResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDevicesSummaryResponse(GetDevicesSummaryResponse other) : this() {
+      activeCount_ = other.activeCount_;
+      inactiveCount_ = other.inactiveCount_;
+      drCount_ = other.drCount_.Clone();
+      neverSeenCount_ = other.neverSeenCount_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetDevicesSummaryResponse Clone() {
+      return new GetDevicesSummaryResponse(this);
+    }
+
+    /// <summary>Field number for the "active_count" field.</summary>
+    public const int ActiveCountFieldNumber = 1;
+    private uint activeCount_;
+    /// <summary>
+    /// Active count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ActiveCount {
+      get { return activeCount_; }
+      set {
+        activeCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "inactive_count" field.</summary>
+    public const int InactiveCountFieldNumber = 2;
+    private uint inactiveCount_;
+    /// <summary>
+    /// Inactive count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint InactiveCount {
+      get { return inactiveCount_; }
+      set {
+        inactiveCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "dr_count" field.</summary>
+    public const int DrCountFieldNumber = 3;
+    private static readonly pbc::MapField<uint, uint>.Codec _map_drCount_codec
+        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForUInt32(8, 0), pb::FieldCodec.ForUInt32(16, 0), 26);
+    private readonly pbc::MapField<uint, uint> drCount_ = new pbc::MapField<uint, uint>();
+    /// <summary>
+    /// per data-rate count.
+    /// Devices that have never been seen are excluded.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<uint, uint> DrCount {
+      get { return drCount_; }
+    }
+
+    /// <summary>Field number for the "never_seen_count" field.</summary>
+    public const int NeverSeenCountFieldNumber = 4;
+    private uint neverSeenCount_;
+    /// <summary>
+    /// Never seen count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint NeverSeenCount {
+      get { return neverSeenCount_; }
+      set {
+        neverSeenCount_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetDevicesSummaryResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetDevicesSummaryResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ActiveCount != other.ActiveCount) return false;
+      if (InactiveCount != other.InactiveCount) return false;
+      if (!DrCount.Equals(other.DrCount)) return false;
+      if (NeverSeenCount != other.NeverSeenCount) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ActiveCount != 0) hash ^= ActiveCount.GetHashCode();
+      if (InactiveCount != 0) hash ^= InactiveCount.GetHashCode();
+      hash ^= DrCount.GetHashCode();
+      if (NeverSeenCount != 0) hash ^= NeverSeenCount.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (ActiveCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(ActiveCount);
+      }
+      if (InactiveCount != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(InactiveCount);
+      }
+      drCount_.WriteTo(output, _map_drCount_codec);
+      if (NeverSeenCount != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(NeverSeenCount);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (ActiveCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(ActiveCount);
+      }
+      if (InactiveCount != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(InactiveCount);
+      }
+      drCount_.WriteTo(ref output, _map_drCount_codec);
+      if (NeverSeenCount != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(NeverSeenCount);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (ActiveCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ActiveCount);
+      }
+      if (InactiveCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(InactiveCount);
+      }
+      size += drCount_.CalculateSize(_map_drCount_codec);
+      if (NeverSeenCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(NeverSeenCount);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetDevicesSummaryResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ActiveCount != 0) {
+        ActiveCount = other.ActiveCount;
+      }
+      if (other.InactiveCount != 0) {
+        InactiveCount = other.InactiveCount;
+      }
+      drCount_.Add(other.drCount_);
+      if (other.NeverSeenCount != 0) {
+        NeverSeenCount = other.NeverSeenCount;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            ActiveCount = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            InactiveCount = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            drCount_.AddEntriesFrom(input, _map_drCount_codec);
+            break;
+          }
+          case 32: {
+            NeverSeenCount = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            ActiveCount = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            InactiveCount = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            drCount_.AddEntriesFrom(ref input, _map_drCount_codec);
+            break;
+          }
+          case 32: {
+            NeverSeenCount = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetGatewaysSummaryRequest : pb::IMessage<GetGatewaysSummaryRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetGatewaysSummaryRequest> _parser = new pb::MessageParser<GetGatewaysSummaryRequest>(() => new GetGatewaysSummaryRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetGatewaysSummaryRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[19]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewaysSummaryRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewaysSummaryRequest(GetGatewaysSummaryRequest other) : this() {
+      tenantId_ = other.tenantId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewaysSummaryRequest Clone() {
+      return new GetGatewaysSummaryRequest(this);
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 1;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetGatewaysSummaryRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetGatewaysSummaryRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TenantId != other.TenantId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetGatewaysSummaryRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetGatewaysSummaryResponse : pb::IMessage<GetGatewaysSummaryResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetGatewaysSummaryResponse> _parser = new pb::MessageParser<GetGatewaysSummaryResponse>(() => new GetGatewaysSummaryResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetGatewaysSummaryResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[20]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewaysSummaryResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewaysSummaryResponse(GetGatewaysSummaryResponse other) : this() {
+      onlineCount_ = other.onlineCount_;
+      offlineCount_ = other.offlineCount_;
+      neverSeenCount_ = other.neverSeenCount_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetGatewaysSummaryResponse Clone() {
+      return new GetGatewaysSummaryResponse(this);
+    }
+
+    /// <summary>Field number for the "online_count" field.</summary>
+    public const int OnlineCountFieldNumber = 1;
+    private uint onlineCount_;
+    /// <summary>
+    /// Online count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint OnlineCount {
+      get { return onlineCount_; }
+      set {
+        onlineCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offline_count" field.</summary>
+    public const int OfflineCountFieldNumber = 2;
+    private uint offlineCount_;
+    /// <summary>
+    /// Offline count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint OfflineCount {
+      get { return offlineCount_; }
+      set {
+        offlineCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "never_seen_count" field.</summary>
+    public const int NeverSeenCountFieldNumber = 3;
+    private uint neverSeenCount_;
+    /// <summary>
+    /// Never seen count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint NeverSeenCount {
+      get { return neverSeenCount_; }
+      set {
+        neverSeenCount_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetGatewaysSummaryResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetGatewaysSummaryResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (OnlineCount != other.OnlineCount) return false;
+      if (OfflineCount != other.OfflineCount) return false;
+      if (NeverSeenCount != other.NeverSeenCount) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (OnlineCount != 0) hash ^= OnlineCount.GetHashCode();
+      if (OfflineCount != 0) hash ^= OfflineCount.GetHashCode();
+      if (NeverSeenCount != 0) hash ^= NeverSeenCount.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (OnlineCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(OnlineCount);
+      }
+      if (OfflineCount != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(OfflineCount);
+      }
+      if (NeverSeenCount != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(NeverSeenCount);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (OnlineCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(OnlineCount);
+      }
+      if (OfflineCount != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(OfflineCount);
+      }
+      if (NeverSeenCount != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(NeverSeenCount);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (OnlineCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(OnlineCount);
+      }
+      if (OfflineCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(OfflineCount);
+      }
+      if (NeverSeenCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(NeverSeenCount);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetGatewaysSummaryResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.OnlineCount != 0) {
+        OnlineCount = other.OnlineCount;
+      }
+      if (other.OfflineCount != 0) {
+        OfflineCount = other.OfflineCount;
+      }
+      if (other.NeverSeenCount != 0) {
+        NeverSeenCount = other.NeverSeenCount;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            OnlineCount = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            OfflineCount = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            NeverSeenCount = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            OnlineCount = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            OfflineCount = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            NeverSeenCount = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class LogItem : pb::IMessage<LogItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<LogItem> _parser = new pb::MessageParser<LogItem>(() => new LogItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<LogItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[21]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LogItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LogItem(LogItem other) : this() {
+      id_ = other.id_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      description_ = other.description_;
+      body_ = other.body_;
+      properties_ = other.properties_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LogItem Clone() {
+      return new LogItem(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 3;
+    private string description_ = "";
+    /// <summary>
+    /// Message.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "body" field.</summary>
+    public const int BodyFieldNumber = 4;
+    private string body_ = "";
+    /// <summary>
+    /// Body.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Body {
+      get { return body_; }
+      set {
+        body_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "properties" field.</summary>
+    public const int PropertiesFieldNumber = 5;
+    private static readonly pbc::MapField<string, string>.Codec _map_properties_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 42);
+    private readonly pbc::MapField<string, string> properties_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Properties.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Properties {
+      get { return properties_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as LogItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(LogItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (Description != other.Description) return false;
+      if (Body != other.Body) return false;
+      if (!Properties.Equals(other.Properties)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (Body.Length != 0) hash ^= Body.GetHashCode();
+      hash ^= Properties.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (Body.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Body);
+      }
+      properties_.WriteTo(output, _map_properties_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (Body.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Body);
+      }
+      properties_.WriteTo(ref output, _map_properties_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (Body.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Body);
+      }
+      size += properties_.CalculateSize(_map_properties_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(LogItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      if (other.Body.Length != 0) {
+        Body = other.Body;
+      }
+      properties_.Add(other.properties_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 34: {
+            Body = input.ReadString();
+            break;
+          }
+          case 42: {
+            properties_.AddEntriesFrom(input, _map_properties_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 34: {
+            Body = input.ReadString();
+            break;
+          }
+          case 42: {
+            properties_.AddEntriesFrom(ref input, _map_properties_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class StreamGatewayFramesRequest : pb::IMessage<StreamGatewayFramesRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<StreamGatewayFramesRequest> _parser = new pb::MessageParser<StreamGatewayFramesRequest>(() => new StreamGatewayFramesRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<StreamGatewayFramesRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[22]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StreamGatewayFramesRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StreamGatewayFramesRequest(StreamGatewayFramesRequest other) : this() {
+      gatewayId_ = other.gatewayId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StreamGatewayFramesRequest Clone() {
+      return new StreamGatewayFramesRequest(this);
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 1;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as StreamGatewayFramesRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(StreamGatewayFramesRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayId != other.GatewayId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(StreamGatewayFramesRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class StreamDeviceFramesRequest : pb::IMessage<StreamDeviceFramesRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<StreamDeviceFramesRequest> _parser = new pb::MessageParser<StreamDeviceFramesRequest>(() => new StreamDeviceFramesRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<StreamDeviceFramesRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[23]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StreamDeviceFramesRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StreamDeviceFramesRequest(StreamDeviceFramesRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StreamDeviceFramesRequest Clone() {
+      return new StreamDeviceFramesRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as StreamDeviceFramesRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(StreamDeviceFramesRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(StreamDeviceFramesRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class StreamDeviceEventsRequest : pb::IMessage<StreamDeviceEventsRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<StreamDeviceEventsRequest> _parser = new pb::MessageParser<StreamDeviceEventsRequest>(() => new StreamDeviceEventsRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<StreamDeviceEventsRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[24]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StreamDeviceEventsRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StreamDeviceEventsRequest(StreamDeviceEventsRequest other) : this() {
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StreamDeviceEventsRequest Clone() {
+      return new StreamDeviceEventsRequest(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as StreamDeviceEventsRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(StreamDeviceEventsRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(StreamDeviceEventsRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListRegionsResponse : pb::IMessage<ListRegionsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListRegionsResponse> _parser = new pb::MessageParser<ListRegionsResponse>(() => new ListRegionsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListRegionsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[25]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListRegionsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListRegionsResponse(ListRegionsResponse other) : this() {
+      regions_ = other.regions_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListRegionsResponse Clone() {
+      return new ListRegionsResponse(this);
+    }
+
+    /// <summary>Field number for the "regions" field.</summary>
+    public const int RegionsFieldNumber = 1;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.RegionListItem> _repeated_regions_codec
+        = pb::FieldCodec.ForMessage(10, global::Chirpstack.Api.RegionListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.RegionListItem> regions_ = new pbc::RepeatedField<global::Chirpstack.Api.RegionListItem>();
+    /// <summary>
+    /// Configured regions.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.RegionListItem> Regions {
+      get { return regions_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListRegionsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListRegionsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if(!regions_.Equals(other.regions_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= regions_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      regions_.WriteTo(output, _repeated_regions_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      regions_.WriteTo(ref output, _repeated_regions_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      size += regions_.CalculateSize(_repeated_regions_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListRegionsResponse other) {
+      if (other == null) {
+        return;
+      }
+      regions_.Add(other.regions_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            regions_.AddEntriesFrom(input, _repeated_regions_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            regions_.AddEntriesFrom(ref input, _repeated_regions_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class RegionListItem : pb::IMessage<RegionListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<RegionListItem> _parser = new pb::MessageParser<RegionListItem>(() => new RegionListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<RegionListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[26]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RegionListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RegionListItem(RegionListItem other) : this() {
+      id_ = other.id_;
+      region_ = other.region_;
+      description_ = other.description_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RegionListItem Clone() {
+      return new RegionListItem(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "region" field.</summary>
+    public const int RegionFieldNumber = 2;
+    private global::Chirpstack.Common.Region region_ = global::Chirpstack.Common.Region.Eu868;
+    /// <summary>
+    /// Region.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Region Region {
+      get { return region_; }
+      set {
+        region_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 3;
+    private string description_ = "";
+    /// <summary>
+    /// Description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as RegionListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(RegionListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (Region != other.Region) return false;
+      if (Description != other.Description) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (Region != global::Chirpstack.Common.Region.Eu868) hash ^= Region.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Region);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Region);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Region);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(RegionListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.Region != global::Chirpstack.Common.Region.Eu868) {
+        Region = other.Region;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 16: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 16: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetRegionRequest : pb::IMessage<GetRegionRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetRegionRequest> _parser = new pb::MessageParser<GetRegionRequest>(() => new GetRegionRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetRegionRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[27]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRegionRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRegionRequest(GetRegionRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRegionRequest Clone() {
+      return new GetRegionRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Region ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetRegionRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetRegionRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetRegionRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetRegionResponse : pb::IMessage<GetRegionResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetRegionResponse> _parser = new pb::MessageParser<GetRegionResponse>(() => new GetRegionResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetRegionResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[28]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRegionResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRegionResponse(GetRegionResponse other) : this() {
+      id_ = other.id_;
+      region_ = other.region_;
+      userInfo_ = other.userInfo_;
+      uplinkChannels_ = other.uplinkChannels_.Clone();
+      rx1Delay_ = other.rx1Delay_;
+      rx1DrOffset_ = other.rx1DrOffset_;
+      rx2Dr_ = other.rx2Dr_;
+      rx2Frequency_ = other.rx2Frequency_;
+      classBPingSlotDr_ = other.classBPingSlotDr_;
+      classBPingSlotFrequency_ = other.classBPingSlotFrequency_;
+      description_ = other.description_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetRegionResponse Clone() {
+      return new GetRegionResponse(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "region" field.</summary>
+    public const int RegionFieldNumber = 2;
+    private global::Chirpstack.Common.Region region_ = global::Chirpstack.Common.Region.Eu868;
+    /// <summary>
+    /// Region.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Region Region {
+      get { return region_; }
+      set {
+        region_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "user_info" field.</summary>
+    public const int UserInfoFieldNumber = 3;
+    private string userInfo_ = "";
+    /// <summary>
+    /// User information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string UserInfo {
+      get { return userInfo_; }
+      set {
+        userInfo_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "uplink_channels" field.</summary>
+    public const int UplinkChannelsFieldNumber = 4;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.RegionChannel> _repeated_uplinkChannels_codec
+        = pb::FieldCodec.ForMessage(34, global::Chirpstack.Api.RegionChannel.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.RegionChannel> uplinkChannels_ = new pbc::RepeatedField<global::Chirpstack.Api.RegionChannel>();
+    /// <summary>
+    /// Uplink channels.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.RegionChannel> UplinkChannels {
+      get { return uplinkChannels_; }
+    }
+
+    /// <summary>Field number for the "rx1_delay" field.</summary>
+    public const int Rx1DelayFieldNumber = 5;
+    private uint rx1Delay_;
+    /// <summary>
+    /// RX1 delay.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Rx1Delay {
+      get { return rx1Delay_; }
+      set {
+        rx1Delay_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx1_dr_offset" field.</summary>
+    public const int Rx1DrOffsetFieldNumber = 6;
+    private uint rx1DrOffset_;
+    /// <summary>
+    /// RX1 data-rate offset.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Rx1DrOffset {
+      get { return rx1DrOffset_; }
+      set {
+        rx1DrOffset_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx2_dr" field.</summary>
+    public const int Rx2DrFieldNumber = 7;
+    private uint rx2Dr_;
+    /// <summary>
+    /// RX2 DR.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Rx2Dr {
+      get { return rx2Dr_; }
+      set {
+        rx2Dr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx2_frequency" field.</summary>
+    public const int Rx2FrequencyFieldNumber = 8;
+    private uint rx2Frequency_;
+    /// <summary>
+    /// RX2 frequency.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Rx2Frequency {
+      get { return rx2Frequency_; }
+      set {
+        rx2Frequency_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_ping_slot_dr" field.</summary>
+    public const int ClassBPingSlotDrFieldNumber = 9;
+    private uint classBPingSlotDr_;
+    /// <summary>
+    /// Class-B ping-slot DR.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBPingSlotDr {
+      get { return classBPingSlotDr_; }
+      set {
+        classBPingSlotDr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_ping_slot_frequency" field.</summary>
+    public const int ClassBPingSlotFrequencyFieldNumber = 10;
+    private uint classBPingSlotFrequency_;
+    /// <summary>
+    /// Class-B ping-slot frequency.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBPingSlotFrequency {
+      get { return classBPingSlotFrequency_; }
+      set {
+        classBPingSlotFrequency_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 11;
+    private string description_ = "";
+    /// <summary>
+    /// Region description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetRegionResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetRegionResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (Region != other.Region) return false;
+      if (UserInfo != other.UserInfo) return false;
+      if(!uplinkChannels_.Equals(other.uplinkChannels_)) return false;
+      if (Rx1Delay != other.Rx1Delay) return false;
+      if (Rx1DrOffset != other.Rx1DrOffset) return false;
+      if (Rx2Dr != other.Rx2Dr) return false;
+      if (Rx2Frequency != other.Rx2Frequency) return false;
+      if (ClassBPingSlotDr != other.ClassBPingSlotDr) return false;
+      if (ClassBPingSlotFrequency != other.ClassBPingSlotFrequency) return false;
+      if (Description != other.Description) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (Region != global::Chirpstack.Common.Region.Eu868) hash ^= Region.GetHashCode();
+      if (UserInfo.Length != 0) hash ^= UserInfo.GetHashCode();
+      hash ^= uplinkChannels_.GetHashCode();
+      if (Rx1Delay != 0) hash ^= Rx1Delay.GetHashCode();
+      if (Rx1DrOffset != 0) hash ^= Rx1DrOffset.GetHashCode();
+      if (Rx2Dr != 0) hash ^= Rx2Dr.GetHashCode();
+      if (Rx2Frequency != 0) hash ^= Rx2Frequency.GetHashCode();
+      if (ClassBPingSlotDr != 0) hash ^= ClassBPingSlotDr.GetHashCode();
+      if (ClassBPingSlotFrequency != 0) hash ^= ClassBPingSlotFrequency.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Region);
+      }
+      if (UserInfo.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(UserInfo);
+      }
+      uplinkChannels_.WriteTo(output, _repeated_uplinkChannels_codec);
+      if (Rx1Delay != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(Rx1Delay);
+      }
+      if (Rx1DrOffset != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(Rx1DrOffset);
+      }
+      if (Rx2Dr != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(Rx2Dr);
+      }
+      if (Rx2Frequency != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(Rx2Frequency);
+      }
+      if (ClassBPingSlotDr != 0) {
+        output.WriteRawTag(72);
+        output.WriteUInt32(ClassBPingSlotDr);
+      }
+      if (ClassBPingSlotFrequency != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(ClassBPingSlotFrequency);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(90);
+        output.WriteString(Description);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Region);
+      }
+      if (UserInfo.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(UserInfo);
+      }
+      uplinkChannels_.WriteTo(ref output, _repeated_uplinkChannels_codec);
+      if (Rx1Delay != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(Rx1Delay);
+      }
+      if (Rx1DrOffset != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(Rx1DrOffset);
+      }
+      if (Rx2Dr != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(Rx2Dr);
+      }
+      if (Rx2Frequency != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(Rx2Frequency);
+      }
+      if (ClassBPingSlotDr != 0) {
+        output.WriteRawTag(72);
+        output.WriteUInt32(ClassBPingSlotDr);
+      }
+      if (ClassBPingSlotFrequency != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(ClassBPingSlotFrequency);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(90);
+        output.WriteString(Description);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Region);
+      }
+      if (UserInfo.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(UserInfo);
+      }
+      size += uplinkChannels_.CalculateSize(_repeated_uplinkChannels_codec);
+      if (Rx1Delay != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Rx1Delay);
+      }
+      if (Rx1DrOffset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Rx1DrOffset);
+      }
+      if (Rx2Dr != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Rx2Dr);
+      }
+      if (Rx2Frequency != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Rx2Frequency);
+      }
+      if (ClassBPingSlotDr != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ClassBPingSlotDr);
+      }
+      if (ClassBPingSlotFrequency != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ClassBPingSlotFrequency);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetRegionResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.Region != global::Chirpstack.Common.Region.Eu868) {
+        Region = other.Region;
+      }
+      if (other.UserInfo.Length != 0) {
+        UserInfo = other.UserInfo;
+      }
+      uplinkChannels_.Add(other.uplinkChannels_);
+      if (other.Rx1Delay != 0) {
+        Rx1Delay = other.Rx1Delay;
+      }
+      if (other.Rx1DrOffset != 0) {
+        Rx1DrOffset = other.Rx1DrOffset;
+      }
+      if (other.Rx2Dr != 0) {
+        Rx2Dr = other.Rx2Dr;
+      }
+      if (other.Rx2Frequency != 0) {
+        Rx2Frequency = other.Rx2Frequency;
+      }
+      if (other.ClassBPingSlotDr != 0) {
+        ClassBPingSlotDr = other.ClassBPingSlotDr;
+      }
+      if (other.ClassBPingSlotFrequency != 0) {
+        ClassBPingSlotFrequency = other.ClassBPingSlotFrequency;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 16: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            UserInfo = input.ReadString();
+            break;
+          }
+          case 34: {
+            uplinkChannels_.AddEntriesFrom(input, _repeated_uplinkChannels_codec);
+            break;
+          }
+          case 40: {
+            Rx1Delay = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            Rx1DrOffset = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            Rx2Dr = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            Rx2Frequency = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            ClassBPingSlotDr = input.ReadUInt32();
+            break;
+          }
+          case 80: {
+            ClassBPingSlotFrequency = input.ReadUInt32();
+            break;
+          }
+          case 90: {
+            Description = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 16: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            UserInfo = input.ReadString();
+            break;
+          }
+          case 34: {
+            uplinkChannels_.AddEntriesFrom(ref input, _repeated_uplinkChannels_codec);
+            break;
+          }
+          case 40: {
+            Rx1Delay = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            Rx1DrOffset = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            Rx2Dr = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            Rx2Frequency = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            ClassBPingSlotDr = input.ReadUInt32();
+            break;
+          }
+          case 80: {
+            ClassBPingSlotFrequency = input.ReadUInt32();
+            break;
+          }
+          case 90: {
+            Description = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class RegionChannel : pb::IMessage<RegionChannel>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<RegionChannel> _parser = new pb::MessageParser<RegionChannel>(() => new RegionChannel());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<RegionChannel> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.MessageTypes[29]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RegionChannel() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RegionChannel(RegionChannel other) : this() {
+      frequency_ = other.frequency_;
+      drMin_ = other.drMin_;
+      drMax_ = other.drMax_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RegionChannel Clone() {
+      return new RegionChannel(this);
+    }
+
+    /// <summary>Field number for the "frequency" field.</summary>
+    public const int FrequencyFieldNumber = 1;
+    private uint frequency_;
+    /// <summary>
+    /// Frequency (Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Frequency {
+      get { return frequency_; }
+      set {
+        frequency_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "dr_min" field.</summary>
+    public const int DrMinFieldNumber = 2;
+    private uint drMin_;
+    /// <summary>
+    /// Min DR.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint DrMin {
+      get { return drMin_; }
+      set {
+        drMin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "dr_max" field.</summary>
+    public const int DrMaxFieldNumber = 3;
+    private uint drMax_;
+    /// <summary>
+    /// Max DR.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint DrMax {
+      get { return drMax_; }
+      set {
+        drMax_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as RegionChannel);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(RegionChannel other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Frequency != other.Frequency) return false;
+      if (DrMin != other.DrMin) return false;
+      if (DrMax != other.DrMax) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Frequency != 0) hash ^= Frequency.GetHashCode();
+      if (DrMin != 0) hash ^= DrMin.GetHashCode();
+      if (DrMax != 0) hash ^= DrMax.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Frequency != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Frequency);
+      }
+      if (DrMin != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(DrMin);
+      }
+      if (DrMax != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(DrMax);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Frequency != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Frequency);
+      }
+      if (DrMin != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(DrMin);
+      }
+      if (DrMax != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(DrMax);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Frequency != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Frequency);
+      }
+      if (DrMin != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(DrMin);
+      }
+      if (DrMax != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(DrMax);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(RegionChannel other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Frequency != 0) {
+        Frequency = other.Frequency;
+      }
+      if (other.DrMin != 0) {
+        DrMin = other.DrMin;
+      }
+      if (other.DrMax != 0) {
+        DrMax = other.DrMax;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            DrMin = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            DrMax = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            DrMin = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            DrMax = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/InternalGrpc.cs b/api/csharp/Chirpstack/api/InternalGrpc.cs
new file mode 100644
index 00000000..1eb8228a
--- /dev/null
+++ b/api/csharp/Chirpstack/api/InternalGrpc.cs
@@ -0,0 +1,1149 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/internal.proto
+// </auto-generated>
+#pragma warning disable 0414, 1591, 8981
+#region Designer generated code
+
+using grpc = global::Grpc.Core;
+
+namespace Chirpstack.Api {
+  /// <summary>
+  /// InternalService is the service providing API endpoints for internal usage.
+  /// </summary>
+  public static partial class InternalService
+  {
+    static readonly string __ServiceName = "api.InternalService";
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (message is global::Google.Protobuf.IBufferMessage)
+      {
+        context.SetPayloadLength(message.CalculateSize());
+        global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
+        context.Complete();
+        return;
+      }
+      #endif
+      context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static class __Helper_MessageCache<T>
+    {
+      public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (__Helper_MessageCache<T>.IsBufferMessage)
+      {
+        return parser.ParseFrom(context.PayloadAsReadOnlySequence());
+      }
+      #endif
+      return parser.ParseFrom(context.PayloadAsNewBuffer());
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.LoginRequest> __Marshaller_api_LoginRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.LoginRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.LoginResponse> __Marshaller_api_LoginResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.LoginResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Google.Protobuf.WellKnownTypes.Empty> __Marshaller_google_protobuf_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Google.Protobuf.WellKnownTypes.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ProfileResponse> __Marshaller_api_ProfileResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ProfileResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GlobalSearchRequest> __Marshaller_api_GlobalSearchRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GlobalSearchRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GlobalSearchResponse> __Marshaller_api_GlobalSearchResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GlobalSearchResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateApiKeyRequest> __Marshaller_api_CreateApiKeyRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateApiKeyRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateApiKeyResponse> __Marshaller_api_CreateApiKeyResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateApiKeyResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteApiKeyRequest> __Marshaller_api_DeleteApiKeyRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteApiKeyRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListApiKeysRequest> __Marshaller_api_ListApiKeysRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListApiKeysRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListApiKeysResponse> __Marshaller_api_ListApiKeysResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListApiKeysResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.SettingsResponse> __Marshaller_api_SettingsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.SettingsResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.OpenIdConnectLoginRequest> __Marshaller_api_OpenIdConnectLoginRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.OpenIdConnectLoginRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.OpenIdConnectLoginResponse> __Marshaller_api_OpenIdConnectLoginResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.OpenIdConnectLoginResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDevicesSummaryRequest> __Marshaller_api_GetDevicesSummaryRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDevicesSummaryRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetDevicesSummaryResponse> __Marshaller_api_GetDevicesSummaryResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetDevicesSummaryResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetGatewaysSummaryRequest> __Marshaller_api_GetGatewaysSummaryRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetGatewaysSummaryRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetGatewaysSummaryResponse> __Marshaller_api_GetGatewaysSummaryResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetGatewaysSummaryResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.StreamGatewayFramesRequest> __Marshaller_api_StreamGatewayFramesRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.StreamGatewayFramesRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.LogItem> __Marshaller_api_LogItem = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.LogItem.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.StreamDeviceFramesRequest> __Marshaller_api_StreamDeviceFramesRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.StreamDeviceFramesRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.StreamDeviceEventsRequest> __Marshaller_api_StreamDeviceEventsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.StreamDeviceEventsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListRegionsResponse> __Marshaller_api_ListRegionsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListRegionsResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetRegionRequest> __Marshaller_api_GetRegionRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetRegionRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetRegionResponse> __Marshaller_api_GetRegionResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetRegionResponse.Parser));
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.LoginRequest, global::Chirpstack.Api.LoginResponse> __Method_Login = new grpc::Method<global::Chirpstack.Api.LoginRequest, global::Chirpstack.Api.LoginResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Login",
+        __Marshaller_api_LoginRequest,
+        __Marshaller_api_LoginResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.ProfileResponse> __Method_Profile = new grpc::Method<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.ProfileResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Profile",
+        __Marshaller_google_protobuf_Empty,
+        __Marshaller_api_ProfileResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GlobalSearchRequest, global::Chirpstack.Api.GlobalSearchResponse> __Method_GlobalSearch = new grpc::Method<global::Chirpstack.Api.GlobalSearchRequest, global::Chirpstack.Api.GlobalSearchResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GlobalSearch",
+        __Marshaller_api_GlobalSearchRequest,
+        __Marshaller_api_GlobalSearchResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateApiKeyRequest, global::Chirpstack.Api.CreateApiKeyResponse> __Method_CreateApiKey = new grpc::Method<global::Chirpstack.Api.CreateApiKeyRequest, global::Chirpstack.Api.CreateApiKeyResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "CreateApiKey",
+        __Marshaller_api_CreateApiKeyRequest,
+        __Marshaller_api_CreateApiKeyResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteApiKeyRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteApiKey = new grpc::Method<global::Chirpstack.Api.DeleteApiKeyRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteApiKey",
+        __Marshaller_api_DeleteApiKeyRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListApiKeysRequest, global::Chirpstack.Api.ListApiKeysResponse> __Method_ListApiKeys = new grpc::Method<global::Chirpstack.Api.ListApiKeysRequest, global::Chirpstack.Api.ListApiKeysResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "ListApiKeys",
+        __Marshaller_api_ListApiKeysRequest,
+        __Marshaller_api_ListApiKeysResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.SettingsResponse> __Method_Settings = new grpc::Method<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.SettingsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Settings",
+        __Marshaller_google_protobuf_Empty,
+        __Marshaller_api_SettingsResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.OpenIdConnectLoginRequest, global::Chirpstack.Api.OpenIdConnectLoginResponse> __Method_OpenIdConnectLogin = new grpc::Method<global::Chirpstack.Api.OpenIdConnectLoginRequest, global::Chirpstack.Api.OpenIdConnectLoginResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "OpenIdConnectLogin",
+        __Marshaller_api_OpenIdConnectLoginRequest,
+        __Marshaller_api_OpenIdConnectLoginResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetDevicesSummaryRequest, global::Chirpstack.Api.GetDevicesSummaryResponse> __Method_GetDevicesSummary = new grpc::Method<global::Chirpstack.Api.GetDevicesSummaryRequest, global::Chirpstack.Api.GetDevicesSummaryResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetDevicesSummary",
+        __Marshaller_api_GetDevicesSummaryRequest,
+        __Marshaller_api_GetDevicesSummaryResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetGatewaysSummaryRequest, global::Chirpstack.Api.GetGatewaysSummaryResponse> __Method_GetGatewaysSummary = new grpc::Method<global::Chirpstack.Api.GetGatewaysSummaryRequest, global::Chirpstack.Api.GetGatewaysSummaryResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetGatewaysSummary",
+        __Marshaller_api_GetGatewaysSummaryRequest,
+        __Marshaller_api_GetGatewaysSummaryResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.StreamGatewayFramesRequest, global::Chirpstack.Api.LogItem> __Method_StreamGatewayFrames = new grpc::Method<global::Chirpstack.Api.StreamGatewayFramesRequest, global::Chirpstack.Api.LogItem>(
+        grpc::MethodType.ServerStreaming,
+        __ServiceName,
+        "StreamGatewayFrames",
+        __Marshaller_api_StreamGatewayFramesRequest,
+        __Marshaller_api_LogItem);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.StreamDeviceFramesRequest, global::Chirpstack.Api.LogItem> __Method_StreamDeviceFrames = new grpc::Method<global::Chirpstack.Api.StreamDeviceFramesRequest, global::Chirpstack.Api.LogItem>(
+        grpc::MethodType.ServerStreaming,
+        __ServiceName,
+        "StreamDeviceFrames",
+        __Marshaller_api_StreamDeviceFramesRequest,
+        __Marshaller_api_LogItem);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.StreamDeviceEventsRequest, global::Chirpstack.Api.LogItem> __Method_StreamDeviceEvents = new grpc::Method<global::Chirpstack.Api.StreamDeviceEventsRequest, global::Chirpstack.Api.LogItem>(
+        grpc::MethodType.ServerStreaming,
+        __ServiceName,
+        "StreamDeviceEvents",
+        __Marshaller_api_StreamDeviceEventsRequest,
+        __Marshaller_api_LogItem);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.ListRegionsResponse> __Method_ListRegions = new grpc::Method<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.ListRegionsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "ListRegions",
+        __Marshaller_google_protobuf_Empty,
+        __Marshaller_api_ListRegionsResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetRegionRequest, global::Chirpstack.Api.GetRegionResponse> __Method_GetRegion = new grpc::Method<global::Chirpstack.Api.GetRegionRequest, global::Chirpstack.Api.GetRegionResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetRegion",
+        __Marshaller_api_GetRegionRequest,
+        __Marshaller_api_GetRegionResponse);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Chirpstack.Api.InternalReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of InternalService</summary>
+    [grpc::BindServiceMethod(typeof(InternalService), "BindService")]
+    public abstract partial class InternalServiceBase
+    {
+      /// <summary>
+      /// Log in a user
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.LoginResponse> Login(global::Chirpstack.Api.LoginRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the current user's profile
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ProfileResponse> Profile(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Perform a global search.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GlobalSearchResponse> GlobalSearch(global::Chirpstack.Api.GlobalSearchRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// CreateApiKey creates the given API key.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.CreateApiKeyResponse> CreateApiKey(global::Chirpstack.Api.CreateApiKeyRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// DeleteApiKey deletes the API key.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteApiKey(global::Chirpstack.Api.DeleteApiKeyRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// ListApiKeys lists the available API keys.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListApiKeysResponse> ListApiKeys(global::Chirpstack.Api.ListApiKeysRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the global settings.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.SettingsResponse> Settings(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// OpenId Connect login.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.OpenIdConnectLoginResponse> OpenIdConnectLogin(global::Chirpstack.Api.OpenIdConnectLoginRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// GetDevicesSummary returns an aggregated summary of the devices.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetDevicesSummaryResponse> GetDevicesSummary(global::Chirpstack.Api.GetDevicesSummaryRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// GetGatewaysSummary returns an aggregated summary of the gateways.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetGatewaysSummaryResponse> GetGatewaysSummary(global::Chirpstack.Api.GetGatewaysSummaryRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Stream frame for the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task StreamGatewayFrames(global::Chirpstack.Api.StreamGatewayFramesRequest request, grpc::IServerStreamWriter<global::Chirpstack.Api.LogItem> responseStream, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Stream frames for the given Device EUI.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task StreamDeviceFrames(global::Chirpstack.Api.StreamDeviceFramesRequest request, grpc::IServerStreamWriter<global::Chirpstack.Api.LogItem> responseStream, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Stream events for the given Device EUI.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="responseStream">Used for sending responses back to the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>A task indicating completion of the handler.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task StreamDeviceEvents(global::Chirpstack.Api.StreamDeviceEventsRequest request, grpc::IServerStreamWriter<global::Chirpstack.Api.LogItem> responseStream, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// ListRegions lists the available (configured) regions.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListRegionsResponse> ListRegions(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// GetRegion returns the region details for the given region.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetRegionResponse> GetRegion(global::Chirpstack.Api.GetRegionRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for InternalService</summary>
+    public partial class InternalServiceClient : grpc::ClientBase<InternalServiceClient>
+    {
+      /// <summary>Creates a new client for InternalService</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public InternalServiceClient(grpc::ChannelBase channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for InternalService that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public InternalServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected InternalServiceClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected InternalServiceClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      /// Log in a user
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.LoginResponse Login(global::Chirpstack.Api.LoginRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Login(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Log in a user
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.LoginResponse Login(global::Chirpstack.Api.LoginRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Login, null, options, request);
+      }
+      /// <summary>
+      /// Log in a user
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.LoginResponse> LoginAsync(global::Chirpstack.Api.LoginRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return LoginAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Log in a user
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.LoginResponse> LoginAsync(global::Chirpstack.Api.LoginRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Login, null, options, request);
+      }
+      /// <summary>
+      /// Get the current user's profile
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ProfileResponse Profile(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Profile(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the current user's profile
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ProfileResponse Profile(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Profile, null, options, request);
+      }
+      /// <summary>
+      /// Get the current user's profile
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ProfileResponse> ProfileAsync(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ProfileAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the current user's profile
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ProfileResponse> ProfileAsync(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Profile, null, options, request);
+      }
+      /// <summary>
+      /// Perform a global search.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GlobalSearchResponse GlobalSearch(global::Chirpstack.Api.GlobalSearchRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GlobalSearch(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Perform a global search.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GlobalSearchResponse GlobalSearch(global::Chirpstack.Api.GlobalSearchRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GlobalSearch, null, options, request);
+      }
+      /// <summary>
+      /// Perform a global search.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GlobalSearchResponse> GlobalSearchAsync(global::Chirpstack.Api.GlobalSearchRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GlobalSearchAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Perform a global search.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GlobalSearchResponse> GlobalSearchAsync(global::Chirpstack.Api.GlobalSearchRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GlobalSearch, null, options, request);
+      }
+      /// <summary>
+      /// CreateApiKey creates the given API key.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateApiKeyResponse CreateApiKey(global::Chirpstack.Api.CreateApiKeyRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateApiKey(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// CreateApiKey creates the given API key.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateApiKeyResponse CreateApiKey(global::Chirpstack.Api.CreateApiKeyRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_CreateApiKey, null, options, request);
+      }
+      /// <summary>
+      /// CreateApiKey creates the given API key.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateApiKeyResponse> CreateApiKeyAsync(global::Chirpstack.Api.CreateApiKeyRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateApiKeyAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// CreateApiKey creates the given API key.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateApiKeyResponse> CreateApiKeyAsync(global::Chirpstack.Api.CreateApiKeyRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_CreateApiKey, null, options, request);
+      }
+      /// <summary>
+      /// DeleteApiKey deletes the API key.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteApiKey(global::Chirpstack.Api.DeleteApiKeyRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteApiKey(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// DeleteApiKey deletes the API key.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteApiKey(global::Chirpstack.Api.DeleteApiKeyRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteApiKey, null, options, request);
+      }
+      /// <summary>
+      /// DeleteApiKey deletes the API key.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteApiKeyAsync(global::Chirpstack.Api.DeleteApiKeyRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteApiKeyAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// DeleteApiKey deletes the API key.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteApiKeyAsync(global::Chirpstack.Api.DeleteApiKeyRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteApiKey, null, options, request);
+      }
+      /// <summary>
+      /// ListApiKeys lists the available API keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListApiKeysResponse ListApiKeys(global::Chirpstack.Api.ListApiKeysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListApiKeys(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// ListApiKeys lists the available API keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListApiKeysResponse ListApiKeys(global::Chirpstack.Api.ListApiKeysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_ListApiKeys, null, options, request);
+      }
+      /// <summary>
+      /// ListApiKeys lists the available API keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListApiKeysResponse> ListApiKeysAsync(global::Chirpstack.Api.ListApiKeysRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListApiKeysAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// ListApiKeys lists the available API keys.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListApiKeysResponse> ListApiKeysAsync(global::Chirpstack.Api.ListApiKeysRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_ListApiKeys, null, options, request);
+      }
+      /// <summary>
+      /// Get the global settings.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.SettingsResponse Settings(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Settings(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the global settings.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.SettingsResponse Settings(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Settings, null, options, request);
+      }
+      /// <summary>
+      /// Get the global settings.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.SettingsResponse> SettingsAsync(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return SettingsAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the global settings.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.SettingsResponse> SettingsAsync(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Settings, null, options, request);
+      }
+      /// <summary>
+      /// OpenId Connect login.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.OpenIdConnectLoginResponse OpenIdConnectLogin(global::Chirpstack.Api.OpenIdConnectLoginRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return OpenIdConnectLogin(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// OpenId Connect login.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.OpenIdConnectLoginResponse OpenIdConnectLogin(global::Chirpstack.Api.OpenIdConnectLoginRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_OpenIdConnectLogin, null, options, request);
+      }
+      /// <summary>
+      /// OpenId Connect login.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.OpenIdConnectLoginResponse> OpenIdConnectLoginAsync(global::Chirpstack.Api.OpenIdConnectLoginRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return OpenIdConnectLoginAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// OpenId Connect login.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.OpenIdConnectLoginResponse> OpenIdConnectLoginAsync(global::Chirpstack.Api.OpenIdConnectLoginRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_OpenIdConnectLogin, null, options, request);
+      }
+      /// <summary>
+      /// GetDevicesSummary returns an aggregated summary of the devices.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDevicesSummaryResponse GetDevicesSummary(global::Chirpstack.Api.GetDevicesSummaryRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetDevicesSummary(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetDevicesSummary returns an aggregated summary of the devices.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetDevicesSummaryResponse GetDevicesSummary(global::Chirpstack.Api.GetDevicesSummaryRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetDevicesSummary, null, options, request);
+      }
+      /// <summary>
+      /// GetDevicesSummary returns an aggregated summary of the devices.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDevicesSummaryResponse> GetDevicesSummaryAsync(global::Chirpstack.Api.GetDevicesSummaryRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetDevicesSummaryAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetDevicesSummary returns an aggregated summary of the devices.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetDevicesSummaryResponse> GetDevicesSummaryAsync(global::Chirpstack.Api.GetDevicesSummaryRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetDevicesSummary, null, options, request);
+      }
+      /// <summary>
+      /// GetGatewaysSummary returns an aggregated summary of the gateways.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetGatewaysSummaryResponse GetGatewaysSummary(global::Chirpstack.Api.GetGatewaysSummaryRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetGatewaysSummary(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetGatewaysSummary returns an aggregated summary of the gateways.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetGatewaysSummaryResponse GetGatewaysSummary(global::Chirpstack.Api.GetGatewaysSummaryRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetGatewaysSummary, null, options, request);
+      }
+      /// <summary>
+      /// GetGatewaysSummary returns an aggregated summary of the gateways.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetGatewaysSummaryResponse> GetGatewaysSummaryAsync(global::Chirpstack.Api.GetGatewaysSummaryRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetGatewaysSummaryAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetGatewaysSummary returns an aggregated summary of the gateways.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetGatewaysSummaryResponse> GetGatewaysSummaryAsync(global::Chirpstack.Api.GetGatewaysSummaryRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetGatewaysSummary, null, options, request);
+      }
+      /// <summary>
+      /// Stream frame for the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncServerStreamingCall<global::Chirpstack.Api.LogItem> StreamGatewayFrames(global::Chirpstack.Api.StreamGatewayFramesRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return StreamGatewayFrames(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Stream frame for the given Gateway ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncServerStreamingCall<global::Chirpstack.Api.LogItem> StreamGatewayFrames(global::Chirpstack.Api.StreamGatewayFramesRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncServerStreamingCall(__Method_StreamGatewayFrames, null, options, request);
+      }
+      /// <summary>
+      /// Stream frames for the given Device EUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncServerStreamingCall<global::Chirpstack.Api.LogItem> StreamDeviceFrames(global::Chirpstack.Api.StreamDeviceFramesRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return StreamDeviceFrames(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Stream frames for the given Device EUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncServerStreamingCall<global::Chirpstack.Api.LogItem> StreamDeviceFrames(global::Chirpstack.Api.StreamDeviceFramesRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncServerStreamingCall(__Method_StreamDeviceFrames, null, options, request);
+      }
+      /// <summary>
+      /// Stream events for the given Device EUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncServerStreamingCall<global::Chirpstack.Api.LogItem> StreamDeviceEvents(global::Chirpstack.Api.StreamDeviceEventsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return StreamDeviceEvents(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Stream events for the given Device EUI.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncServerStreamingCall<global::Chirpstack.Api.LogItem> StreamDeviceEvents(global::Chirpstack.Api.StreamDeviceEventsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncServerStreamingCall(__Method_StreamDeviceEvents, null, options, request);
+      }
+      /// <summary>
+      /// ListRegions lists the available (configured) regions.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListRegionsResponse ListRegions(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListRegions(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// ListRegions lists the available (configured) regions.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListRegionsResponse ListRegions(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_ListRegions, null, options, request);
+      }
+      /// <summary>
+      /// ListRegions lists the available (configured) regions.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListRegionsResponse> ListRegionsAsync(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListRegionsAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// ListRegions lists the available (configured) regions.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListRegionsResponse> ListRegionsAsync(global::Google.Protobuf.WellKnownTypes.Empty request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_ListRegions, null, options, request);
+      }
+      /// <summary>
+      /// GetRegion returns the region details for the given region.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetRegionResponse GetRegion(global::Chirpstack.Api.GetRegionRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetRegion(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetRegion returns the region details for the given region.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetRegionResponse GetRegion(global::Chirpstack.Api.GetRegionRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetRegion, null, options, request);
+      }
+      /// <summary>
+      /// GetRegion returns the region details for the given region.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetRegionResponse> GetRegionAsync(global::Chirpstack.Api.GetRegionRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetRegionAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// GetRegion returns the region details for the given region.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetRegionResponse> GetRegionAsync(global::Chirpstack.Api.GetRegionRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetRegion, null, options, request);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected override InternalServiceClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new InternalServiceClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static grpc::ServerServiceDefinition BindService(InternalServiceBase serviceImpl)
+    {
+      return grpc::ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_Login, serviceImpl.Login)
+          .AddMethod(__Method_Profile, serviceImpl.Profile)
+          .AddMethod(__Method_GlobalSearch, serviceImpl.GlobalSearch)
+          .AddMethod(__Method_CreateApiKey, serviceImpl.CreateApiKey)
+          .AddMethod(__Method_DeleteApiKey, serviceImpl.DeleteApiKey)
+          .AddMethod(__Method_ListApiKeys, serviceImpl.ListApiKeys)
+          .AddMethod(__Method_Settings, serviceImpl.Settings)
+          .AddMethod(__Method_OpenIdConnectLogin, serviceImpl.OpenIdConnectLogin)
+          .AddMethod(__Method_GetDevicesSummary, serviceImpl.GetDevicesSummary)
+          .AddMethod(__Method_GetGatewaysSummary, serviceImpl.GetGatewaysSummary)
+          .AddMethod(__Method_StreamGatewayFrames, serviceImpl.StreamGatewayFrames)
+          .AddMethod(__Method_StreamDeviceFrames, serviceImpl.StreamDeviceFrames)
+          .AddMethod(__Method_StreamDeviceEvents, serviceImpl.StreamDeviceEvents)
+          .AddMethod(__Method_ListRegions, serviceImpl.ListRegions)
+          .AddMethod(__Method_GetRegion, serviceImpl.GetRegion).Build();
+    }
+
+    /// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
+    /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+    /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static void BindService(grpc::ServiceBinderBase serviceBinder, InternalServiceBase serviceImpl)
+    {
+      serviceBinder.AddMethod(__Method_Login, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.LoginRequest, global::Chirpstack.Api.LoginResponse>(serviceImpl.Login));
+      serviceBinder.AddMethod(__Method_Profile, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.ProfileResponse>(serviceImpl.Profile));
+      serviceBinder.AddMethod(__Method_GlobalSearch, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GlobalSearchRequest, global::Chirpstack.Api.GlobalSearchResponse>(serviceImpl.GlobalSearch));
+      serviceBinder.AddMethod(__Method_CreateApiKey, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateApiKeyRequest, global::Chirpstack.Api.CreateApiKeyResponse>(serviceImpl.CreateApiKey));
+      serviceBinder.AddMethod(__Method_DeleteApiKey, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteApiKeyRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteApiKey));
+      serviceBinder.AddMethod(__Method_ListApiKeys, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListApiKeysRequest, global::Chirpstack.Api.ListApiKeysResponse>(serviceImpl.ListApiKeys));
+      serviceBinder.AddMethod(__Method_Settings, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.SettingsResponse>(serviceImpl.Settings));
+      serviceBinder.AddMethod(__Method_OpenIdConnectLogin, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.OpenIdConnectLoginRequest, global::Chirpstack.Api.OpenIdConnectLoginResponse>(serviceImpl.OpenIdConnectLogin));
+      serviceBinder.AddMethod(__Method_GetDevicesSummary, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetDevicesSummaryRequest, global::Chirpstack.Api.GetDevicesSummaryResponse>(serviceImpl.GetDevicesSummary));
+      serviceBinder.AddMethod(__Method_GetGatewaysSummary, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetGatewaysSummaryRequest, global::Chirpstack.Api.GetGatewaysSummaryResponse>(serviceImpl.GetGatewaysSummary));
+      serviceBinder.AddMethod(__Method_StreamGatewayFrames, serviceImpl == null ? null : new grpc::ServerStreamingServerMethod<global::Chirpstack.Api.StreamGatewayFramesRequest, global::Chirpstack.Api.LogItem>(serviceImpl.StreamGatewayFrames));
+      serviceBinder.AddMethod(__Method_StreamDeviceFrames, serviceImpl == null ? null : new grpc::ServerStreamingServerMethod<global::Chirpstack.Api.StreamDeviceFramesRequest, global::Chirpstack.Api.LogItem>(serviceImpl.StreamDeviceFrames));
+      serviceBinder.AddMethod(__Method_StreamDeviceEvents, serviceImpl == null ? null : new grpc::ServerStreamingServerMethod<global::Chirpstack.Api.StreamDeviceEventsRequest, global::Chirpstack.Api.LogItem>(serviceImpl.StreamDeviceEvents));
+      serviceBinder.AddMethod(__Method_ListRegions, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Google.Protobuf.WellKnownTypes.Empty, global::Chirpstack.Api.ListRegionsResponse>(serviceImpl.ListRegions));
+      serviceBinder.AddMethod(__Method_GetRegion, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetRegionRequest, global::Chirpstack.Api.GetRegionResponse>(serviceImpl.GetRegion));
+    }
+
+  }
+}
+#endregion
diff --git a/api/csharp/Chirpstack/api/MulticastGroup.cs b/api/csharp/Chirpstack/api/MulticastGroup.cs
new file mode 100644
index 00000000..723d7149
--- /dev/null
+++ b/api/csharp/Chirpstack/api/MulticastGroup.cs
@@ -0,0 +1,5270 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/multicast_group.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/multicast_group.proto</summary>
+  public static partial class MulticastGroupReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/multicast_group.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static MulticastGroupReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChlhcGkvbXVsdGljYXN0X2dyb3VwLnByb3RvEgNhcGkaHGdvb2dsZS9hcGkv",
+            "YW5ub3RhdGlvbnMucHJvdG8aH2dvb2dsZS9wcm90b2J1Zi90aW1lc3RhbXAu",
+            "cHJvdG8aG2dvb2dsZS9wcm90b2J1Zi9lbXB0eS5wcm90bxoTY29tbW9uL2Nv",
+            "bW1vbi5wcm90byLgAgoOTXVsdGljYXN0R3JvdXASCgoCaWQYASABKAkSDAoE",
+            "bmFtZRgCIAEoCRIWCg5hcHBsaWNhdGlvbl9pZBgDIAEoCRIeCgZyZWdpb24Y",
+            "BCABKA4yDi5jb21tb24uUmVnaW9uEg8KB21jX2FkZHIYBSABKAkSFAoMbWNf",
+            "bndrX3Nfa2V5GAYgASgJEhQKDG1jX2FwcF9zX2tleRgHIAEoCRINCgVmX2Nu",
+            "dBgIIAEoDRIrCgpncm91cF90eXBlGAkgASgOMhcuYXBpLk11bHRpY2FzdEdy",
+            "b3VwVHlwZRIKCgJkchgKIAEoDRIRCglmcmVxdWVuY3kYCyABKA0SIAoYY2xh",
+            "c3NfYl9waW5nX3Nsb3RfcGVyaW9kGAwgASgNEkIKF2NsYXNzX2Nfc2NoZWR1",
+            "bGluZ190eXBlGA0gASgOMiEuYXBpLk11bHRpY2FzdEdyb3VwU2NoZWR1bGlu",
+            "Z1R5cGUi3wEKFk11bHRpY2FzdEdyb3VwTGlzdEl0ZW0SCgoCaWQYASABKAkS",
+            "LgoKY3JlYXRlZF9hdBgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3Rh",
+            "bXASLgoKdXBkYXRlZF9hdBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1l",
+            "c3RhbXASDAoEbmFtZRgEIAEoCRIeCgZyZWdpb24YBSABKA4yDi5jb21tb24u",
+            "UmVnaW9uEisKCmdyb3VwX3R5cGUYBiABKA4yFy5hcGkuTXVsdGljYXN0R3Jv",
+            "dXBUeXBlIksKG0NyZWF0ZU11bHRpY2FzdEdyb3VwUmVxdWVzdBIsCg9tdWx0",
+            "aWNhc3RfZ3JvdXAYASABKAsyEy5hcGkuTXVsdGljYXN0R3JvdXAiKgocQ3Jl",
+            "YXRlTXVsdGljYXN0R3JvdXBSZXNwb25zZRIKCgJpZBgBIAEoCSImChhHZXRN",
+            "dWx0aWNhc3RHcm91cFJlcXVlc3QSCgoCaWQYASABKAkiqQEKGUdldE11bHRp",
+            "Y2FzdEdyb3VwUmVzcG9uc2USLAoPbXVsdGljYXN0X2dyb3VwGAEgASgLMhMu",
+            "YXBpLk11bHRpY2FzdEdyb3VwEi4KCmNyZWF0ZWRfYXQYAiABKAsyGi5nb29n",
+            "bGUucHJvdG9idWYuVGltZXN0YW1wEi4KCnVwZGF0ZWRfYXQYAyABKAsyGi5n",
+            "b29nbGUucHJvdG9idWYuVGltZXN0YW1wIksKG1VwZGF0ZU11bHRpY2FzdEdy",
+            "b3VwUmVxdWVzdBIsCg9tdWx0aWNhc3RfZ3JvdXAYASABKAsyEy5hcGkuTXVs",
+            "dGljYXN0R3JvdXAiKQobRGVsZXRlTXVsdGljYXN0R3JvdXBSZXF1ZXN0EgoK",
+            "AmlkGAEgASgJImMKGkxpc3RNdWx0aWNhc3RHcm91cHNSZXF1ZXN0Eg0KBWxp",
+            "bWl0GAEgASgNEg4KBm9mZnNldBgCIAEoDRIOCgZzZWFyY2gYAyABKAkSFgoO",
+            "YXBwbGljYXRpb25faWQYBCABKAkiXwobTGlzdE11bHRpY2FzdEdyb3Vwc1Jl",
+            "c3BvbnNlEhMKC3RvdGFsX2NvdW50GAEgASgNEisKBnJlc3VsdBgCIAMoCzIb",
+            "LmFwaS5NdWx0aWNhc3RHcm91cExpc3RJdGVtIk8KIEFkZERldmljZVRvTXVs",
+            "dGljYXN0R3JvdXBSZXF1ZXN0EhoKEm11bHRpY2FzdF9ncm91cF9pZBgBIAEo",
+            "CRIPCgdkZXZfZXVpGAIgASgJIlQKJVJlbW92ZURldmljZUZyb21NdWx0aWNh",
+            "c3RHcm91cFJlcXVlc3QSGgoSbXVsdGljYXN0X2dyb3VwX2lkGAEgASgJEg8K",
+            "B2Rldl9ldWkYAiABKAkiUwohQWRkR2F0ZXdheVRvTXVsdGljYXN0R3JvdXBS",
+            "ZXF1ZXN0EhoKEm11bHRpY2FzdF9ncm91cF9pZBgBIAEoCRISCgpnYXRld2F5",
+            "X2lkGAIgASgJIlgKJlJlbW92ZUdhdGV3YXlGcm9tTXVsdGljYXN0R3JvdXBS",
+            "ZXF1ZXN0EhoKEm11bHRpY2FzdF9ncm91cF9pZBgBIAEoCRISCgpnYXRld2F5",
+            "X2lkGAIgASgJImIKF011bHRpY2FzdEdyb3VwUXVldWVJdGVtEhoKEm11bHRp",
+            "Y2FzdF9ncm91cF9pZBgBIAEoCRINCgVmX2NudBgCIAEoDRIOCgZmX3BvcnQY",
+            "AyABKA0SDAoEZGF0YRgEIAEoDCJZCiVFbnF1ZXVlTXVsdGljYXN0R3JvdXBR",
+            "dWV1ZUl0ZW1SZXF1ZXN0EjAKCnF1ZXVlX2l0ZW0YASABKAsyHC5hcGkuTXVs",
+            "dGljYXN0R3JvdXBRdWV1ZUl0ZW0iNwomRW5xdWV1ZU11bHRpY2FzdEdyb3Vw",
+            "UXVldWVJdGVtUmVzcG9uc2USDQoFZl9jbnQYASABKA0iPQofRmx1c2hNdWx0",
+            "aWNhc3RHcm91cFF1ZXVlUmVxdWVzdBIaChJtdWx0aWNhc3RfZ3JvdXBfaWQY",
+            "ASABKAkiPAoeTGlzdE11bHRpY2FzdEdyb3VwUXVldWVSZXF1ZXN0EhoKEm11",
+            "bHRpY2FzdF9ncm91cF9pZBgBIAEoCSJOCh9MaXN0TXVsdGljYXN0R3JvdXBR",
+            "dWV1ZVJlc3BvbnNlEisKBWl0ZW1zGAEgAygLMhwuYXBpLk11bHRpY2FzdEdy",
+            "b3VwUXVldWVJdGVtKi4KEk11bHRpY2FzdEdyb3VwVHlwZRILCgdDTEFTU19D",
+            "EAASCwoHQ0xBU1NfQhABKjcKHE11bHRpY2FzdEdyb3VwU2NoZWR1bGluZ1R5",
+            "cGUSCQoFREVMQVkQABIMCghHUFNfVElNRRABMt0MChVNdWx0aWNhc3RHcm91",
+            "cFNlcnZpY2USbwoGQ3JlYXRlEiAuYXBpLkNyZWF0ZU11bHRpY2FzdEdyb3Vw",
+            "UmVxdWVzdBohLmFwaS5DcmVhdGVNdWx0aWNhc3RHcm91cFJlc3BvbnNlIiCC",
+            "0+STAhoiFS9hcGkvbXVsdGljYXN0LWdyb3VwczoBKhJoCgNHZXQSHS5hcGku",
+            "R2V0TXVsdGljYXN0R3JvdXBSZXF1ZXN0Gh4uYXBpLkdldE11bHRpY2FzdEdy",
+            "b3VwUmVzcG9uc2UiIoLT5JMCHBIaL2FwaS9tdWx0aWNhc3QtZ3JvdXBzL3tp",
+            "ZH0SeQoGVXBkYXRlEiAuYXBpLlVwZGF0ZU11bHRpY2FzdEdyb3VwUmVxdWVz",
+            "dBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSI1gtPkkwIvGiovYXBpL211bHRp",
+            "Y2FzdC1ncm91cHMve211bHRpY2FzdF9ncm91cC5pZH06ASoSZgoGRGVsZXRl",
+            "EiAuYXBpLkRlbGV0ZU11bHRpY2FzdEdyb3VwUmVxdWVzdBoWLmdvb2dsZS5w",
+            "cm90b2J1Zi5FbXB0eSIigtPkkwIcKhovYXBpL211bHRpY2FzdC1ncm91cHMv",
+            "e2lkfRJoCgRMaXN0Eh8uYXBpLkxpc3RNdWx0aWNhc3RHcm91cHNSZXF1ZXN0",
+            "GiAuYXBpLkxpc3RNdWx0aWNhc3RHcm91cHNSZXNwb25zZSIdgtPkkwIXEhUv",
+            "YXBpL211bHRpY2FzdC1ncm91cHMSiQEKCUFkZERldmljZRIlLmFwaS5BZGRE",
+            "ZXZpY2VUb011bHRpY2FzdEdyb3VwUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1",
+            "Zi5FbXB0eSI9gtPkkwI3IjIvYXBpL211bHRpY2FzdC1ncm91cHMve211bHRp",
+            "Y2FzdF9ncm91cF9pZH0vZGV2aWNlczoBKhKYAQoMUmVtb3ZlRGV2aWNlEiou",
+            "YXBpLlJlbW92ZURldmljZUZyb21NdWx0aWNhc3RHcm91cFJlcXVlc3QaFi5n",
+            "b29nbGUucHJvdG9idWYuRW1wdHkiRILT5JMCPio8L2FwaS9tdWx0aWNhc3Qt",
+            "Z3JvdXBzL3ttdWx0aWNhc3RfZ3JvdXBfaWR9L2RldmljZXMve2Rldl9ldWl9",
+            "EowBCgpBZGRHYXRld2F5EiYuYXBpLkFkZEdhdGV3YXlUb011bHRpY2FzdEdy",
+            "b3VwUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSI+gtPkkwI4IjMv",
+            "YXBpL211bHRpY2FzdC1ncm91cHMve211bHRpY2FzdF9ncm91cF9pZH0vZ2F0",
+            "ZXdheXM6ASoSngEKDVJlbW92ZUdhdGV3YXkSKy5hcGkuUmVtb3ZlR2F0ZXdh",
+            "eUZyb21NdWx0aWNhc3RHcm91cFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYu",
+            "RW1wdHkiSILT5JMCQipAL2FwaS9tdWx0aWNhc3QtZ3JvdXBzL3ttdWx0aWNh",
+            "c3RfZ3JvdXBfaWR9L2dhdGV3YXlzL3tnYXRld2F5X2lkfRKqAQoHRW5xdWV1",
+            "ZRIqLmFwaS5FbnF1ZXVlTXVsdGljYXN0R3JvdXBRdWV1ZUl0ZW1SZXF1ZXN0",
+            "GisuYXBpLkVucXVldWVNdWx0aWNhc3RHcm91cFF1ZXVlSXRlbVJlc3BvbnNl",
+            "IkaC0+STAkAiOy9hcGkvbXVsdGljYXN0LWdyb3Vwcy97cXVldWVfaXRlbS5t",
+            "dWx0aWNhc3RfZ3JvdXBfaWR9L3F1ZXVlOgEqEoQBCgpGbHVzaFF1ZXVlEiQu",
+            "YXBpLkZsdXNoTXVsdGljYXN0R3JvdXBRdWV1ZVJlcXVlc3QaFi5nb29nbGUu",
+            "cHJvdG9idWYuRW1wdHkiOILT5JMCMiowL2FwaS9tdWx0aWNhc3QtZ3JvdXBz",
+            "L3ttdWx0aWNhc3RfZ3JvdXBfaWR9L3F1ZXVlEpABCglMaXN0UXVldWUSIy5h",
+            "cGkuTGlzdE11bHRpY2FzdEdyb3VwUXVldWVSZXF1ZXN0GiQuYXBpLkxpc3RN",
+            "dWx0aWNhc3RHcm91cFF1ZXVlUmVzcG9uc2UiOILT5JMCMhIwL2FwaS9tdWx0",
+            "aWNhc3QtZ3JvdXBzL3ttdWx0aWNhc3RfZ3JvdXBfaWR9L3F1ZXVlQmsKEWlv",
+            "LmNoaXJwc3RhY2suYXBpQhNNdWx0aWNhc3RHcm91cFByb3RvUAFaLmdpdGh1",
+            "Yi5jb20vY2hpcnBzdGFjay9jaGlycHN0YWNrL2FwaS9nby92NC9hcGmqAg5D",
+            "aGlycHN0YWNrLkFwaWIGcHJvdG8z"));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Api.AnnotationsReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Chirpstack.Common.CommonReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Chirpstack.Api.MulticastGroupType), typeof(global::Chirpstack.Api.MulticastGroupSchedulingType), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.MulticastGroup), global::Chirpstack.Api.MulticastGroup.Parser, new[]{ "Id", "Name", "ApplicationId", "Region", "McAddr", "McNwkSKey", "McAppSKey", "FCnt", "GroupType", "Dr", "Frequency", "ClassBPingSlotPeriod", "ClassCSchedulingType" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.MulticastGroupListItem), global::Chirpstack.Api.MulticastGroupListItem.Parser, new[]{ "Id", "CreatedAt", "UpdatedAt", "Name", "Region", "GroupType" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateMulticastGroupRequest), global::Chirpstack.Api.CreateMulticastGroupRequest.Parser, new[]{ "MulticastGroup" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateMulticastGroupResponse), global::Chirpstack.Api.CreateMulticastGroupResponse.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetMulticastGroupRequest), global::Chirpstack.Api.GetMulticastGroupRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetMulticastGroupResponse), global::Chirpstack.Api.GetMulticastGroupResponse.Parser, new[]{ "MulticastGroup", "CreatedAt", "UpdatedAt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateMulticastGroupRequest), global::Chirpstack.Api.UpdateMulticastGroupRequest.Parser, new[]{ "MulticastGroup" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteMulticastGroupRequest), global::Chirpstack.Api.DeleteMulticastGroupRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListMulticastGroupsRequest), global::Chirpstack.Api.ListMulticastGroupsRequest.Parser, new[]{ "Limit", "Offset", "Search", "ApplicationId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListMulticastGroupsResponse), global::Chirpstack.Api.ListMulticastGroupsResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.AddDeviceToMulticastGroupRequest), global::Chirpstack.Api.AddDeviceToMulticastGroupRequest.Parser, new[]{ "MulticastGroupId", "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest), global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest.Parser, new[]{ "MulticastGroupId", "DevEui" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.AddGatewayToMulticastGroupRequest), global::Chirpstack.Api.AddGatewayToMulticastGroupRequest.Parser, new[]{ "MulticastGroupId", "GatewayId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest), global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest.Parser, new[]{ "MulticastGroupId", "GatewayId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.MulticastGroupQueueItem), global::Chirpstack.Api.MulticastGroupQueueItem.Parser, new[]{ "MulticastGroupId", "FCnt", "FPort", "Data" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest), global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest.Parser, new[]{ "QueueItem" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse), global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse.Parser, new[]{ "FCnt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.FlushMulticastGroupQueueRequest), global::Chirpstack.Api.FlushMulticastGroupQueueRequest.Parser, new[]{ "MulticastGroupId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListMulticastGroupQueueRequest), global::Chirpstack.Api.ListMulticastGroupQueueRequest.Parser, new[]{ "MulticastGroupId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListMulticastGroupQueueResponse), global::Chirpstack.Api.ListMulticastGroupQueueResponse.Parser, new[]{ "Items" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Enums
+  public enum MulticastGroupType {
+    /// <summary>
+    /// Class C.
+    /// </summary>
+    [pbr::OriginalName("CLASS_C")] ClassC = 0,
+    /// <summary>
+    /// Class-B.
+    /// </summary>
+    [pbr::OriginalName("CLASS_B")] ClassB = 1,
+  }
+
+  public enum MulticastGroupSchedulingType {
+    /// <summary>
+    /// Delay.
+    /// If multicast downlinks must be sent through multiple gateways, then
+    /// these will be sent one by one with a delay between each gateway.
+    /// </summary>
+    [pbr::OriginalName("DELAY")] Delay = 0,
+    /// <summary>
+    /// Time.
+    /// If multicast downlinks must be sent through multiple gateways, then
+    /// these will be sent simultaneously using GPS time synchronization.
+    /// Note that this does require GPS time-synchronized LoRa gateways.
+    /// </summary>
+    [pbr::OriginalName("GPS_TIME")] GpsTime = 1,
+  }
+
+  #endregion
+
+  #region Messages
+  public sealed partial class MulticastGroup : pb::IMessage<MulticastGroup>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<MulticastGroup> _parser = new pb::MessageParser<MulticastGroup>(() => new MulticastGroup());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<MulticastGroup> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MulticastGroup() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MulticastGroup(MulticastGroup other) : this() {
+      id_ = other.id_;
+      name_ = other.name_;
+      applicationId_ = other.applicationId_;
+      region_ = other.region_;
+      mcAddr_ = other.mcAddr_;
+      mcNwkSKey_ = other.mcNwkSKey_;
+      mcAppSKey_ = other.mcAppSKey_;
+      fCnt_ = other.fCnt_;
+      groupType_ = other.groupType_;
+      dr_ = other.dr_;
+      frequency_ = other.frequency_;
+      classBPingSlotPeriod_ = other.classBPingSlotPeriod_;
+      classCSchedulingType_ = other.classCSchedulingType_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MulticastGroup Clone() {
+      return new MulticastGroup(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID (UUID).
+    /// This will be generated automatically on create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 2;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 3;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID.
+    /// After creation, this can not be updated.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "region" field.</summary>
+    public const int RegionFieldNumber = 4;
+    private global::Chirpstack.Common.Region region_ = global::Chirpstack.Common.Region.Eu868;
+    /// <summary>
+    /// Region.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Region Region {
+      get { return region_; }
+      set {
+        region_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "mc_addr" field.</summary>
+    public const int McAddrFieldNumber = 5;
+    private string mcAddr_ = "";
+    /// <summary>
+    /// Multicast address (HEX encoded DevAddr).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string McAddr {
+      get { return mcAddr_; }
+      set {
+        mcAddr_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "mc_nwk_s_key" field.</summary>
+    public const int McNwkSKeyFieldNumber = 6;
+    private string mcNwkSKey_ = "";
+    /// <summary>
+    /// Multicast network session key (HEX encoded AES128 key).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string McNwkSKey {
+      get { return mcNwkSKey_; }
+      set {
+        mcNwkSKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "mc_app_s_key" field.</summary>
+    public const int McAppSKeyFieldNumber = 7;
+    private string mcAppSKey_ = "";
+    /// <summary>
+    /// Multicast application session key (HEX encoded AES128 key).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string McAppSKey {
+      get { return mcAppSKey_; }
+      set {
+        mcAppSKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "f_cnt" field.</summary>
+    public const int FCntFieldNumber = 8;
+    private uint fCnt_;
+    /// <summary>
+    /// Frame-counter.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FCnt {
+      get { return fCnt_; }
+      set {
+        fCnt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "group_type" field.</summary>
+    public const int GroupTypeFieldNumber = 9;
+    private global::Chirpstack.Api.MulticastGroupType groupType_ = global::Chirpstack.Api.MulticastGroupType.ClassC;
+    /// <summary>
+    /// Multicast group type.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MulticastGroupType GroupType {
+      get { return groupType_; }
+      set {
+        groupType_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "dr" field.</summary>
+    public const int DrFieldNumber = 10;
+    private uint dr_;
+    /// <summary>
+    /// Data-rate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Dr {
+      get { return dr_; }
+      set {
+        dr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "frequency" field.</summary>
+    public const int FrequencyFieldNumber = 11;
+    private uint frequency_;
+    /// <summary>
+    /// Frequency (Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Frequency {
+      get { return frequency_; }
+      set {
+        frequency_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_b_ping_slot_period" field.</summary>
+    public const int ClassBPingSlotPeriodFieldNumber = 12;
+    private uint classBPingSlotPeriod_;
+    /// <summary>
+    /// Ping-slot period (only for Class-B).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ClassBPingSlotPeriod {
+      get { return classBPingSlotPeriod_; }
+      set {
+        classBPingSlotPeriod_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "class_c_scheduling_type" field.</summary>
+    public const int ClassCSchedulingTypeFieldNumber = 13;
+    private global::Chirpstack.Api.MulticastGroupSchedulingType classCSchedulingType_ = global::Chirpstack.Api.MulticastGroupSchedulingType.Delay;
+    /// <summary>
+    /// Scheduling type (only for Class-C).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MulticastGroupSchedulingType ClassCSchedulingType {
+      get { return classCSchedulingType_; }
+      set {
+        classCSchedulingType_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as MulticastGroup);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(MulticastGroup other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (Name != other.Name) return false;
+      if (ApplicationId != other.ApplicationId) return false;
+      if (Region != other.Region) return false;
+      if (McAddr != other.McAddr) return false;
+      if (McNwkSKey != other.McNwkSKey) return false;
+      if (McAppSKey != other.McAppSKey) return false;
+      if (FCnt != other.FCnt) return false;
+      if (GroupType != other.GroupType) return false;
+      if (Dr != other.Dr) return false;
+      if (Frequency != other.Frequency) return false;
+      if (ClassBPingSlotPeriod != other.ClassBPingSlotPeriod) return false;
+      if (ClassCSchedulingType != other.ClassCSchedulingType) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (Region != global::Chirpstack.Common.Region.Eu868) hash ^= Region.GetHashCode();
+      if (McAddr.Length != 0) hash ^= McAddr.GetHashCode();
+      if (McNwkSKey.Length != 0) hash ^= McNwkSKey.GetHashCode();
+      if (McAppSKey.Length != 0) hash ^= McAppSKey.GetHashCode();
+      if (FCnt != 0) hash ^= FCnt.GetHashCode();
+      if (GroupType != global::Chirpstack.Api.MulticastGroupType.ClassC) hash ^= GroupType.GetHashCode();
+      if (Dr != 0) hash ^= Dr.GetHashCode();
+      if (Frequency != 0) hash ^= Frequency.GetHashCode();
+      if (ClassBPingSlotPeriod != 0) hash ^= ClassBPingSlotPeriod.GetHashCode();
+      if (ClassCSchedulingType != global::Chirpstack.Api.MulticastGroupSchedulingType.Delay) hash ^= ClassCSchedulingType.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(ApplicationId);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Region);
+      }
+      if (McAddr.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(McAddr);
+      }
+      if (McNwkSKey.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(McNwkSKey);
+      }
+      if (McAppSKey.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(McAppSKey);
+      }
+      if (FCnt != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(FCnt);
+      }
+      if (GroupType != global::Chirpstack.Api.MulticastGroupType.ClassC) {
+        output.WriteRawTag(72);
+        output.WriteEnum((int) GroupType);
+      }
+      if (Dr != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(Dr);
+      }
+      if (Frequency != 0) {
+        output.WriteRawTag(88);
+        output.WriteUInt32(Frequency);
+      }
+      if (ClassBPingSlotPeriod != 0) {
+        output.WriteRawTag(96);
+        output.WriteUInt32(ClassBPingSlotPeriod);
+      }
+      if (ClassCSchedulingType != global::Chirpstack.Api.MulticastGroupSchedulingType.Delay) {
+        output.WriteRawTag(104);
+        output.WriteEnum((int) ClassCSchedulingType);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(ApplicationId);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Region);
+      }
+      if (McAddr.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(McAddr);
+      }
+      if (McNwkSKey.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(McNwkSKey);
+      }
+      if (McAppSKey.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(McAppSKey);
+      }
+      if (FCnt != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(FCnt);
+      }
+      if (GroupType != global::Chirpstack.Api.MulticastGroupType.ClassC) {
+        output.WriteRawTag(72);
+        output.WriteEnum((int) GroupType);
+      }
+      if (Dr != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(Dr);
+      }
+      if (Frequency != 0) {
+        output.WriteRawTag(88);
+        output.WriteUInt32(Frequency);
+      }
+      if (ClassBPingSlotPeriod != 0) {
+        output.WriteRawTag(96);
+        output.WriteUInt32(ClassBPingSlotPeriod);
+      }
+      if (ClassCSchedulingType != global::Chirpstack.Api.MulticastGroupSchedulingType.Delay) {
+        output.WriteRawTag(104);
+        output.WriteEnum((int) ClassCSchedulingType);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Region);
+      }
+      if (McAddr.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(McAddr);
+      }
+      if (McNwkSKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(McNwkSKey);
+      }
+      if (McAppSKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(McAppSKey);
+      }
+      if (FCnt != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FCnt);
+      }
+      if (GroupType != global::Chirpstack.Api.MulticastGroupType.ClassC) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) GroupType);
+      }
+      if (Dr != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Dr);
+      }
+      if (Frequency != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Frequency);
+      }
+      if (ClassBPingSlotPeriod != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ClassBPingSlotPeriod);
+      }
+      if (ClassCSchedulingType != global::Chirpstack.Api.MulticastGroupSchedulingType.Delay) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ClassCSchedulingType);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(MulticastGroup other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.Region != global::Chirpstack.Common.Region.Eu868) {
+        Region = other.Region;
+      }
+      if (other.McAddr.Length != 0) {
+        McAddr = other.McAddr;
+      }
+      if (other.McNwkSKey.Length != 0) {
+        McNwkSKey = other.McNwkSKey;
+      }
+      if (other.McAppSKey.Length != 0) {
+        McAppSKey = other.McAppSKey;
+      }
+      if (other.FCnt != 0) {
+        FCnt = other.FCnt;
+      }
+      if (other.GroupType != global::Chirpstack.Api.MulticastGroupType.ClassC) {
+        GroupType = other.GroupType;
+      }
+      if (other.Dr != 0) {
+        Dr = other.Dr;
+      }
+      if (other.Frequency != 0) {
+        Frequency = other.Frequency;
+      }
+      if (other.ClassBPingSlotPeriod != 0) {
+        ClassBPingSlotPeriod = other.ClassBPingSlotPeriod;
+      }
+      if (other.ClassCSchedulingType != global::Chirpstack.Api.MulticastGroupSchedulingType.Delay) {
+        ClassCSchedulingType = other.ClassCSchedulingType;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 32: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 42: {
+            McAddr = input.ReadString();
+            break;
+          }
+          case 50: {
+            McNwkSKey = input.ReadString();
+            break;
+          }
+          case 58: {
+            McAppSKey = input.ReadString();
+            break;
+          }
+          case 64: {
+            FCnt = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            GroupType = (global::Chirpstack.Api.MulticastGroupType) input.ReadEnum();
+            break;
+          }
+          case 80: {
+            Dr = input.ReadUInt32();
+            break;
+          }
+          case 88: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 96: {
+            ClassBPingSlotPeriod = input.ReadUInt32();
+            break;
+          }
+          case 104: {
+            ClassCSchedulingType = (global::Chirpstack.Api.MulticastGroupSchedulingType) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 32: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 42: {
+            McAddr = input.ReadString();
+            break;
+          }
+          case 50: {
+            McNwkSKey = input.ReadString();
+            break;
+          }
+          case 58: {
+            McAppSKey = input.ReadString();
+            break;
+          }
+          case 64: {
+            FCnt = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            GroupType = (global::Chirpstack.Api.MulticastGroupType) input.ReadEnum();
+            break;
+          }
+          case 80: {
+            Dr = input.ReadUInt32();
+            break;
+          }
+          case 88: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 96: {
+            ClassBPingSlotPeriod = input.ReadUInt32();
+            break;
+          }
+          case 104: {
+            ClassCSchedulingType = (global::Chirpstack.Api.MulticastGroupSchedulingType) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class MulticastGroupListItem : pb::IMessage<MulticastGroupListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<MulticastGroupListItem> _parser = new pb::MessageParser<MulticastGroupListItem>(() => new MulticastGroupListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<MulticastGroupListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MulticastGroupListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MulticastGroupListItem(MulticastGroupListItem other) : this() {
+      id_ = other.id_;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      name_ = other.name_;
+      region_ = other.region_;
+      groupType_ = other.groupType_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MulticastGroupListItem Clone() {
+      return new MulticastGroupListItem(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 4;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "region" field.</summary>
+    public const int RegionFieldNumber = 5;
+    private global::Chirpstack.Common.Region region_ = global::Chirpstack.Common.Region.Eu868;
+    /// <summary>
+    /// Region.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Region Region {
+      get { return region_; }
+      set {
+        region_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "group_type" field.</summary>
+    public const int GroupTypeFieldNumber = 6;
+    private global::Chirpstack.Api.MulticastGroupType groupType_ = global::Chirpstack.Api.MulticastGroupType.ClassC;
+    /// <summary>
+    /// Multicast group type.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MulticastGroupType GroupType {
+      get { return groupType_; }
+      set {
+        groupType_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as MulticastGroupListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(MulticastGroupListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (Name != other.Name) return false;
+      if (Region != other.Region) return false;
+      if (GroupType != other.GroupType) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Region != global::Chirpstack.Common.Region.Eu868) hash ^= Region.GetHashCode();
+      if (GroupType != global::Chirpstack.Api.MulticastGroupType.ClassC) hash ^= GroupType.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) Region);
+      }
+      if (GroupType != global::Chirpstack.Api.MulticastGroupType.ClassC) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) GroupType);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) Region);
+      }
+      if (GroupType != global::Chirpstack.Api.MulticastGroupType.ClassC) {
+        output.WriteRawTag(48);
+        output.WriteEnum((int) GroupType);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Region != global::Chirpstack.Common.Region.Eu868) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Region);
+      }
+      if (GroupType != global::Chirpstack.Api.MulticastGroupType.ClassC) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) GroupType);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(MulticastGroupListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Region != global::Chirpstack.Common.Region.Eu868) {
+        Region = other.Region;
+      }
+      if (other.GroupType != global::Chirpstack.Api.MulticastGroupType.ClassC) {
+        GroupType = other.GroupType;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Name = input.ReadString();
+            break;
+          }
+          case 40: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 48: {
+            GroupType = (global::Chirpstack.Api.MulticastGroupType) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Name = input.ReadString();
+            break;
+          }
+          case 40: {
+            Region = (global::Chirpstack.Common.Region) input.ReadEnum();
+            break;
+          }
+          case 48: {
+            GroupType = (global::Chirpstack.Api.MulticastGroupType) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateMulticastGroupRequest : pb::IMessage<CreateMulticastGroupRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateMulticastGroupRequest> _parser = new pb::MessageParser<CreateMulticastGroupRequest>(() => new CreateMulticastGroupRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateMulticastGroupRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateMulticastGroupRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateMulticastGroupRequest(CreateMulticastGroupRequest other) : this() {
+      multicastGroup_ = other.multicastGroup_ != null ? other.multicastGroup_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateMulticastGroupRequest Clone() {
+      return new CreateMulticastGroupRequest(this);
+    }
+
+    /// <summary>Field number for the "multicast_group" field.</summary>
+    public const int MulticastGroupFieldNumber = 1;
+    private global::Chirpstack.Api.MulticastGroup multicastGroup_;
+    /// <summary>
+    /// Multicast group to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MulticastGroup MulticastGroup {
+      get { return multicastGroup_; }
+      set {
+        multicastGroup_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateMulticastGroupRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateMulticastGroupRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(MulticastGroup, other.MulticastGroup)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (multicastGroup_ != null) hash ^= MulticastGroup.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (multicastGroup_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(MulticastGroup);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (multicastGroup_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(MulticastGroup);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (multicastGroup_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(MulticastGroup);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateMulticastGroupRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.multicastGroup_ != null) {
+        if (multicastGroup_ == null) {
+          MulticastGroup = new global::Chirpstack.Api.MulticastGroup();
+        }
+        MulticastGroup.MergeFrom(other.MulticastGroup);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (multicastGroup_ == null) {
+              MulticastGroup = new global::Chirpstack.Api.MulticastGroup();
+            }
+            input.ReadMessage(MulticastGroup);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (multicastGroup_ == null) {
+              MulticastGroup = new global::Chirpstack.Api.MulticastGroup();
+            }
+            input.ReadMessage(MulticastGroup);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateMulticastGroupResponse : pb::IMessage<CreateMulticastGroupResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateMulticastGroupResponse> _parser = new pb::MessageParser<CreateMulticastGroupResponse>(() => new CreateMulticastGroupResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateMulticastGroupResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateMulticastGroupResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateMulticastGroupResponse(CreateMulticastGroupResponse other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateMulticastGroupResponse Clone() {
+      return new CreateMulticastGroupResponse(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID of created multicast group (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateMulticastGroupResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateMulticastGroupResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateMulticastGroupResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetMulticastGroupRequest : pb::IMessage<GetMulticastGroupRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetMulticastGroupRequest> _parser = new pb::MessageParser<GetMulticastGroupRequest>(() => new GetMulticastGroupRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetMulticastGroupRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMulticastGroupRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMulticastGroupRequest(GetMulticastGroupRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMulticastGroupRequest Clone() {
+      return new GetMulticastGroupRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Multicast group ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetMulticastGroupRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetMulticastGroupRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetMulticastGroupRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetMulticastGroupResponse : pb::IMessage<GetMulticastGroupResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetMulticastGroupResponse> _parser = new pb::MessageParser<GetMulticastGroupResponse>(() => new GetMulticastGroupResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetMulticastGroupResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMulticastGroupResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMulticastGroupResponse(GetMulticastGroupResponse other) : this() {
+      multicastGroup_ = other.multicastGroup_ != null ? other.multicastGroup_.Clone() : null;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetMulticastGroupResponse Clone() {
+      return new GetMulticastGroupResponse(this);
+    }
+
+    /// <summary>Field number for the "multicast_group" field.</summary>
+    public const int MulticastGroupFieldNumber = 1;
+    private global::Chirpstack.Api.MulticastGroup multicastGroup_;
+    /// <summary>
+    /// Multicast group object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MulticastGroup MulticastGroup {
+      get { return multicastGroup_; }
+      set {
+        multicastGroup_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetMulticastGroupResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetMulticastGroupResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(MulticastGroup, other.MulticastGroup)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (multicastGroup_ != null) hash ^= MulticastGroup.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (multicastGroup_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(MulticastGroup);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (multicastGroup_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(MulticastGroup);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (multicastGroup_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(MulticastGroup);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetMulticastGroupResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.multicastGroup_ != null) {
+        if (multicastGroup_ == null) {
+          MulticastGroup = new global::Chirpstack.Api.MulticastGroup();
+        }
+        MulticastGroup.MergeFrom(other.MulticastGroup);
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (multicastGroup_ == null) {
+              MulticastGroup = new global::Chirpstack.Api.MulticastGroup();
+            }
+            input.ReadMessage(MulticastGroup);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (multicastGroup_ == null) {
+              MulticastGroup = new global::Chirpstack.Api.MulticastGroup();
+            }
+            input.ReadMessage(MulticastGroup);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateMulticastGroupRequest : pb::IMessage<UpdateMulticastGroupRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateMulticastGroupRequest> _parser = new pb::MessageParser<UpdateMulticastGroupRequest>(() => new UpdateMulticastGroupRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateMulticastGroupRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateMulticastGroupRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateMulticastGroupRequest(UpdateMulticastGroupRequest other) : this() {
+      multicastGroup_ = other.multicastGroup_ != null ? other.multicastGroup_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateMulticastGroupRequest Clone() {
+      return new UpdateMulticastGroupRequest(this);
+    }
+
+    /// <summary>Field number for the "multicast_group" field.</summary>
+    public const int MulticastGroupFieldNumber = 1;
+    private global::Chirpstack.Api.MulticastGroup multicastGroup_;
+    /// <summary>
+    /// Multicast group object to update.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MulticastGroup MulticastGroup {
+      get { return multicastGroup_; }
+      set {
+        multicastGroup_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateMulticastGroupRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateMulticastGroupRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(MulticastGroup, other.MulticastGroup)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (multicastGroup_ != null) hash ^= MulticastGroup.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (multicastGroup_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(MulticastGroup);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (multicastGroup_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(MulticastGroup);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (multicastGroup_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(MulticastGroup);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateMulticastGroupRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.multicastGroup_ != null) {
+        if (multicastGroup_ == null) {
+          MulticastGroup = new global::Chirpstack.Api.MulticastGroup();
+        }
+        MulticastGroup.MergeFrom(other.MulticastGroup);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (multicastGroup_ == null) {
+              MulticastGroup = new global::Chirpstack.Api.MulticastGroup();
+            }
+            input.ReadMessage(MulticastGroup);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (multicastGroup_ == null) {
+              MulticastGroup = new global::Chirpstack.Api.MulticastGroup();
+            }
+            input.ReadMessage(MulticastGroup);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteMulticastGroupRequest : pb::IMessage<DeleteMulticastGroupRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteMulticastGroupRequest> _parser = new pb::MessageParser<DeleteMulticastGroupRequest>(() => new DeleteMulticastGroupRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteMulticastGroupRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteMulticastGroupRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteMulticastGroupRequest(DeleteMulticastGroupRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteMulticastGroupRequest Clone() {
+      return new DeleteMulticastGroupRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Multicast group iD.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteMulticastGroupRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteMulticastGroupRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteMulticastGroupRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListMulticastGroupsRequest : pb::IMessage<ListMulticastGroupsRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListMulticastGroupsRequest> _parser = new pb::MessageParser<ListMulticastGroupsRequest>(() => new ListMulticastGroupsRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListMulticastGroupsRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupsRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupsRequest(ListMulticastGroupsRequest other) : this() {
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      search_ = other.search_;
+      applicationId_ = other.applicationId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupsRequest Clone() {
+      return new ListMulticastGroupsRequest(this);
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 1;
+    private uint limit_;
+    /// <summary>
+    /// Max number of multicast groups to return in the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 2;
+    private uint offset_;
+    /// <summary>
+    /// Offset in the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "search" field.</summary>
+    public const int SearchFieldNumber = 3;
+    private string search_ = "";
+    /// <summary>
+    /// If set, the given string will be used to search on name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Search {
+      get { return search_; }
+      set {
+        search_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 4;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID to list the multicast groups for.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListMulticastGroupsRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListMulticastGroupsRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      if (Search != other.Search) return false;
+      if (ApplicationId != other.ApplicationId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Limit != 0) hash ^= Limit.GetHashCode();
+      if (Offset != 0) hash ^= Offset.GetHashCode();
+      if (Search.Length != 0) hash ^= Search.GetHashCode();
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Limit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Limit);
+      }
+      if (Offset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Offset);
+      }
+      if (Search.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Search);
+      }
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListMulticastGroupsRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Limit != 0) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0) {
+        Offset = other.Offset;
+      }
+      if (other.Search.Length != 0) {
+        Search = other.Search;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListMulticastGroupsResponse : pb::IMessage<ListMulticastGroupsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListMulticastGroupsResponse> _parser = new pb::MessageParser<ListMulticastGroupsResponse>(() => new ListMulticastGroupsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListMulticastGroupsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupsResponse(ListMulticastGroupsResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupsResponse Clone() {
+      return new ListMulticastGroupsResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of multicast groups.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.MulticastGroupListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.MulticastGroupListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.MulticastGroupListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.MulticastGroupListItem>();
+    /// <summary>
+    /// Result-test.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.MulticastGroupListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListMulticastGroupsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListMulticastGroupsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListMulticastGroupsResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class AddDeviceToMulticastGroupRequest : pb::IMessage<AddDeviceToMulticastGroupRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<AddDeviceToMulticastGroupRequest> _parser = new pb::MessageParser<AddDeviceToMulticastGroupRequest>(() => new AddDeviceToMulticastGroupRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<AddDeviceToMulticastGroupRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[10]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AddDeviceToMulticastGroupRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AddDeviceToMulticastGroupRequest(AddDeviceToMulticastGroupRequest other) : this() {
+      multicastGroupId_ = other.multicastGroupId_;
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AddDeviceToMulticastGroupRequest Clone() {
+      return new AddDeviceToMulticastGroupRequest(this);
+    }
+
+    /// <summary>Field number for the "multicast_group_id" field.</summary>
+    public const int MulticastGroupIdFieldNumber = 1;
+    private string multicastGroupId_ = "";
+    /// <summary>
+    /// Multicast group ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string MulticastGroupId {
+      get { return multicastGroupId_; }
+      set {
+        multicastGroupId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 2;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (HEX encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as AddDeviceToMulticastGroupRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(AddDeviceToMulticastGroupRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (MulticastGroupId != other.MulticastGroupId) return false;
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (MulticastGroupId.Length != 0) hash ^= MulticastGroupId.GetHashCode();
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (MulticastGroupId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(MulticastGroupId);
+      }
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(AddDeviceToMulticastGroupRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.MulticastGroupId.Length != 0) {
+        MulticastGroupId = other.MulticastGroupId;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 18: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 18: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class RemoveDeviceFromMulticastGroupRequest : pb::IMessage<RemoveDeviceFromMulticastGroupRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<RemoveDeviceFromMulticastGroupRequest> _parser = new pb::MessageParser<RemoveDeviceFromMulticastGroupRequest>(() => new RemoveDeviceFromMulticastGroupRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<RemoveDeviceFromMulticastGroupRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[11]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RemoveDeviceFromMulticastGroupRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RemoveDeviceFromMulticastGroupRequest(RemoveDeviceFromMulticastGroupRequest other) : this() {
+      multicastGroupId_ = other.multicastGroupId_;
+      devEui_ = other.devEui_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RemoveDeviceFromMulticastGroupRequest Clone() {
+      return new RemoveDeviceFromMulticastGroupRequest(this);
+    }
+
+    /// <summary>Field number for the "multicast_group_id" field.</summary>
+    public const int MulticastGroupIdFieldNumber = 1;
+    private string multicastGroupId_ = "";
+    /// <summary>
+    /// Multicast group ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string MulticastGroupId {
+      get { return multicastGroupId_; }
+      set {
+        multicastGroupId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 2;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (HEX encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as RemoveDeviceFromMulticastGroupRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(RemoveDeviceFromMulticastGroupRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (MulticastGroupId != other.MulticastGroupId) return false;
+      if (DevEui != other.DevEui) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (MulticastGroupId.Length != 0) hash ^= MulticastGroupId.GetHashCode();
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(DevEui);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (MulticastGroupId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(MulticastGroupId);
+      }
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(RemoveDeviceFromMulticastGroupRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.MulticastGroupId.Length != 0) {
+        MulticastGroupId = other.MulticastGroupId;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 18: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 18: {
+            DevEui = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class AddGatewayToMulticastGroupRequest : pb::IMessage<AddGatewayToMulticastGroupRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<AddGatewayToMulticastGroupRequest> _parser = new pb::MessageParser<AddGatewayToMulticastGroupRequest>(() => new AddGatewayToMulticastGroupRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<AddGatewayToMulticastGroupRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[12]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AddGatewayToMulticastGroupRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AddGatewayToMulticastGroupRequest(AddGatewayToMulticastGroupRequest other) : this() {
+      multicastGroupId_ = other.multicastGroupId_;
+      gatewayId_ = other.gatewayId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AddGatewayToMulticastGroupRequest Clone() {
+      return new AddGatewayToMulticastGroupRequest(this);
+    }
+
+    /// <summary>Field number for the "multicast_group_id" field.</summary>
+    public const int MulticastGroupIdFieldNumber = 1;
+    private string multicastGroupId_ = "";
+    /// <summary>
+    /// Multicast group ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string MulticastGroupId {
+      get { return multicastGroupId_; }
+      set {
+        multicastGroupId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 2;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (HEX encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as AddGatewayToMulticastGroupRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(AddGatewayToMulticastGroupRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (MulticastGroupId != other.MulticastGroupId) return false;
+      if (GatewayId != other.GatewayId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (MulticastGroupId.Length != 0) hash ^= MulticastGroupId.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (MulticastGroupId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(MulticastGroupId);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(AddGatewayToMulticastGroupRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.MulticastGroupId.Length != 0) {
+        MulticastGroupId = other.MulticastGroupId;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 18: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 18: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class RemoveGatewayFromMulticastGroupRequest : pb::IMessage<RemoveGatewayFromMulticastGroupRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<RemoveGatewayFromMulticastGroupRequest> _parser = new pb::MessageParser<RemoveGatewayFromMulticastGroupRequest>(() => new RemoveGatewayFromMulticastGroupRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<RemoveGatewayFromMulticastGroupRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[13]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RemoveGatewayFromMulticastGroupRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RemoveGatewayFromMulticastGroupRequest(RemoveGatewayFromMulticastGroupRequest other) : this() {
+      multicastGroupId_ = other.multicastGroupId_;
+      gatewayId_ = other.gatewayId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RemoveGatewayFromMulticastGroupRequest Clone() {
+      return new RemoveGatewayFromMulticastGroupRequest(this);
+    }
+
+    /// <summary>Field number for the "multicast_group_id" field.</summary>
+    public const int MulticastGroupIdFieldNumber = 1;
+    private string multicastGroupId_ = "";
+    /// <summary>
+    /// Multicast group ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string MulticastGroupId {
+      get { return multicastGroupId_; }
+      set {
+        multicastGroupId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 2;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (HEX encoded).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as RemoveGatewayFromMulticastGroupRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(RemoveGatewayFromMulticastGroupRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (MulticastGroupId != other.MulticastGroupId) return false;
+      if (GatewayId != other.GatewayId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (MulticastGroupId.Length != 0) hash ^= MulticastGroupId.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (MulticastGroupId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(MulticastGroupId);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(RemoveGatewayFromMulticastGroupRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.MulticastGroupId.Length != 0) {
+        MulticastGroupId = other.MulticastGroupId;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 18: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 18: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class MulticastGroupQueueItem : pb::IMessage<MulticastGroupQueueItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<MulticastGroupQueueItem> _parser = new pb::MessageParser<MulticastGroupQueueItem>(() => new MulticastGroupQueueItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<MulticastGroupQueueItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[14]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MulticastGroupQueueItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MulticastGroupQueueItem(MulticastGroupQueueItem other) : this() {
+      multicastGroupId_ = other.multicastGroupId_;
+      fCnt_ = other.fCnt_;
+      fPort_ = other.fPort_;
+      data_ = other.data_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MulticastGroupQueueItem Clone() {
+      return new MulticastGroupQueueItem(this);
+    }
+
+    /// <summary>Field number for the "multicast_group_id" field.</summary>
+    public const int MulticastGroupIdFieldNumber = 1;
+    private string multicastGroupId_ = "";
+    /// <summary>
+    /// Multicast group ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string MulticastGroupId {
+      get { return multicastGroupId_; }
+      set {
+        multicastGroupId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "f_cnt" field.</summary>
+    public const int FCntFieldNumber = 2;
+    private uint fCnt_;
+    /// <summary>
+    /// Downlink frame-counter.
+    /// This will be automatically set on enqueue.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FCnt {
+      get { return fCnt_; }
+      set {
+        fCnt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "f_port" field.</summary>
+    public const int FPortFieldNumber = 3;
+    private uint fPort_;
+    /// <summary>
+    /// FPort (must be > 0).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FPort {
+      get { return fPort_; }
+      set {
+        fPort_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "data" field.</summary>
+    public const int DataFieldNumber = 4;
+    private pb::ByteString data_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Payload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Data {
+      get { return data_; }
+      set {
+        data_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as MulticastGroupQueueItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(MulticastGroupQueueItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (MulticastGroupId != other.MulticastGroupId) return false;
+      if (FCnt != other.FCnt) return false;
+      if (FPort != other.FPort) return false;
+      if (Data != other.Data) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (MulticastGroupId.Length != 0) hash ^= MulticastGroupId.GetHashCode();
+      if (FCnt != 0) hash ^= FCnt.GetHashCode();
+      if (FPort != 0) hash ^= FPort.GetHashCode();
+      if (Data.Length != 0) hash ^= Data.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (FCnt != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(FCnt);
+      }
+      if (FPort != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(FPort);
+      }
+      if (Data.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteBytes(Data);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (FCnt != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(FCnt);
+      }
+      if (FPort != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(FPort);
+      }
+      if (Data.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteBytes(Data);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (MulticastGroupId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(MulticastGroupId);
+      }
+      if (FCnt != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FCnt);
+      }
+      if (FPort != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FPort);
+      }
+      if (Data.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Data);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(MulticastGroupQueueItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.MulticastGroupId.Length != 0) {
+        MulticastGroupId = other.MulticastGroupId;
+      }
+      if (other.FCnt != 0) {
+        FCnt = other.FCnt;
+      }
+      if (other.FPort != 0) {
+        FPort = other.FPort;
+      }
+      if (other.Data.Length != 0) {
+        Data = other.Data;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 16: {
+            FCnt = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            FPort = input.ReadUInt32();
+            break;
+          }
+          case 34: {
+            Data = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 16: {
+            FCnt = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            FPort = input.ReadUInt32();
+            break;
+          }
+          case 34: {
+            Data = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class EnqueueMulticastGroupQueueItemRequest : pb::IMessage<EnqueueMulticastGroupQueueItemRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<EnqueueMulticastGroupQueueItemRequest> _parser = new pb::MessageParser<EnqueueMulticastGroupQueueItemRequest>(() => new EnqueueMulticastGroupQueueItemRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<EnqueueMulticastGroupQueueItemRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[15]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueMulticastGroupQueueItemRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueMulticastGroupQueueItemRequest(EnqueueMulticastGroupQueueItemRequest other) : this() {
+      queueItem_ = other.queueItem_ != null ? other.queueItem_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueMulticastGroupQueueItemRequest Clone() {
+      return new EnqueueMulticastGroupQueueItemRequest(this);
+    }
+
+    /// <summary>Field number for the "queue_item" field.</summary>
+    public const int QueueItemFieldNumber = 1;
+    private global::Chirpstack.Api.MulticastGroupQueueItem queueItem_;
+    /// <summary>
+    /// Multicast queue-item to enqueue.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.MulticastGroupQueueItem QueueItem {
+      get { return queueItem_; }
+      set {
+        queueItem_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as EnqueueMulticastGroupQueueItemRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(EnqueueMulticastGroupQueueItemRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(QueueItem, other.QueueItem)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (queueItem_ != null) hash ^= QueueItem.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (queueItem_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(QueueItem);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (queueItem_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(QueueItem);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (queueItem_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(QueueItem);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(EnqueueMulticastGroupQueueItemRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.queueItem_ != null) {
+        if (queueItem_ == null) {
+          QueueItem = new global::Chirpstack.Api.MulticastGroupQueueItem();
+        }
+        QueueItem.MergeFrom(other.QueueItem);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (queueItem_ == null) {
+              QueueItem = new global::Chirpstack.Api.MulticastGroupQueueItem();
+            }
+            input.ReadMessage(QueueItem);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (queueItem_ == null) {
+              QueueItem = new global::Chirpstack.Api.MulticastGroupQueueItem();
+            }
+            input.ReadMessage(QueueItem);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class EnqueueMulticastGroupQueueItemResponse : pb::IMessage<EnqueueMulticastGroupQueueItemResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<EnqueueMulticastGroupQueueItemResponse> _parser = new pb::MessageParser<EnqueueMulticastGroupQueueItemResponse>(() => new EnqueueMulticastGroupQueueItemResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<EnqueueMulticastGroupQueueItemResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[16]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueMulticastGroupQueueItemResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueMulticastGroupQueueItemResponse(EnqueueMulticastGroupQueueItemResponse other) : this() {
+      fCnt_ = other.fCnt_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EnqueueMulticastGroupQueueItemResponse Clone() {
+      return new EnqueueMulticastGroupQueueItemResponse(this);
+    }
+
+    /// <summary>Field number for the "f_cnt" field.</summary>
+    public const int FCntFieldNumber = 1;
+    private uint fCnt_;
+    /// <summary>
+    /// Frame-counter of the enqueued payload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FCnt {
+      get { return fCnt_; }
+      set {
+        fCnt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as EnqueueMulticastGroupQueueItemResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(EnqueueMulticastGroupQueueItemResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (FCnt != other.FCnt) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (FCnt != 0) hash ^= FCnt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (FCnt != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(FCnt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (FCnt != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(FCnt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (FCnt != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FCnt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(EnqueueMulticastGroupQueueItemResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.FCnt != 0) {
+        FCnt = other.FCnt;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            FCnt = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            FCnt = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class FlushMulticastGroupQueueRequest : pb::IMessage<FlushMulticastGroupQueueRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<FlushMulticastGroupQueueRequest> _parser = new pb::MessageParser<FlushMulticastGroupQueueRequest>(() => new FlushMulticastGroupQueueRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<FlushMulticastGroupQueueRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[17]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FlushMulticastGroupQueueRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FlushMulticastGroupQueueRequest(FlushMulticastGroupQueueRequest other) : this() {
+      multicastGroupId_ = other.multicastGroupId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FlushMulticastGroupQueueRequest Clone() {
+      return new FlushMulticastGroupQueueRequest(this);
+    }
+
+    /// <summary>Field number for the "multicast_group_id" field.</summary>
+    public const int MulticastGroupIdFieldNumber = 1;
+    private string multicastGroupId_ = "";
+    /// <summary>
+    /// Multicast group ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string MulticastGroupId {
+      get { return multicastGroupId_; }
+      set {
+        multicastGroupId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as FlushMulticastGroupQueueRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(FlushMulticastGroupQueueRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (MulticastGroupId != other.MulticastGroupId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (MulticastGroupId.Length != 0) hash ^= MulticastGroupId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (MulticastGroupId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(FlushMulticastGroupQueueRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.MulticastGroupId.Length != 0) {
+        MulticastGroupId = other.MulticastGroupId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListMulticastGroupQueueRequest : pb::IMessage<ListMulticastGroupQueueRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListMulticastGroupQueueRequest> _parser = new pb::MessageParser<ListMulticastGroupQueueRequest>(() => new ListMulticastGroupQueueRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListMulticastGroupQueueRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[18]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupQueueRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupQueueRequest(ListMulticastGroupQueueRequest other) : this() {
+      multicastGroupId_ = other.multicastGroupId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupQueueRequest Clone() {
+      return new ListMulticastGroupQueueRequest(this);
+    }
+
+    /// <summary>Field number for the "multicast_group_id" field.</summary>
+    public const int MulticastGroupIdFieldNumber = 1;
+    private string multicastGroupId_ = "";
+    /// <summary>
+    /// Multicast group ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string MulticastGroupId {
+      get { return multicastGroupId_; }
+      set {
+        multicastGroupId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListMulticastGroupQueueRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListMulticastGroupQueueRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (MulticastGroupId != other.MulticastGroupId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (MulticastGroupId.Length != 0) hash ^= MulticastGroupId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (MulticastGroupId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(MulticastGroupId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListMulticastGroupQueueRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.MulticastGroupId.Length != 0) {
+        MulticastGroupId = other.MulticastGroupId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListMulticastGroupQueueResponse : pb::IMessage<ListMulticastGroupQueueResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListMulticastGroupQueueResponse> _parser = new pb::MessageParser<ListMulticastGroupQueueResponse>(() => new ListMulticastGroupQueueResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListMulticastGroupQueueResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.MessageTypes[19]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupQueueResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupQueueResponse(ListMulticastGroupQueueResponse other) : this() {
+      items_ = other.items_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListMulticastGroupQueueResponse Clone() {
+      return new ListMulticastGroupQueueResponse(this);
+    }
+
+    /// <summary>Field number for the "items" field.</summary>
+    public const int ItemsFieldNumber = 1;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.MulticastGroupQueueItem> _repeated_items_codec
+        = pb::FieldCodec.ForMessage(10, global::Chirpstack.Api.MulticastGroupQueueItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.MulticastGroupQueueItem> items_ = new pbc::RepeatedField<global::Chirpstack.Api.MulticastGroupQueueItem>();
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.MulticastGroupQueueItem> Items {
+      get { return items_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListMulticastGroupQueueResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListMulticastGroupQueueResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if(!items_.Equals(other.items_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= items_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      items_.WriteTo(output, _repeated_items_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      items_.WriteTo(ref output, _repeated_items_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      size += items_.CalculateSize(_repeated_items_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListMulticastGroupQueueResponse other) {
+      if (other == null) {
+        return;
+      }
+      items_.Add(other.items_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            items_.AddEntriesFrom(input, _repeated_items_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            items_.AddEntriesFrom(ref input, _repeated_items_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/MulticastGroupGrpc.cs b/api/csharp/Chirpstack/api/MulticastGroupGrpc.cs
new file mode 100644
index 00000000..ab3ae60a
--- /dev/null
+++ b/api/csharp/Chirpstack/api/MulticastGroupGrpc.cs
@@ -0,0 +1,994 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/multicast_group.proto
+// </auto-generated>
+#pragma warning disable 0414, 1591, 8981
+#region Designer generated code
+
+using grpc = global::Grpc.Core;
+
+namespace Chirpstack.Api {
+  /// <summary>
+  /// MulticastGroupService is the service managing multicast-groups.
+  /// </summary>
+  public static partial class MulticastGroupService
+  {
+    static readonly string __ServiceName = "api.MulticastGroupService";
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (message is global::Google.Protobuf.IBufferMessage)
+      {
+        context.SetPayloadLength(message.CalculateSize());
+        global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
+        context.Complete();
+        return;
+      }
+      #endif
+      context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static class __Helper_MessageCache<T>
+    {
+      public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (__Helper_MessageCache<T>.IsBufferMessage)
+      {
+        return parser.ParseFrom(context.PayloadAsReadOnlySequence());
+      }
+      #endif
+      return parser.ParseFrom(context.PayloadAsNewBuffer());
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateMulticastGroupRequest> __Marshaller_api_CreateMulticastGroupRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateMulticastGroupRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateMulticastGroupResponse> __Marshaller_api_CreateMulticastGroupResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateMulticastGroupResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetMulticastGroupRequest> __Marshaller_api_GetMulticastGroupRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetMulticastGroupRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetMulticastGroupResponse> __Marshaller_api_GetMulticastGroupResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetMulticastGroupResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateMulticastGroupRequest> __Marshaller_api_UpdateMulticastGroupRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateMulticastGroupRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Google.Protobuf.WellKnownTypes.Empty> __Marshaller_google_protobuf_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Google.Protobuf.WellKnownTypes.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteMulticastGroupRequest> __Marshaller_api_DeleteMulticastGroupRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteMulticastGroupRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListMulticastGroupsRequest> __Marshaller_api_ListMulticastGroupsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListMulticastGroupsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListMulticastGroupsResponse> __Marshaller_api_ListMulticastGroupsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListMulticastGroupsResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.AddDeviceToMulticastGroupRequest> __Marshaller_api_AddDeviceToMulticastGroupRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.AddDeviceToMulticastGroupRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest> __Marshaller_api_RemoveDeviceFromMulticastGroupRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.AddGatewayToMulticastGroupRequest> __Marshaller_api_AddGatewayToMulticastGroupRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.AddGatewayToMulticastGroupRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest> __Marshaller_api_RemoveGatewayFromMulticastGroupRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest> __Marshaller_api_EnqueueMulticastGroupQueueItemRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse> __Marshaller_api_EnqueueMulticastGroupQueueItemResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.FlushMulticastGroupQueueRequest> __Marshaller_api_FlushMulticastGroupQueueRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.FlushMulticastGroupQueueRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListMulticastGroupQueueRequest> __Marshaller_api_ListMulticastGroupQueueRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListMulticastGroupQueueRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListMulticastGroupQueueResponse> __Marshaller_api_ListMulticastGroupQueueResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListMulticastGroupQueueResponse.Parser));
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateMulticastGroupRequest, global::Chirpstack.Api.CreateMulticastGroupResponse> __Method_Create = new grpc::Method<global::Chirpstack.Api.CreateMulticastGroupRequest, global::Chirpstack.Api.CreateMulticastGroupResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Create",
+        __Marshaller_api_CreateMulticastGroupRequest,
+        __Marshaller_api_CreateMulticastGroupResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetMulticastGroupRequest, global::Chirpstack.Api.GetMulticastGroupResponse> __Method_Get = new grpc::Method<global::Chirpstack.Api.GetMulticastGroupRequest, global::Chirpstack.Api.GetMulticastGroupResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Get",
+        __Marshaller_api_GetMulticastGroupRequest,
+        __Marshaller_api_GetMulticastGroupResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Update = new grpc::Method<global::Chirpstack.Api.UpdateMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Update",
+        __Marshaller_api_UpdateMulticastGroupRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Delete = new grpc::Method<global::Chirpstack.Api.DeleteMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Delete",
+        __Marshaller_api_DeleteMulticastGroupRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListMulticastGroupsRequest, global::Chirpstack.Api.ListMulticastGroupsResponse> __Method_List = new grpc::Method<global::Chirpstack.Api.ListMulticastGroupsRequest, global::Chirpstack.Api.ListMulticastGroupsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "List",
+        __Marshaller_api_ListMulticastGroupsRequest,
+        __Marshaller_api_ListMulticastGroupsResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.AddDeviceToMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_AddDevice = new grpc::Method<global::Chirpstack.Api.AddDeviceToMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "AddDevice",
+        __Marshaller_api_AddDeviceToMulticastGroupRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_RemoveDevice = new grpc::Method<global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "RemoveDevice",
+        __Marshaller_api_RemoveDeviceFromMulticastGroupRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.AddGatewayToMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_AddGateway = new grpc::Method<global::Chirpstack.Api.AddGatewayToMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "AddGateway",
+        __Marshaller_api_AddGatewayToMulticastGroupRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_RemoveGateway = new grpc::Method<global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "RemoveGateway",
+        __Marshaller_api_RemoveGatewayFromMulticastGroupRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest, global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse> __Method_Enqueue = new grpc::Method<global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest, global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Enqueue",
+        __Marshaller_api_EnqueueMulticastGroupQueueItemRequest,
+        __Marshaller_api_EnqueueMulticastGroupQueueItemResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.FlushMulticastGroupQueueRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_FlushQueue = new grpc::Method<global::Chirpstack.Api.FlushMulticastGroupQueueRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "FlushQueue",
+        __Marshaller_api_FlushMulticastGroupQueueRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListMulticastGroupQueueRequest, global::Chirpstack.Api.ListMulticastGroupQueueResponse> __Method_ListQueue = new grpc::Method<global::Chirpstack.Api.ListMulticastGroupQueueRequest, global::Chirpstack.Api.ListMulticastGroupQueueResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "ListQueue",
+        __Marshaller_api_ListMulticastGroupQueueRequest,
+        __Marshaller_api_ListMulticastGroupQueueResponse);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Chirpstack.Api.MulticastGroupReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of MulticastGroupService</summary>
+    [grpc::BindServiceMethod(typeof(MulticastGroupService), "BindService")]
+    public abstract partial class MulticastGroupServiceBase
+    {
+      /// <summary>
+      /// Create the given multicast group.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.CreateMulticastGroupResponse> Create(global::Chirpstack.Api.CreateMulticastGroupRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get returns the multicast group for the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetMulticastGroupResponse> Get(global::Chirpstack.Api.GetMulticastGroupRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update the given multicast group.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Update(global::Chirpstack.Api.UpdateMulticastGroupRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete the multicast-group with the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Delete(global::Chirpstack.Api.DeleteMulticastGroupRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// List the available multicast groups.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListMulticastGroupsResponse> List(global::Chirpstack.Api.ListMulticastGroupsRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Add a device to the multicast group.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> AddDevice(global::Chirpstack.Api.AddDeviceToMulticastGroupRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Remove a device from the multicast group.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> RemoveDevice(global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Add a gateway to the multicast group.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> AddGateway(global::Chirpstack.Api.AddGatewayToMulticastGroupRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Remove a gateway from the multicast group.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> RemoveGateway(global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Add the given item to the multicast group queue.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse> Enqueue(global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Flush the queue for the given multicast group.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> FlushQueue(global::Chirpstack.Api.FlushMulticastGroupQueueRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// List the items in the multicast group queue.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListMulticastGroupQueueResponse> ListQueue(global::Chirpstack.Api.ListMulticastGroupQueueRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for MulticastGroupService</summary>
+    public partial class MulticastGroupServiceClient : grpc::ClientBase<MulticastGroupServiceClient>
+    {
+      /// <summary>Creates a new client for MulticastGroupService</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public MulticastGroupServiceClient(grpc::ChannelBase channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for MulticastGroupService that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public MulticastGroupServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected MulticastGroupServiceClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected MulticastGroupServiceClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      /// Create the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateMulticastGroupResponse Create(global::Chirpstack.Api.CreateMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Create(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateMulticastGroupResponse Create(global::Chirpstack.Api.CreateMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Create the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateMulticastGroupResponse> CreateAsync(global::Chirpstack.Api.CreateMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateMulticastGroupResponse> CreateAsync(global::Chirpstack.Api.CreateMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Get returns the multicast group for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetMulticastGroupResponse Get(global::Chirpstack.Api.GetMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Get(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get returns the multicast group for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetMulticastGroupResponse Get(global::Chirpstack.Api.GetMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Get returns the multicast group for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetMulticastGroupResponse> GetAsync(global::Chirpstack.Api.GetMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get returns the multicast group for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetMulticastGroupResponse> GetAsync(global::Chirpstack.Api.GetMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Update the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Update(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Update the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Delete the multicast-group with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Delete(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the multicast-group with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Delete the multicast-group with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the multicast-group with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// List the available multicast groups.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListMulticastGroupsResponse List(global::Chirpstack.Api.ListMulticastGroupsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return List(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List the available multicast groups.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListMulticastGroupsResponse List(global::Chirpstack.Api.ListMulticastGroupsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// List the available multicast groups.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListMulticastGroupsResponse> ListAsync(global::Chirpstack.Api.ListMulticastGroupsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List the available multicast groups.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListMulticastGroupsResponse> ListAsync(global::Chirpstack.Api.ListMulticastGroupsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// Add a device to the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty AddDevice(global::Chirpstack.Api.AddDeviceToMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return AddDevice(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Add a device to the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty AddDevice(global::Chirpstack.Api.AddDeviceToMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_AddDevice, null, options, request);
+      }
+      /// <summary>
+      /// Add a device to the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> AddDeviceAsync(global::Chirpstack.Api.AddDeviceToMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return AddDeviceAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Add a device to the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> AddDeviceAsync(global::Chirpstack.Api.AddDeviceToMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_AddDevice, null, options, request);
+      }
+      /// <summary>
+      /// Remove a device from the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty RemoveDevice(global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return RemoveDevice(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Remove a device from the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty RemoveDevice(global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_RemoveDevice, null, options, request);
+      }
+      /// <summary>
+      /// Remove a device from the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> RemoveDeviceAsync(global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return RemoveDeviceAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Remove a device from the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> RemoveDeviceAsync(global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_RemoveDevice, null, options, request);
+      }
+      /// <summary>
+      /// Add a gateway to the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty AddGateway(global::Chirpstack.Api.AddGatewayToMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return AddGateway(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Add a gateway to the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty AddGateway(global::Chirpstack.Api.AddGatewayToMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_AddGateway, null, options, request);
+      }
+      /// <summary>
+      /// Add a gateway to the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> AddGatewayAsync(global::Chirpstack.Api.AddGatewayToMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return AddGatewayAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Add a gateway to the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> AddGatewayAsync(global::Chirpstack.Api.AddGatewayToMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_AddGateway, null, options, request);
+      }
+      /// <summary>
+      /// Remove a gateway from the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty RemoveGateway(global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return RemoveGateway(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Remove a gateway from the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty RemoveGateway(global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_RemoveGateway, null, options, request);
+      }
+      /// <summary>
+      /// Remove a gateway from the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> RemoveGatewayAsync(global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return RemoveGatewayAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Remove a gateway from the multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> RemoveGatewayAsync(global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_RemoveGateway, null, options, request);
+      }
+      /// <summary>
+      /// Add the given item to the multicast group queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse Enqueue(global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Enqueue(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Add the given item to the multicast group queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse Enqueue(global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Enqueue, null, options, request);
+      }
+      /// <summary>
+      /// Add the given item to the multicast group queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse> EnqueueAsync(global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return EnqueueAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Add the given item to the multicast group queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse> EnqueueAsync(global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Enqueue, null, options, request);
+      }
+      /// <summary>
+      /// Flush the queue for the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty FlushQueue(global::Chirpstack.Api.FlushMulticastGroupQueueRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return FlushQueue(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Flush the queue for the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty FlushQueue(global::Chirpstack.Api.FlushMulticastGroupQueueRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_FlushQueue, null, options, request);
+      }
+      /// <summary>
+      /// Flush the queue for the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> FlushQueueAsync(global::Chirpstack.Api.FlushMulticastGroupQueueRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return FlushQueueAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Flush the queue for the given multicast group.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> FlushQueueAsync(global::Chirpstack.Api.FlushMulticastGroupQueueRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_FlushQueue, null, options, request);
+      }
+      /// <summary>
+      /// List the items in the multicast group queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListMulticastGroupQueueResponse ListQueue(global::Chirpstack.Api.ListMulticastGroupQueueRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListQueue(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List the items in the multicast group queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListMulticastGroupQueueResponse ListQueue(global::Chirpstack.Api.ListMulticastGroupQueueRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_ListQueue, null, options, request);
+      }
+      /// <summary>
+      /// List the items in the multicast group queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListMulticastGroupQueueResponse> ListQueueAsync(global::Chirpstack.Api.ListMulticastGroupQueueRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListQueueAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// List the items in the multicast group queue.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListMulticastGroupQueueResponse> ListQueueAsync(global::Chirpstack.Api.ListMulticastGroupQueueRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_ListQueue, null, options, request);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected override MulticastGroupServiceClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new MulticastGroupServiceClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static grpc::ServerServiceDefinition BindService(MulticastGroupServiceBase serviceImpl)
+    {
+      return grpc::ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_Create, serviceImpl.Create)
+          .AddMethod(__Method_Get, serviceImpl.Get)
+          .AddMethod(__Method_Update, serviceImpl.Update)
+          .AddMethod(__Method_Delete, serviceImpl.Delete)
+          .AddMethod(__Method_List, serviceImpl.List)
+          .AddMethod(__Method_AddDevice, serviceImpl.AddDevice)
+          .AddMethod(__Method_RemoveDevice, serviceImpl.RemoveDevice)
+          .AddMethod(__Method_AddGateway, serviceImpl.AddGateway)
+          .AddMethod(__Method_RemoveGateway, serviceImpl.RemoveGateway)
+          .AddMethod(__Method_Enqueue, serviceImpl.Enqueue)
+          .AddMethod(__Method_FlushQueue, serviceImpl.FlushQueue)
+          .AddMethod(__Method_ListQueue, serviceImpl.ListQueue).Build();
+    }
+
+    /// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
+    /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+    /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static void BindService(grpc::ServiceBinderBase serviceBinder, MulticastGroupServiceBase serviceImpl)
+    {
+      serviceBinder.AddMethod(__Method_Create, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateMulticastGroupRequest, global::Chirpstack.Api.CreateMulticastGroupResponse>(serviceImpl.Create));
+      serviceBinder.AddMethod(__Method_Get, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetMulticastGroupRequest, global::Chirpstack.Api.GetMulticastGroupResponse>(serviceImpl.Get));
+      serviceBinder.AddMethod(__Method_Update, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Update));
+      serviceBinder.AddMethod(__Method_Delete, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Delete));
+      serviceBinder.AddMethod(__Method_List, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListMulticastGroupsRequest, global::Chirpstack.Api.ListMulticastGroupsResponse>(serviceImpl.List));
+      serviceBinder.AddMethod(__Method_AddDevice, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.AddDeviceToMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.AddDevice));
+      serviceBinder.AddMethod(__Method_RemoveDevice, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.RemoveDeviceFromMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.RemoveDevice));
+      serviceBinder.AddMethod(__Method_AddGateway, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.AddGatewayToMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.AddGateway));
+      serviceBinder.AddMethod(__Method_RemoveGateway, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.RemoveGatewayFromMulticastGroupRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.RemoveGateway));
+      serviceBinder.AddMethod(__Method_Enqueue, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.EnqueueMulticastGroupQueueItemRequest, global::Chirpstack.Api.EnqueueMulticastGroupQueueItemResponse>(serviceImpl.Enqueue));
+      serviceBinder.AddMethod(__Method_FlushQueue, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.FlushMulticastGroupQueueRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.FlushQueue));
+      serviceBinder.AddMethod(__Method_ListQueue, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListMulticastGroupQueueRequest, global::Chirpstack.Api.ListMulticastGroupQueueResponse>(serviceImpl.ListQueue));
+    }
+
+  }
+}
+#endregion
diff --git a/api/csharp/Chirpstack/api/RequestLog.cs b/api/csharp/Chirpstack/api/RequestLog.cs
new file mode 100644
index 00000000..aaa83468
--- /dev/null
+++ b/api/csharp/Chirpstack/api/RequestLog.cs
@@ -0,0 +1,310 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/request_log.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/request_log.proto</summary>
+  public static partial class RequestLogReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/request_log.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static RequestLogReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChVhcGkvcmVxdWVzdF9sb2cucHJvdG8SA2FwaRofZ29vZ2xlL3Byb3RvYnVm",
+            "L3RpbWVzdGFtcC5wcm90bxoTY29tbW9uL2NvbW1vbi5wcm90bxoLZ3cvZ3cu",
+            "cHJvdG8ijwEKClJlcXVlc3RMb2cSDwoHc2VydmljZRgBIAEoCRIOCgZtZXRo",
+            "b2QYAiABKAkSLwoIbWV0YWRhdGEYAyADKAsyHS5hcGkuUmVxdWVzdExvZy5N",
+            "ZXRhZGF0YUVudHJ5Gi8KDU1ldGFkYXRhRW50cnkSCwoDa2V5GAEgASgJEg0K",
+            "BXZhbHVlGAIgASgJOgI4AUJnChFpby5jaGlycHN0YWNrLmFwaUIPUmVxdWVz",
+            "dExvZ1Byb3RvUAFaLmdpdGh1Yi5jb20vY2hpcnBzdGFjay9jaGlycHN0YWNr",
+            "L2FwaS9nby92NC9hcGmqAg5DaGlycHN0YWNrLkFwaWIGcHJvdG8z"));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Chirpstack.Common.CommonReflection.Descriptor, global::Chirpstack.Gateway.GwReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.RequestLog), global::Chirpstack.Api.RequestLog.Parser, new[]{ "Service", "Method", "Metadata" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, })
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  public sealed partial class RequestLog : pb::IMessage<RequestLog>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<RequestLog> _parser = new pb::MessageParser<RequestLog>(() => new RequestLog());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<RequestLog> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.RequestLogReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RequestLog() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RequestLog(RequestLog other) : this() {
+      service_ = other.service_;
+      method_ = other.method_;
+      metadata_ = other.metadata_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RequestLog Clone() {
+      return new RequestLog(this);
+    }
+
+    /// <summary>Field number for the "service" field.</summary>
+    public const int ServiceFieldNumber = 1;
+    private string service_ = "";
+    /// <summary>
+    /// API service name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Service {
+      get { return service_; }
+      set {
+        service_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "method" field.</summary>
+    public const int MethodFieldNumber = 2;
+    private string method_ = "";
+    /// <summary>
+    /// API method name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Method {
+      get { return method_; }
+      set {
+        method_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "metadata" field.</summary>
+    public const int MetadataFieldNumber = 3;
+    private static readonly pbc::MapField<string, string>.Codec _map_metadata_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 26);
+    private readonly pbc::MapField<string, string> metadata_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Metadata.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Metadata {
+      get { return metadata_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as RequestLog);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(RequestLog other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Service != other.Service) return false;
+      if (Method != other.Method) return false;
+      if (!Metadata.Equals(other.Metadata)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Service.Length != 0) hash ^= Service.GetHashCode();
+      if (Method.Length != 0) hash ^= Method.GetHashCode();
+      hash ^= Metadata.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Service.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Service);
+      }
+      if (Method.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Method);
+      }
+      metadata_.WriteTo(output, _map_metadata_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Service.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Service);
+      }
+      if (Method.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Method);
+      }
+      metadata_.WriteTo(ref output, _map_metadata_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Service.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Service);
+      }
+      if (Method.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Method);
+      }
+      size += metadata_.CalculateSize(_map_metadata_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(RequestLog other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Service.Length != 0) {
+        Service = other.Service;
+      }
+      if (other.Method.Length != 0) {
+        Method = other.Method;
+      }
+      metadata_.Add(other.metadata_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Service = input.ReadString();
+            break;
+          }
+          case 18: {
+            Method = input.ReadString();
+            break;
+          }
+          case 26: {
+            metadata_.AddEntriesFrom(input, _map_metadata_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Service = input.ReadString();
+            break;
+          }
+          case 18: {
+            Method = input.ReadString();
+            break;
+          }
+          case 26: {
+            metadata_.AddEntriesFrom(ref input, _map_metadata_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/Tenant.cs b/api/csharp/Chirpstack/api/Tenant.cs
new file mode 100644
index 00000000..4fb7df71
--- /dev/null
+++ b/api/csharp/Chirpstack/api/Tenant.cs
@@ -0,0 +1,5413 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/tenant.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/tenant.proto</summary>
+  public static partial class TenantReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/tenant.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static TenantReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChBhcGkvdGVuYW50LnByb3RvEgNhcGkaHGdvb2dsZS9hcGkvYW5ub3RhdGlv",
+            "bnMucHJvdG8aH2dvb2dsZS9wcm90b2J1Zi90aW1lc3RhbXAucHJvdG8aG2dv",
+            "b2dsZS9wcm90b2J1Zi9lbXB0eS5wcm90byKhAQoGVGVuYW50EgoKAmlkGAEg",
+            "ASgJEgwKBG5hbWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkSGQoRY2Fu",
+            "X2hhdmVfZ2F0ZXdheXMYBCABKAgSGQoRbWF4X2dhdGV3YXlfY291bnQYBSAB",
+            "KA0SGAoQbWF4X2RldmljZV9jb3VudBgGIAEoDRIYChBwcml2YXRlX2dhdGV3",
+            "YXlzGAcgASgIIvQBCg5UZW5hbnRMaXN0SXRlbRIKCgJpZBgBIAEoCRIuCgpj",
+            "cmVhdGVkX2F0GAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIu",
+            "Cgp1cGRhdGVkX2F0GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFt",
+            "cBIMCgRuYW1lGAQgASgJEhkKEWNhbl9oYXZlX2dhdGV3YXlzGAUgASgIEhgK",
+            "EHByaXZhdGVfZ2F0ZXdheXMYBiABKAgSGQoRbWF4X2dhdGV3YXlfY291bnQY",
+            "ByABKA0SGAoQbWF4X2RldmljZV9jb3VudBgIIAEoDSIyChNDcmVhdGVUZW5h",
+            "bnRSZXF1ZXN0EhsKBnRlbmFudBgBIAEoCzILLmFwaS5UZW5hbnQiIgoUQ3Jl",
+            "YXRlVGVuYW50UmVzcG9uc2USCgoCaWQYASABKAkiHgoQR2V0VGVuYW50UmVx",
+            "dWVzdBIKCgJpZBgBIAEoCSKQAQoRR2V0VGVuYW50UmVzcG9uc2USGwoGdGVu",
+            "YW50GAEgASgLMgsuYXBpLlRlbmFudBIuCgpjcmVhdGVkX2F0GAIgASgLMhou",
+            "Z29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVkX2F0GAMgASgL",
+            "MhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCIyChNVcGRhdGVUZW5hbnRS",
+            "ZXF1ZXN0EhsKBnRlbmFudBgBIAEoCzILLmFwaS5UZW5hbnQiIQoTRGVsZXRl",
+            "VGVuYW50UmVxdWVzdBIKCgJpZBgBIAEoCSJUChJMaXN0VGVuYW50c1JlcXVl",
+            "c3QSDQoFbGltaXQYASABKA0SDgoGb2Zmc2V0GAIgASgNEg4KBnNlYXJjaBgD",
+            "IAEoCRIPCgd1c2VyX2lkGAQgASgJIk8KE0xpc3RUZW5hbnRzUmVzcG9uc2US",
+            "EwoLdG90YWxfY291bnQYASABKA0SIwoGcmVzdWx0GAIgAygLMhMuYXBpLlRl",
+            "bmFudExpc3RJdGVtIoQBCgpUZW5hbnRVc2VyEhEKCXRlbmFudF9pZBgBIAEo",
+            "CRIPCgd1c2VyX2lkGAIgASgJEhAKCGlzX2FkbWluGAMgASgIEhcKD2lzX2Rl",
+            "dmljZV9hZG1pbhgEIAEoCBIYChBpc19nYXRld2F5X2FkbWluGAUgASgIEg0K",
+            "BWVtYWlsGAYgASgJIuwBChJUZW5hbnRVc2VyTGlzdEl0ZW0SEQoJdGVuYW50",
+            "X2lkGAEgASgJEg8KB3VzZXJfaWQYAiABKAkSLgoKY3JlYXRlZF9hdBgDIAEo",
+            "CzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLgoKdXBkYXRlZF9hdBgE",
+            "IAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASDQoFZW1haWwYBSAB",
+            "KAkSEAoIaXNfYWRtaW4YBiABKAgSFwoPaXNfZGV2aWNlX2FkbWluGAcgASgI",
+            "EhgKEGlzX2dhdGV3YXlfYWRtaW4YCCABKAgiPAoUQWRkVGVuYW50VXNlclJl",
+            "cXVlc3QSJAoLdGVuYW50X3VzZXIYASABKAsyDy5hcGkuVGVuYW50VXNlciI6",
+            "ChRHZXRUZW5hbnRVc2VyUmVxdWVzdBIRCgl0ZW5hbnRfaWQYASABKAkSDwoH",
+            "dXNlcl9pZBgCIAEoCSKdAQoVR2V0VGVuYW50VXNlclJlc3BvbnNlEiQKC3Rl",
+            "bmFudF91c2VyGAEgASgLMg8uYXBpLlRlbmFudFVzZXISLgoKY3JlYXRlZF9h",
+            "dBgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLgoKdXBkYXRl",
+            "ZF9hdBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAiPwoXVXBk",
+            "YXRlVGVuYW50VXNlclJlcXVlc3QSJAoLdGVuYW50X3VzZXIYASABKAsyDy5h",
+            "cGkuVGVuYW50VXNlciI9ChdEZWxldGVUZW5hbnRVc2VyUmVxdWVzdBIRCgl0",
+            "ZW5hbnRfaWQYASABKAkSDwoHdXNlcl9pZBgCIAEoCSJKChZMaXN0VGVuYW50",
+            "VXNlcnNSZXF1ZXN0EhEKCXRlbmFudF9pZBgBIAEoCRINCgVsaW1pdBgCIAEo",
+            "DRIOCgZvZmZzZXQYAyABKA0iVwoXTGlzdFRlbmFudFVzZXJzUmVzcG9uc2US",
+            "EwoLdG90YWxfY291bnQYASABKA0SJwoGcmVzdWx0GAIgAygLMhcuYXBpLlRl",
+            "bmFudFVzZXJMaXN0SXRlbTKiCAoNVGVuYW50U2VydmljZRJWCgZDcmVhdGUS",
+            "GC5hcGkuQ3JlYXRlVGVuYW50UmVxdWVzdBoZLmFwaS5DcmVhdGVUZW5hbnRS",
+            "ZXNwb25zZSIXgtPkkwIRIgwvYXBpL3RlbmFudHM6ASoSTwoDR2V0EhUuYXBp",
+            "LkdldFRlbmFudFJlcXVlc3QaFi5hcGkuR2V0VGVuYW50UmVzcG9uc2UiGYLT",
+            "5JMCExIRL2FwaS90ZW5hbnRzL3tpZH0SXwoGVXBkYXRlEhguYXBpLlVwZGF0",
+            "ZVRlbmFudFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiI4LT5JMC",
+            "HRoYL2FwaS90ZW5hbnRzL3t0ZW5hbnQuaWR9OgEqElUKBkRlbGV0ZRIYLmFw",
+            "aS5EZWxldGVUZW5hbnRSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5",
+            "IhmC0+STAhMqES9hcGkvdGVuYW50cy97aWR9Ek8KBExpc3QSFy5hcGkuTGlz",
+            "dFRlbmFudHNSZXF1ZXN0GhguYXBpLkxpc3RUZW5hbnRzUmVzcG9uc2UiFILT",
+            "5JMCDhIML2FwaS90ZW5hbnRzEnMKB0FkZFVzZXISGS5hcGkuQWRkVGVuYW50",
+            "VXNlclJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiNYLT5JMCLyIq",
+            "L2FwaS90ZW5hbnRzL3t0ZW5hbnRfdXNlci50ZW5hbnRfaWR9L3VzZXJzOgEq",
+            "EnIKB0dldFVzZXISGS5hcGkuR2V0VGVuYW50VXNlclJlcXVlc3QaGi5hcGku",
+            "R2V0VGVuYW50VXNlclJlc3BvbnNlIjCC0+STAioSKC9hcGkvdGVuYW50cy97",
+            "dGVuYW50X2lkfS91c2Vycy97dXNlcl9pZH0SjwEKClVwZGF0ZVVzZXISHC5h",
+            "cGkuVXBkYXRlVGVuYW50VXNlclJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYu",
+            "RW1wdHkiS4LT5JMCRRpAL2FwaS90ZW5hbnRzL3t0ZW5hbnRfdXNlci50ZW5h",
+            "bnRfaWR9L3VzZXJzL3t0ZW5hbnRfdXNlci51c2VyX2lkfToBKhJ0CgpEZWxl",
+            "dGVVc2VyEhwuYXBpLkRlbGV0ZVRlbmFudFVzZXJSZXF1ZXN0GhYuZ29vZ2xl",
+            "LnByb3RvYnVmLkVtcHR5IjCC0+STAioqKC9hcGkvdGVuYW50cy97dGVuYW50",
+            "X2lkfS91c2Vycy97dXNlcl9pZH0SbgoJTGlzdFVzZXJzEhsuYXBpLkxpc3RU",
+            "ZW5hbnRVc2Vyc1JlcXVlc3QaHC5hcGkuTGlzdFRlbmFudFVzZXJzUmVzcG9u",
+            "c2UiJoLT5JMCIBIeL2FwaS90ZW5hbnRzL3t0ZW5hbnRfaWR9L3VzZXJzQmMK",
+            "EWlvLmNoaXJwc3RhY2suYXBpQgtUZW5hbnRQcm90b1ABWi5naXRodWIuY29t",
+            "L2NoaXJwc3RhY2svY2hpcnBzdGFjay9hcGkvZ28vdjQvYXBpqgIOQ2hpcnBz",
+            "dGFjay5BcGliBnByb3RvMw=="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Api.AnnotationsReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.Tenant), global::Chirpstack.Api.Tenant.Parser, new[]{ "Id", "Name", "Description", "CanHaveGateways", "MaxGatewayCount", "MaxDeviceCount", "PrivateGateways" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.TenantListItem), global::Chirpstack.Api.TenantListItem.Parser, new[]{ "Id", "CreatedAt", "UpdatedAt", "Name", "CanHaveGateways", "PrivateGateways", "MaxGatewayCount", "MaxDeviceCount" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateTenantRequest), global::Chirpstack.Api.CreateTenantRequest.Parser, new[]{ "Tenant" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateTenantResponse), global::Chirpstack.Api.CreateTenantResponse.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetTenantRequest), global::Chirpstack.Api.GetTenantRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetTenantResponse), global::Chirpstack.Api.GetTenantResponse.Parser, new[]{ "Tenant", "CreatedAt", "UpdatedAt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateTenantRequest), global::Chirpstack.Api.UpdateTenantRequest.Parser, new[]{ "Tenant" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteTenantRequest), global::Chirpstack.Api.DeleteTenantRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListTenantsRequest), global::Chirpstack.Api.ListTenantsRequest.Parser, new[]{ "Limit", "Offset", "Search", "UserId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListTenantsResponse), global::Chirpstack.Api.ListTenantsResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.TenantUser), global::Chirpstack.Api.TenantUser.Parser, new[]{ "TenantId", "UserId", "IsAdmin", "IsDeviceAdmin", "IsGatewayAdmin", "Email" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.TenantUserListItem), global::Chirpstack.Api.TenantUserListItem.Parser, new[]{ "TenantId", "UserId", "CreatedAt", "UpdatedAt", "Email", "IsAdmin", "IsDeviceAdmin", "IsGatewayAdmin" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.AddTenantUserRequest), global::Chirpstack.Api.AddTenantUserRequest.Parser, new[]{ "TenantUser" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetTenantUserRequest), global::Chirpstack.Api.GetTenantUserRequest.Parser, new[]{ "TenantId", "UserId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetTenantUserResponse), global::Chirpstack.Api.GetTenantUserResponse.Parser, new[]{ "TenantUser", "CreatedAt", "UpdatedAt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateTenantUserRequest), global::Chirpstack.Api.UpdateTenantUserRequest.Parser, new[]{ "TenantUser" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteTenantUserRequest), global::Chirpstack.Api.DeleteTenantUserRequest.Parser, new[]{ "TenantId", "UserId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListTenantUsersRequest), global::Chirpstack.Api.ListTenantUsersRequest.Parser, new[]{ "TenantId", "Limit", "Offset" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListTenantUsersResponse), global::Chirpstack.Api.ListTenantUsersResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  public sealed partial class Tenant : pb::IMessage<Tenant>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<Tenant> _parser = new pb::MessageParser<Tenant>(() => new Tenant());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<Tenant> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Tenant() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Tenant(Tenant other) : this() {
+      id_ = other.id_;
+      name_ = other.name_;
+      description_ = other.description_;
+      canHaveGateways_ = other.canHaveGateways_;
+      maxGatewayCount_ = other.maxGatewayCount_;
+      maxDeviceCount_ = other.maxDeviceCount_;
+      privateGateways_ = other.privateGateways_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Tenant Clone() {
+      return new Tenant(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// Note: this value will be automatically generated on create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 2;
+    private string name_ = "";
+    /// <summary>
+    /// Tenant name,
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 3;
+    private string description_ = "";
+    /// <summary>
+    /// Tenant description.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "can_have_gateways" field.</summary>
+    public const int CanHaveGatewaysFieldNumber = 4;
+    private bool canHaveGateways_;
+    /// <summary>
+    /// Can the tenant create and "own" Gateways?
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool CanHaveGateways {
+      get { return canHaveGateways_; }
+      set {
+        canHaveGateways_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "max_gateway_count" field.</summary>
+    public const int MaxGatewayCountFieldNumber = 5;
+    private uint maxGatewayCount_;
+    /// <summary>
+    /// Max. gateway count for tenant.
+    /// When set to 0, the tenant can have unlimited gateways.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint MaxGatewayCount {
+      get { return maxGatewayCount_; }
+      set {
+        maxGatewayCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "max_device_count" field.</summary>
+    public const int MaxDeviceCountFieldNumber = 6;
+    private uint maxDeviceCount_;
+    /// <summary>
+    /// Max. device count for tenant.
+    /// When set to 0, the tenant can have unlimited devices.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint MaxDeviceCount {
+      get { return maxDeviceCount_; }
+      set {
+        maxDeviceCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "private_gateways" field.</summary>
+    public const int PrivateGatewaysFieldNumber = 7;
+    private bool privateGateways_;
+    /// <summary>
+    /// Private gateways.
+    /// Gateways under this tenant are private.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool PrivateGateways {
+      get { return privateGateways_; }
+      set {
+        privateGateways_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as Tenant);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(Tenant other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (Name != other.Name) return false;
+      if (Description != other.Description) return false;
+      if (CanHaveGateways != other.CanHaveGateways) return false;
+      if (MaxGatewayCount != other.MaxGatewayCount) return false;
+      if (MaxDeviceCount != other.MaxDeviceCount) return false;
+      if (PrivateGateways != other.PrivateGateways) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      if (CanHaveGateways != false) hash ^= CanHaveGateways.GetHashCode();
+      if (MaxGatewayCount != 0) hash ^= MaxGatewayCount.GetHashCode();
+      if (MaxDeviceCount != 0) hash ^= MaxDeviceCount.GetHashCode();
+      if (PrivateGateways != false) hash ^= PrivateGateways.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (CanHaveGateways != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(CanHaveGateways);
+      }
+      if (MaxGatewayCount != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(MaxGatewayCount);
+      }
+      if (MaxDeviceCount != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(MaxDeviceCount);
+      }
+      if (PrivateGateways != false) {
+        output.WriteRawTag(56);
+        output.WriteBool(PrivateGateways);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Name);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Description);
+      }
+      if (CanHaveGateways != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(CanHaveGateways);
+      }
+      if (MaxGatewayCount != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(MaxGatewayCount);
+      }
+      if (MaxDeviceCount != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(MaxDeviceCount);
+      }
+      if (PrivateGateways != false) {
+        output.WriteRawTag(56);
+        output.WriteBool(PrivateGateways);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      if (CanHaveGateways != false) {
+        size += 1 + 1;
+      }
+      if (MaxGatewayCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(MaxGatewayCount);
+      }
+      if (MaxDeviceCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(MaxDeviceCount);
+      }
+      if (PrivateGateways != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(Tenant other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      if (other.CanHaveGateways != false) {
+        CanHaveGateways = other.CanHaveGateways;
+      }
+      if (other.MaxGatewayCount != 0) {
+        MaxGatewayCount = other.MaxGatewayCount;
+      }
+      if (other.MaxDeviceCount != 0) {
+        MaxDeviceCount = other.MaxDeviceCount;
+      }
+      if (other.PrivateGateways != false) {
+        PrivateGateways = other.PrivateGateways;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 32: {
+            CanHaveGateways = input.ReadBool();
+            break;
+          }
+          case 40: {
+            MaxGatewayCount = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            MaxDeviceCount = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            PrivateGateways = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            Name = input.ReadString();
+            break;
+          }
+          case 26: {
+            Description = input.ReadString();
+            break;
+          }
+          case 32: {
+            CanHaveGateways = input.ReadBool();
+            break;
+          }
+          case 40: {
+            MaxGatewayCount = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            MaxDeviceCount = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            PrivateGateways = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class TenantListItem : pb::IMessage<TenantListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<TenantListItem> _parser = new pb::MessageParser<TenantListItem>(() => new TenantListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<TenantListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TenantListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TenantListItem(TenantListItem other) : this() {
+      id_ = other.id_;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      name_ = other.name_;
+      canHaveGateways_ = other.canHaveGateways_;
+      privateGateways_ = other.privateGateways_;
+      maxGatewayCount_ = other.maxGatewayCount_;
+      maxDeviceCount_ = other.maxDeviceCount_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TenantListItem Clone() {
+      return new TenantListItem(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 4;
+    private string name_ = "";
+    /// <summary>
+    /// Tenant name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "can_have_gateways" field.</summary>
+    public const int CanHaveGatewaysFieldNumber = 5;
+    private bool canHaveGateways_;
+    /// <summary>
+    /// Can the tenant create and "own" Gateways?
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool CanHaveGateways {
+      get { return canHaveGateways_; }
+      set {
+        canHaveGateways_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "private_gateways" field.</summary>
+    public const int PrivateGatewaysFieldNumber = 6;
+    private bool privateGateways_;
+    /// <summary>
+    /// Gateways are private to tenant.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool PrivateGateways {
+      get { return privateGateways_; }
+      set {
+        privateGateways_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "max_gateway_count" field.</summary>
+    public const int MaxGatewayCountFieldNumber = 7;
+    private uint maxGatewayCount_;
+    /// <summary>
+    /// Max gateway count.
+    /// 0 = unlimited.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint MaxGatewayCount {
+      get { return maxGatewayCount_; }
+      set {
+        maxGatewayCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "max_device_count" field.</summary>
+    public const int MaxDeviceCountFieldNumber = 8;
+    private uint maxDeviceCount_;
+    /// <summary>
+    /// Max device count.
+    /// 0 = unlimited.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint MaxDeviceCount {
+      get { return maxDeviceCount_; }
+      set {
+        maxDeviceCount_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as TenantListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(TenantListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (Name != other.Name) return false;
+      if (CanHaveGateways != other.CanHaveGateways) return false;
+      if (PrivateGateways != other.PrivateGateways) return false;
+      if (MaxGatewayCount != other.MaxGatewayCount) return false;
+      if (MaxDeviceCount != other.MaxDeviceCount) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      if (CanHaveGateways != false) hash ^= CanHaveGateways.GetHashCode();
+      if (PrivateGateways != false) hash ^= PrivateGateways.GetHashCode();
+      if (MaxGatewayCount != 0) hash ^= MaxGatewayCount.GetHashCode();
+      if (MaxDeviceCount != 0) hash ^= MaxDeviceCount.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (CanHaveGateways != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(CanHaveGateways);
+      }
+      if (PrivateGateways != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(PrivateGateways);
+      }
+      if (MaxGatewayCount != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(MaxGatewayCount);
+      }
+      if (MaxDeviceCount != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(MaxDeviceCount);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Name);
+      }
+      if (CanHaveGateways != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(CanHaveGateways);
+      }
+      if (PrivateGateways != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(PrivateGateways);
+      }
+      if (MaxGatewayCount != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(MaxGatewayCount);
+      }
+      if (MaxDeviceCount != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(MaxDeviceCount);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      if (CanHaveGateways != false) {
+        size += 1 + 1;
+      }
+      if (PrivateGateways != false) {
+        size += 1 + 1;
+      }
+      if (MaxGatewayCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(MaxGatewayCount);
+      }
+      if (MaxDeviceCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(MaxDeviceCount);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(TenantListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      if (other.CanHaveGateways != false) {
+        CanHaveGateways = other.CanHaveGateways;
+      }
+      if (other.PrivateGateways != false) {
+        PrivateGateways = other.PrivateGateways;
+      }
+      if (other.MaxGatewayCount != 0) {
+        MaxGatewayCount = other.MaxGatewayCount;
+      }
+      if (other.MaxDeviceCount != 0) {
+        MaxDeviceCount = other.MaxDeviceCount;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Name = input.ReadString();
+            break;
+          }
+          case 40: {
+            CanHaveGateways = input.ReadBool();
+            break;
+          }
+          case 48: {
+            PrivateGateways = input.ReadBool();
+            break;
+          }
+          case 56: {
+            MaxGatewayCount = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            MaxDeviceCount = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Name = input.ReadString();
+            break;
+          }
+          case 40: {
+            CanHaveGateways = input.ReadBool();
+            break;
+          }
+          case 48: {
+            PrivateGateways = input.ReadBool();
+            break;
+          }
+          case 56: {
+            MaxGatewayCount = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            MaxDeviceCount = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateTenantRequest : pb::IMessage<CreateTenantRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateTenantRequest> _parser = new pb::MessageParser<CreateTenantRequest>(() => new CreateTenantRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateTenantRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateTenantRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateTenantRequest(CreateTenantRequest other) : this() {
+      tenant_ = other.tenant_ != null ? other.tenant_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateTenantRequest Clone() {
+      return new CreateTenantRequest(this);
+    }
+
+    /// <summary>Field number for the "tenant" field.</summary>
+    public const int TenantFieldNumber = 1;
+    private global::Chirpstack.Api.Tenant tenant_;
+    /// <summary>
+    /// Tenant object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Tenant Tenant {
+      get { return tenant_; }
+      set {
+        tenant_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateTenantRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateTenantRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Tenant, other.Tenant)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (tenant_ != null) hash ^= Tenant.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (tenant_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Tenant);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (tenant_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Tenant);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (tenant_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Tenant);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateTenantRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.tenant_ != null) {
+        if (tenant_ == null) {
+          Tenant = new global::Chirpstack.Api.Tenant();
+        }
+        Tenant.MergeFrom(other.Tenant);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (tenant_ == null) {
+              Tenant = new global::Chirpstack.Api.Tenant();
+            }
+            input.ReadMessage(Tenant);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (tenant_ == null) {
+              Tenant = new global::Chirpstack.Api.Tenant();
+            }
+            input.ReadMessage(Tenant);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateTenantResponse : pb::IMessage<CreateTenantResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateTenantResponse> _parser = new pb::MessageParser<CreateTenantResponse>(() => new CreateTenantResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateTenantResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateTenantResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateTenantResponse(CreateTenantResponse other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateTenantResponse Clone() {
+      return new CreateTenantResponse(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Tenant ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateTenantResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateTenantResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateTenantResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetTenantRequest : pb::IMessage<GetTenantRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetTenantRequest> _parser = new pb::MessageParser<GetTenantRequest>(() => new GetTenantRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetTenantRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantRequest(GetTenantRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantRequest Clone() {
+      return new GetTenantRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Tenant ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetTenantRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetTenantRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetTenantRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetTenantResponse : pb::IMessage<GetTenantResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetTenantResponse> _parser = new pb::MessageParser<GetTenantResponse>(() => new GetTenantResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetTenantResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantResponse(GetTenantResponse other) : this() {
+      tenant_ = other.tenant_ != null ? other.tenant_.Clone() : null;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantResponse Clone() {
+      return new GetTenantResponse(this);
+    }
+
+    /// <summary>Field number for the "tenant" field.</summary>
+    public const int TenantFieldNumber = 1;
+    private global::Chirpstack.Api.Tenant tenant_;
+    /// <summary>
+    /// Tenant object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Tenant Tenant {
+      get { return tenant_; }
+      set {
+        tenant_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetTenantResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetTenantResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Tenant, other.Tenant)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (tenant_ != null) hash ^= Tenant.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (tenant_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Tenant);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (tenant_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Tenant);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (tenant_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Tenant);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetTenantResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.tenant_ != null) {
+        if (tenant_ == null) {
+          Tenant = new global::Chirpstack.Api.Tenant();
+        }
+        Tenant.MergeFrom(other.Tenant);
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (tenant_ == null) {
+              Tenant = new global::Chirpstack.Api.Tenant();
+            }
+            input.ReadMessage(Tenant);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (tenant_ == null) {
+              Tenant = new global::Chirpstack.Api.Tenant();
+            }
+            input.ReadMessage(Tenant);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateTenantRequest : pb::IMessage<UpdateTenantRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateTenantRequest> _parser = new pb::MessageParser<UpdateTenantRequest>(() => new UpdateTenantRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateTenantRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateTenantRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateTenantRequest(UpdateTenantRequest other) : this() {
+      tenant_ = other.tenant_ != null ? other.tenant_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateTenantRequest Clone() {
+      return new UpdateTenantRequest(this);
+    }
+
+    /// <summary>Field number for the "tenant" field.</summary>
+    public const int TenantFieldNumber = 1;
+    private global::Chirpstack.Api.Tenant tenant_;
+    /// <summary>
+    /// Tenant object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.Tenant Tenant {
+      get { return tenant_; }
+      set {
+        tenant_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateTenantRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateTenantRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Tenant, other.Tenant)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (tenant_ != null) hash ^= Tenant.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (tenant_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Tenant);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (tenant_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Tenant);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (tenant_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Tenant);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateTenantRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.tenant_ != null) {
+        if (tenant_ == null) {
+          Tenant = new global::Chirpstack.Api.Tenant();
+        }
+        Tenant.MergeFrom(other.Tenant);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (tenant_ == null) {
+              Tenant = new global::Chirpstack.Api.Tenant();
+            }
+            input.ReadMessage(Tenant);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (tenant_ == null) {
+              Tenant = new global::Chirpstack.Api.Tenant();
+            }
+            input.ReadMessage(Tenant);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteTenantRequest : pb::IMessage<DeleteTenantRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteTenantRequest> _parser = new pb::MessageParser<DeleteTenantRequest>(() => new DeleteTenantRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteTenantRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteTenantRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteTenantRequest(DeleteTenantRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteTenantRequest Clone() {
+      return new DeleteTenantRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// Tenant ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteTenantRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteTenantRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteTenantRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListTenantsRequest : pb::IMessage<ListTenantsRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListTenantsRequest> _parser = new pb::MessageParser<ListTenantsRequest>(() => new ListTenantsRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListTenantsRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantsRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantsRequest(ListTenantsRequest other) : this() {
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      search_ = other.search_;
+      userId_ = other.userId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantsRequest Clone() {
+      return new ListTenantsRequest(this);
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 1;
+    private uint limit_;
+    /// <summary>
+    /// Max number of tenants to return in the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 2;
+    private uint offset_;
+    /// <summary>
+    /// Offset in the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "search" field.</summary>
+    public const int SearchFieldNumber = 3;
+    private string search_ = "";
+    /// <summary>
+    /// If set, the given string will be used to search on name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Search {
+      get { return search_; }
+      set {
+        search_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "user_id" field.</summary>
+    public const int UserIdFieldNumber = 4;
+    private string userId_ = "";
+    /// <summary>
+    /// If set, filters the result set to the tenants of the user.
+    /// Only global API keys are able to filter by this field.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string UserId {
+      get { return userId_; }
+      set {
+        userId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListTenantsRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListTenantsRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      if (Search != other.Search) return false;
+      if (UserId != other.UserId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Limit != 0) hash ^= Limit.GetHashCode();
+      if (Offset != 0) hash ^= Offset.GetHashCode();
+      if (Search.Length != 0) hash ^= Search.GetHashCode();
+      if (UserId.Length != 0) hash ^= UserId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (UserId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(UserId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (Search.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Search);
+      }
+      if (UserId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(UserId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Limit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Limit);
+      }
+      if (Offset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Offset);
+      }
+      if (Search.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Search);
+      }
+      if (UserId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(UserId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListTenantsRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Limit != 0) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0) {
+        Offset = other.Offset;
+      }
+      if (other.Search.Length != 0) {
+        Search = other.Search;
+      }
+      if (other.UserId.Length != 0) {
+        UserId = other.UserId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            UserId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            Search = input.ReadString();
+            break;
+          }
+          case 34: {
+            UserId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListTenantsResponse : pb::IMessage<ListTenantsResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListTenantsResponse> _parser = new pb::MessageParser<ListTenantsResponse>(() => new ListTenantsResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListTenantsResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantsResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantsResponse(ListTenantsResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantsResponse Clone() {
+      return new ListTenantsResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of tenants.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.TenantListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.TenantListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.TenantListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.TenantListItem>();
+    /// <summary>
+    /// Result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.TenantListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListTenantsResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListTenantsResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListTenantsResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class TenantUser : pb::IMessage<TenantUser>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<TenantUser> _parser = new pb::MessageParser<TenantUser>(() => new TenantUser());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<TenantUser> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[10]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TenantUser() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TenantUser(TenantUser other) : this() {
+      tenantId_ = other.tenantId_;
+      userId_ = other.userId_;
+      isAdmin_ = other.isAdmin_;
+      isDeviceAdmin_ = other.isDeviceAdmin_;
+      isGatewayAdmin_ = other.isGatewayAdmin_;
+      email_ = other.email_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TenantUser Clone() {
+      return new TenantUser(this);
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 1;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "user_id" field.</summary>
+    public const int UserIdFieldNumber = 2;
+    private string userId_ = "";
+    /// <summary>
+    /// User ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string UserId {
+      get { return userId_; }
+      set {
+        userId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "is_admin" field.</summary>
+    public const int IsAdminFieldNumber = 3;
+    private bool isAdmin_;
+    /// <summary>
+    /// User is admin within the context of the tenant.
+    /// There is no need to set the is_device_admin and is_gateway_admin flags.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsAdmin {
+      get { return isAdmin_; }
+      set {
+        isAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_device_admin" field.</summary>
+    public const int IsDeviceAdminFieldNumber = 4;
+    private bool isDeviceAdmin_;
+    /// <summary>
+    /// User is able to modify device related resources (applications,
+    /// device-profiles, devices, multicast-groups).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsDeviceAdmin {
+      get { return isDeviceAdmin_; }
+      set {
+        isDeviceAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_gateway_admin" field.</summary>
+    public const int IsGatewayAdminFieldNumber = 5;
+    private bool isGatewayAdmin_;
+    /// <summary>
+    /// User is able to modify gateways.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsGatewayAdmin {
+      get { return isGatewayAdmin_; }
+      set {
+        isGatewayAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "email" field.</summary>
+    public const int EmailFieldNumber = 6;
+    private string email_ = "";
+    /// <summary>
+    /// Email (only used on get and when adding a user to a tenant).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Email {
+      get { return email_; }
+      set {
+        email_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as TenantUser);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(TenantUser other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TenantId != other.TenantId) return false;
+      if (UserId != other.UserId) return false;
+      if (IsAdmin != other.IsAdmin) return false;
+      if (IsDeviceAdmin != other.IsDeviceAdmin) return false;
+      if (IsGatewayAdmin != other.IsGatewayAdmin) return false;
+      if (Email != other.Email) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (UserId.Length != 0) hash ^= UserId.GetHashCode();
+      if (IsAdmin != false) hash ^= IsAdmin.GetHashCode();
+      if (IsDeviceAdmin != false) hash ^= IsDeviceAdmin.GetHashCode();
+      if (IsGatewayAdmin != false) hash ^= IsGatewayAdmin.GetHashCode();
+      if (Email.Length != 0) hash ^= Email.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (UserId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(UserId);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsDeviceAdmin != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(IsDeviceAdmin);
+      }
+      if (IsGatewayAdmin != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(IsGatewayAdmin);
+      }
+      if (Email.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(Email);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (UserId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(UserId);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsDeviceAdmin != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(IsDeviceAdmin);
+      }
+      if (IsGatewayAdmin != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(IsGatewayAdmin);
+      }
+      if (Email.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(Email);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (UserId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(UserId);
+      }
+      if (IsAdmin != false) {
+        size += 1 + 1;
+      }
+      if (IsDeviceAdmin != false) {
+        size += 1 + 1;
+      }
+      if (IsGatewayAdmin != false) {
+        size += 1 + 1;
+      }
+      if (Email.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(TenantUser other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.UserId.Length != 0) {
+        UserId = other.UserId;
+      }
+      if (other.IsAdmin != false) {
+        IsAdmin = other.IsAdmin;
+      }
+      if (other.IsDeviceAdmin != false) {
+        IsDeviceAdmin = other.IsDeviceAdmin;
+      }
+      if (other.IsGatewayAdmin != false) {
+        IsGatewayAdmin = other.IsGatewayAdmin;
+      }
+      if (other.Email.Length != 0) {
+        Email = other.Email;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            UserId = input.ReadString();
+            break;
+          }
+          case 24: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 32: {
+            IsDeviceAdmin = input.ReadBool();
+            break;
+          }
+          case 40: {
+            IsGatewayAdmin = input.ReadBool();
+            break;
+          }
+          case 50: {
+            Email = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            UserId = input.ReadString();
+            break;
+          }
+          case 24: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 32: {
+            IsDeviceAdmin = input.ReadBool();
+            break;
+          }
+          case 40: {
+            IsGatewayAdmin = input.ReadBool();
+            break;
+          }
+          case 50: {
+            Email = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class TenantUserListItem : pb::IMessage<TenantUserListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<TenantUserListItem> _parser = new pb::MessageParser<TenantUserListItem>(() => new TenantUserListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<TenantUserListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[11]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TenantUserListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TenantUserListItem(TenantUserListItem other) : this() {
+      tenantId_ = other.tenantId_;
+      userId_ = other.userId_;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      email_ = other.email_;
+      isAdmin_ = other.isAdmin_;
+      isDeviceAdmin_ = other.isDeviceAdmin_;
+      isGatewayAdmin_ = other.isGatewayAdmin_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TenantUserListItem Clone() {
+      return new TenantUserListItem(this);
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 1;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "user_id" field.</summary>
+    public const int UserIdFieldNumber = 2;
+    private string userId_ = "";
+    /// <summary>
+    /// User ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string UserId {
+      get { return userId_; }
+      set {
+        userId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 4;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "email" field.</summary>
+    public const int EmailFieldNumber = 5;
+    private string email_ = "";
+    /// <summary>
+    /// Email.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Email {
+      get { return email_; }
+      set {
+        email_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "is_admin" field.</summary>
+    public const int IsAdminFieldNumber = 6;
+    private bool isAdmin_;
+    /// <summary>
+    /// User is admin within the context of the tenant.
+    /// There is no need to set the is_device_admin and is_gateway_admin flags.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsAdmin {
+      get { return isAdmin_; }
+      set {
+        isAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_device_admin" field.</summary>
+    public const int IsDeviceAdminFieldNumber = 7;
+    private bool isDeviceAdmin_;
+    /// <summary>
+    /// User is able to modify device related resources (applications,
+    /// device-profiles, devices, multicast-groups).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsDeviceAdmin {
+      get { return isDeviceAdmin_; }
+      set {
+        isDeviceAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_gateway_admin" field.</summary>
+    public const int IsGatewayAdminFieldNumber = 8;
+    private bool isGatewayAdmin_;
+    /// <summary>
+    /// User is able to modify gateways.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsGatewayAdmin {
+      get { return isGatewayAdmin_; }
+      set {
+        isGatewayAdmin_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as TenantUserListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(TenantUserListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TenantId != other.TenantId) return false;
+      if (UserId != other.UserId) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (Email != other.Email) return false;
+      if (IsAdmin != other.IsAdmin) return false;
+      if (IsDeviceAdmin != other.IsDeviceAdmin) return false;
+      if (IsGatewayAdmin != other.IsGatewayAdmin) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (UserId.Length != 0) hash ^= UserId.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (Email.Length != 0) hash ^= Email.GetHashCode();
+      if (IsAdmin != false) hash ^= IsAdmin.GetHashCode();
+      if (IsDeviceAdmin != false) hash ^= IsDeviceAdmin.GetHashCode();
+      if (IsGatewayAdmin != false) hash ^= IsGatewayAdmin.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (UserId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(UserId);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Email.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Email);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsDeviceAdmin != false) {
+        output.WriteRawTag(56);
+        output.WriteBool(IsDeviceAdmin);
+      }
+      if (IsGatewayAdmin != false) {
+        output.WriteRawTag(64);
+        output.WriteBool(IsGatewayAdmin);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (UserId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(UserId);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Email.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Email);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsDeviceAdmin != false) {
+        output.WriteRawTag(56);
+        output.WriteBool(IsDeviceAdmin);
+      }
+      if (IsGatewayAdmin != false) {
+        output.WriteRawTag(64);
+        output.WriteBool(IsGatewayAdmin);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (UserId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(UserId);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (Email.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);
+      }
+      if (IsAdmin != false) {
+        size += 1 + 1;
+      }
+      if (IsDeviceAdmin != false) {
+        size += 1 + 1;
+      }
+      if (IsGatewayAdmin != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(TenantUserListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.UserId.Length != 0) {
+        UserId = other.UserId;
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.Email.Length != 0) {
+        Email = other.Email;
+      }
+      if (other.IsAdmin != false) {
+        IsAdmin = other.IsAdmin;
+      }
+      if (other.IsDeviceAdmin != false) {
+        IsDeviceAdmin = other.IsDeviceAdmin;
+      }
+      if (other.IsGatewayAdmin != false) {
+        IsGatewayAdmin = other.IsGatewayAdmin;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            UserId = input.ReadString();
+            break;
+          }
+          case 26: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 34: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 42: {
+            Email = input.ReadString();
+            break;
+          }
+          case 48: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 56: {
+            IsDeviceAdmin = input.ReadBool();
+            break;
+          }
+          case 64: {
+            IsGatewayAdmin = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            UserId = input.ReadString();
+            break;
+          }
+          case 26: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 34: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 42: {
+            Email = input.ReadString();
+            break;
+          }
+          case 48: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 56: {
+            IsDeviceAdmin = input.ReadBool();
+            break;
+          }
+          case 64: {
+            IsGatewayAdmin = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class AddTenantUserRequest : pb::IMessage<AddTenantUserRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<AddTenantUserRequest> _parser = new pb::MessageParser<AddTenantUserRequest>(() => new AddTenantUserRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<AddTenantUserRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[12]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AddTenantUserRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AddTenantUserRequest(AddTenantUserRequest other) : this() {
+      tenantUser_ = other.tenantUser_ != null ? other.tenantUser_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AddTenantUserRequest Clone() {
+      return new AddTenantUserRequest(this);
+    }
+
+    /// <summary>Field number for the "tenant_user" field.</summary>
+    public const int TenantUserFieldNumber = 1;
+    private global::Chirpstack.Api.TenantUser tenantUser_;
+    /// <summary>
+    /// Tenant user object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.TenantUser TenantUser {
+      get { return tenantUser_; }
+      set {
+        tenantUser_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as AddTenantUserRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(AddTenantUserRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(TenantUser, other.TenantUser)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (tenantUser_ != null) hash ^= TenantUser.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (tenantUser_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(TenantUser);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (tenantUser_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(TenantUser);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (tenantUser_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TenantUser);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(AddTenantUserRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.tenantUser_ != null) {
+        if (tenantUser_ == null) {
+          TenantUser = new global::Chirpstack.Api.TenantUser();
+        }
+        TenantUser.MergeFrom(other.TenantUser);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (tenantUser_ == null) {
+              TenantUser = new global::Chirpstack.Api.TenantUser();
+            }
+            input.ReadMessage(TenantUser);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (tenantUser_ == null) {
+              TenantUser = new global::Chirpstack.Api.TenantUser();
+            }
+            input.ReadMessage(TenantUser);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetTenantUserRequest : pb::IMessage<GetTenantUserRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetTenantUserRequest> _parser = new pb::MessageParser<GetTenantUserRequest>(() => new GetTenantUserRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetTenantUserRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[13]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantUserRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantUserRequest(GetTenantUserRequest other) : this() {
+      tenantId_ = other.tenantId_;
+      userId_ = other.userId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantUserRequest Clone() {
+      return new GetTenantUserRequest(this);
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 1;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "user_id" field.</summary>
+    public const int UserIdFieldNumber = 2;
+    private string userId_ = "";
+    /// <summary>
+    /// User ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string UserId {
+      get { return userId_; }
+      set {
+        userId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetTenantUserRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetTenantUserRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TenantId != other.TenantId) return false;
+      if (UserId != other.UserId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (UserId.Length != 0) hash ^= UserId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (UserId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(UserId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (UserId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(UserId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (UserId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(UserId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetTenantUserRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.UserId.Length != 0) {
+        UserId = other.UserId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            UserId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            UserId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetTenantUserResponse : pb::IMessage<GetTenantUserResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetTenantUserResponse> _parser = new pb::MessageParser<GetTenantUserResponse>(() => new GetTenantUserResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetTenantUserResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[14]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantUserResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantUserResponse(GetTenantUserResponse other) : this() {
+      tenantUser_ = other.tenantUser_ != null ? other.tenantUser_.Clone() : null;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetTenantUserResponse Clone() {
+      return new GetTenantUserResponse(this);
+    }
+
+    /// <summary>Field number for the "tenant_user" field.</summary>
+    public const int TenantUserFieldNumber = 1;
+    private global::Chirpstack.Api.TenantUser tenantUser_;
+    /// <summary>
+    /// Tenant user object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.TenantUser TenantUser {
+      get { return tenantUser_; }
+      set {
+        tenantUser_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetTenantUserResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetTenantUserResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(TenantUser, other.TenantUser)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (tenantUser_ != null) hash ^= TenantUser.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (tenantUser_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(TenantUser);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (tenantUser_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(TenantUser);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (tenantUser_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TenantUser);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetTenantUserResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.tenantUser_ != null) {
+        if (tenantUser_ == null) {
+          TenantUser = new global::Chirpstack.Api.TenantUser();
+        }
+        TenantUser.MergeFrom(other.TenantUser);
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (tenantUser_ == null) {
+              TenantUser = new global::Chirpstack.Api.TenantUser();
+            }
+            input.ReadMessage(TenantUser);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (tenantUser_ == null) {
+              TenantUser = new global::Chirpstack.Api.TenantUser();
+            }
+            input.ReadMessage(TenantUser);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateTenantUserRequest : pb::IMessage<UpdateTenantUserRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateTenantUserRequest> _parser = new pb::MessageParser<UpdateTenantUserRequest>(() => new UpdateTenantUserRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateTenantUserRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[15]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateTenantUserRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateTenantUserRequest(UpdateTenantUserRequest other) : this() {
+      tenantUser_ = other.tenantUser_ != null ? other.tenantUser_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateTenantUserRequest Clone() {
+      return new UpdateTenantUserRequest(this);
+    }
+
+    /// <summary>Field number for the "tenant_user" field.</summary>
+    public const int TenantUserFieldNumber = 1;
+    private global::Chirpstack.Api.TenantUser tenantUser_;
+    /// <summary>
+    /// Tenant user object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.TenantUser TenantUser {
+      get { return tenantUser_; }
+      set {
+        tenantUser_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateTenantUserRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateTenantUserRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(TenantUser, other.TenantUser)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (tenantUser_ != null) hash ^= TenantUser.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (tenantUser_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(TenantUser);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (tenantUser_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(TenantUser);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (tenantUser_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TenantUser);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateTenantUserRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.tenantUser_ != null) {
+        if (tenantUser_ == null) {
+          TenantUser = new global::Chirpstack.Api.TenantUser();
+        }
+        TenantUser.MergeFrom(other.TenantUser);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (tenantUser_ == null) {
+              TenantUser = new global::Chirpstack.Api.TenantUser();
+            }
+            input.ReadMessage(TenantUser);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (tenantUser_ == null) {
+              TenantUser = new global::Chirpstack.Api.TenantUser();
+            }
+            input.ReadMessage(TenantUser);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteTenantUserRequest : pb::IMessage<DeleteTenantUserRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteTenantUserRequest> _parser = new pb::MessageParser<DeleteTenantUserRequest>(() => new DeleteTenantUserRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteTenantUserRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[16]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteTenantUserRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteTenantUserRequest(DeleteTenantUserRequest other) : this() {
+      tenantId_ = other.tenantId_;
+      userId_ = other.userId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteTenantUserRequest Clone() {
+      return new DeleteTenantUserRequest(this);
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 1;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "user_id" field.</summary>
+    public const int UserIdFieldNumber = 2;
+    private string userId_ = "";
+    /// <summary>
+    /// User ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string UserId {
+      get { return userId_; }
+      set {
+        userId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteTenantUserRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteTenantUserRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TenantId != other.TenantId) return false;
+      if (UserId != other.UserId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (UserId.Length != 0) hash ^= UserId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (UserId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(UserId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (UserId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(UserId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (UserId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(UserId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteTenantUserRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.UserId.Length != 0) {
+        UserId = other.UserId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            UserId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            UserId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListTenantUsersRequest : pb::IMessage<ListTenantUsersRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListTenantUsersRequest> _parser = new pb::MessageParser<ListTenantUsersRequest>(() => new ListTenantUsersRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListTenantUsersRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[17]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantUsersRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantUsersRequest(ListTenantUsersRequest other) : this() {
+      tenantId_ = other.tenantId_;
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantUsersRequest Clone() {
+      return new ListTenantUsersRequest(this);
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 1;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 2;
+    private uint limit_;
+    /// <summary>
+    /// Max number of tenants to return in the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 3;
+    private uint offset_;
+    /// <summary>
+    /// Offset in the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListTenantUsersRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListTenantUsersRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TenantId != other.TenantId) return false;
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (Limit != 0) hash ^= Limit.GetHashCode();
+      if (Offset != 0) hash ^= Offset.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (Limit != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(Offset);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (Limit != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(Offset);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (Limit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Limit);
+      }
+      if (Offset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Offset);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListTenantUsersRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.Limit != 0) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0) {
+        Offset = other.Offset;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 16: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 16: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListTenantUsersResponse : pb::IMessage<ListTenantUsersResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListTenantUsersResponse> _parser = new pb::MessageParser<ListTenantUsersResponse>(() => new ListTenantUsersResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListTenantUsersResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.MessageTypes[18]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantUsersResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantUsersResponse(ListTenantUsersResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListTenantUsersResponse Clone() {
+      return new ListTenantUsersResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of tenants.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.TenantUserListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.TenantUserListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.TenantUserListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.TenantUserListItem>();
+    /// <summary>
+    /// Result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.TenantUserListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListTenantUsersResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListTenantUsersResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListTenantUsersResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/TenantGrpc.cs b/api/csharp/Chirpstack/api/TenantGrpc.cs
new file mode 100644
index 00000000..9b6958b3
--- /dev/null
+++ b/api/csharp/Chirpstack/api/TenantGrpc.cs
@@ -0,0 +1,855 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/tenant.proto
+// </auto-generated>
+#pragma warning disable 0414, 1591, 8981
+#region Designer generated code
+
+using grpc = global::Grpc.Core;
+
+namespace Chirpstack.Api {
+  /// <summary>
+  /// TenantService is the service providing API methods for managing tenants.
+  /// </summary>
+  public static partial class TenantService
+  {
+    static readonly string __ServiceName = "api.TenantService";
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (message is global::Google.Protobuf.IBufferMessage)
+      {
+        context.SetPayloadLength(message.CalculateSize());
+        global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
+        context.Complete();
+        return;
+      }
+      #endif
+      context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static class __Helper_MessageCache<T>
+    {
+      public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (__Helper_MessageCache<T>.IsBufferMessage)
+      {
+        return parser.ParseFrom(context.PayloadAsReadOnlySequence());
+      }
+      #endif
+      return parser.ParseFrom(context.PayloadAsNewBuffer());
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateTenantRequest> __Marshaller_api_CreateTenantRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateTenantRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateTenantResponse> __Marshaller_api_CreateTenantResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateTenantResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetTenantRequest> __Marshaller_api_GetTenantRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetTenantRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetTenantResponse> __Marshaller_api_GetTenantResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetTenantResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateTenantRequest> __Marshaller_api_UpdateTenantRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateTenantRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Google.Protobuf.WellKnownTypes.Empty> __Marshaller_google_protobuf_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Google.Protobuf.WellKnownTypes.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteTenantRequest> __Marshaller_api_DeleteTenantRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteTenantRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListTenantsRequest> __Marshaller_api_ListTenantsRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListTenantsRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListTenantsResponse> __Marshaller_api_ListTenantsResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListTenantsResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.AddTenantUserRequest> __Marshaller_api_AddTenantUserRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.AddTenantUserRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetTenantUserRequest> __Marshaller_api_GetTenantUserRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetTenantUserRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetTenantUserResponse> __Marshaller_api_GetTenantUserResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetTenantUserResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateTenantUserRequest> __Marshaller_api_UpdateTenantUserRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateTenantUserRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteTenantUserRequest> __Marshaller_api_DeleteTenantUserRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteTenantUserRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListTenantUsersRequest> __Marshaller_api_ListTenantUsersRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListTenantUsersRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListTenantUsersResponse> __Marshaller_api_ListTenantUsersResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListTenantUsersResponse.Parser));
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateTenantRequest, global::Chirpstack.Api.CreateTenantResponse> __Method_Create = new grpc::Method<global::Chirpstack.Api.CreateTenantRequest, global::Chirpstack.Api.CreateTenantResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Create",
+        __Marshaller_api_CreateTenantRequest,
+        __Marshaller_api_CreateTenantResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetTenantRequest, global::Chirpstack.Api.GetTenantResponse> __Method_Get = new grpc::Method<global::Chirpstack.Api.GetTenantRequest, global::Chirpstack.Api.GetTenantResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Get",
+        __Marshaller_api_GetTenantRequest,
+        __Marshaller_api_GetTenantResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateTenantRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Update = new grpc::Method<global::Chirpstack.Api.UpdateTenantRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Update",
+        __Marshaller_api_UpdateTenantRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteTenantRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Delete = new grpc::Method<global::Chirpstack.Api.DeleteTenantRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Delete",
+        __Marshaller_api_DeleteTenantRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListTenantsRequest, global::Chirpstack.Api.ListTenantsResponse> __Method_List = new grpc::Method<global::Chirpstack.Api.ListTenantsRequest, global::Chirpstack.Api.ListTenantsResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "List",
+        __Marshaller_api_ListTenantsRequest,
+        __Marshaller_api_ListTenantsResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.AddTenantUserRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_AddUser = new grpc::Method<global::Chirpstack.Api.AddTenantUserRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "AddUser",
+        __Marshaller_api_AddTenantUserRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetTenantUserRequest, global::Chirpstack.Api.GetTenantUserResponse> __Method_GetUser = new grpc::Method<global::Chirpstack.Api.GetTenantUserRequest, global::Chirpstack.Api.GetTenantUserResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "GetUser",
+        __Marshaller_api_GetTenantUserRequest,
+        __Marshaller_api_GetTenantUserResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateTenantUserRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdateUser = new grpc::Method<global::Chirpstack.Api.UpdateTenantUserRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdateUser",
+        __Marshaller_api_UpdateTenantUserRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteTenantUserRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_DeleteUser = new grpc::Method<global::Chirpstack.Api.DeleteTenantUserRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "DeleteUser",
+        __Marshaller_api_DeleteTenantUserRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListTenantUsersRequest, global::Chirpstack.Api.ListTenantUsersResponse> __Method_ListUsers = new grpc::Method<global::Chirpstack.Api.ListTenantUsersRequest, global::Chirpstack.Api.ListTenantUsersResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "ListUsers",
+        __Marshaller_api_ListTenantUsersRequest,
+        __Marshaller_api_ListTenantUsersResponse);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Chirpstack.Api.TenantReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of TenantService</summary>
+    [grpc::BindServiceMethod(typeof(TenantService), "BindService")]
+    public abstract partial class TenantServiceBase
+    {
+      /// <summary>
+      /// Create a new tenant.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.CreateTenantResponse> Create(global::Chirpstack.Api.CreateTenantRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the tenant for the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetTenantResponse> Get(global::Chirpstack.Api.GetTenantRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update the given tenant.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Update(global::Chirpstack.Api.UpdateTenantRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete the tenant with the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Delete(global::Chirpstack.Api.DeleteTenantRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the list of tenants.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListTenantsResponse> List(global::Chirpstack.Api.ListTenantsRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Add an user to the tenant.
+      /// Note: the user must already exist.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> AddUser(global::Chirpstack.Api.AddTenantUserRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the the tenant user for the given tenant and user IDs.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetTenantUserResponse> GetUser(global::Chirpstack.Api.GetTenantUserRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update the given tenant user.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdateUser(global::Chirpstack.Api.UpdateTenantUserRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete the given tenant user.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> DeleteUser(global::Chirpstack.Api.DeleteTenantUserRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the list of tenant users.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListTenantUsersResponse> ListUsers(global::Chirpstack.Api.ListTenantUsersRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for TenantService</summary>
+    public partial class TenantServiceClient : grpc::ClientBase<TenantServiceClient>
+    {
+      /// <summary>Creates a new client for TenantService</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public TenantServiceClient(grpc::ChannelBase channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for TenantService that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public TenantServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected TenantServiceClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected TenantServiceClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      /// Create a new tenant.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateTenantResponse Create(global::Chirpstack.Api.CreateTenantRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Create(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create a new tenant.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateTenantResponse Create(global::Chirpstack.Api.CreateTenantRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Create a new tenant.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateTenantResponse> CreateAsync(global::Chirpstack.Api.CreateTenantRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create a new tenant.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateTenantResponse> CreateAsync(global::Chirpstack.Api.CreateTenantRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Get the tenant for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetTenantResponse Get(global::Chirpstack.Api.GetTenantRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Get(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the tenant for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetTenantResponse Get(global::Chirpstack.Api.GetTenantRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Get the tenant for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetTenantResponse> GetAsync(global::Chirpstack.Api.GetTenantRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the tenant for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetTenantResponse> GetAsync(global::Chirpstack.Api.GetTenantRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Update the given tenant.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateTenantRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Update(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given tenant.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateTenantRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Update the given tenant.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateTenantRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given tenant.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateTenantRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Delete the tenant with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteTenantRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Delete(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the tenant with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteTenantRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Delete the tenant with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteTenantRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the tenant with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteTenantRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of tenants.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListTenantsResponse List(global::Chirpstack.Api.ListTenantsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return List(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of tenants.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListTenantsResponse List(global::Chirpstack.Api.ListTenantsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of tenants.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListTenantsResponse> ListAsync(global::Chirpstack.Api.ListTenantsRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of tenants.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListTenantsResponse> ListAsync(global::Chirpstack.Api.ListTenantsRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// Add an user to the tenant.
+      /// Note: the user must already exist.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty AddUser(global::Chirpstack.Api.AddTenantUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return AddUser(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Add an user to the tenant.
+      /// Note: the user must already exist.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty AddUser(global::Chirpstack.Api.AddTenantUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_AddUser, null, options, request);
+      }
+      /// <summary>
+      /// Add an user to the tenant.
+      /// Note: the user must already exist.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> AddUserAsync(global::Chirpstack.Api.AddTenantUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return AddUserAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Add an user to the tenant.
+      /// Note: the user must already exist.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> AddUserAsync(global::Chirpstack.Api.AddTenantUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_AddUser, null, options, request);
+      }
+      /// <summary>
+      /// Get the the tenant user for the given tenant and user IDs.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetTenantUserResponse GetUser(global::Chirpstack.Api.GetTenantUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetUser(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the the tenant user for the given tenant and user IDs.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetTenantUserResponse GetUser(global::Chirpstack.Api.GetTenantUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_GetUser, null, options, request);
+      }
+      /// <summary>
+      /// Get the the tenant user for the given tenant and user IDs.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetTenantUserResponse> GetUserAsync(global::Chirpstack.Api.GetTenantUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetUserAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the the tenant user for the given tenant and user IDs.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetTenantUserResponse> GetUserAsync(global::Chirpstack.Api.GetTenantUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_GetUser, null, options, request);
+      }
+      /// <summary>
+      /// Update the given tenant user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateUser(global::Chirpstack.Api.UpdateTenantUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateUser(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given tenant user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdateUser(global::Chirpstack.Api.UpdateTenantUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdateUser, null, options, request);
+      }
+      /// <summary>
+      /// Update the given tenant user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateUserAsync(global::Chirpstack.Api.UpdateTenantUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateUserAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given tenant user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateUserAsync(global::Chirpstack.Api.UpdateTenantUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdateUser, null, options, request);
+      }
+      /// <summary>
+      /// Delete the given tenant user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteUser(global::Chirpstack.Api.DeleteTenantUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteUser(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the given tenant user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty DeleteUser(global::Chirpstack.Api.DeleteTenantUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_DeleteUser, null, options, request);
+      }
+      /// <summary>
+      /// Delete the given tenant user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteUserAsync(global::Chirpstack.Api.DeleteTenantUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteUserAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the given tenant user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteUserAsync(global::Chirpstack.Api.DeleteTenantUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_DeleteUser, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of tenant users.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListTenantUsersResponse ListUsers(global::Chirpstack.Api.ListTenantUsersRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListUsers(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of tenant users.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListTenantUsersResponse ListUsers(global::Chirpstack.Api.ListTenantUsersRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_ListUsers, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of tenant users.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListTenantUsersResponse> ListUsersAsync(global::Chirpstack.Api.ListTenantUsersRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListUsersAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of tenant users.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListTenantUsersResponse> ListUsersAsync(global::Chirpstack.Api.ListTenantUsersRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_ListUsers, null, options, request);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected override TenantServiceClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new TenantServiceClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static grpc::ServerServiceDefinition BindService(TenantServiceBase serviceImpl)
+    {
+      return grpc::ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_Create, serviceImpl.Create)
+          .AddMethod(__Method_Get, serviceImpl.Get)
+          .AddMethod(__Method_Update, serviceImpl.Update)
+          .AddMethod(__Method_Delete, serviceImpl.Delete)
+          .AddMethod(__Method_List, serviceImpl.List)
+          .AddMethod(__Method_AddUser, serviceImpl.AddUser)
+          .AddMethod(__Method_GetUser, serviceImpl.GetUser)
+          .AddMethod(__Method_UpdateUser, serviceImpl.UpdateUser)
+          .AddMethod(__Method_DeleteUser, serviceImpl.DeleteUser)
+          .AddMethod(__Method_ListUsers, serviceImpl.ListUsers).Build();
+    }
+
+    /// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
+    /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+    /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static void BindService(grpc::ServiceBinderBase serviceBinder, TenantServiceBase serviceImpl)
+    {
+      serviceBinder.AddMethod(__Method_Create, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateTenantRequest, global::Chirpstack.Api.CreateTenantResponse>(serviceImpl.Create));
+      serviceBinder.AddMethod(__Method_Get, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetTenantRequest, global::Chirpstack.Api.GetTenantResponse>(serviceImpl.Get));
+      serviceBinder.AddMethod(__Method_Update, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateTenantRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Update));
+      serviceBinder.AddMethod(__Method_Delete, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteTenantRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Delete));
+      serviceBinder.AddMethod(__Method_List, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListTenantsRequest, global::Chirpstack.Api.ListTenantsResponse>(serviceImpl.List));
+      serviceBinder.AddMethod(__Method_AddUser, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.AddTenantUserRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.AddUser));
+      serviceBinder.AddMethod(__Method_GetUser, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetTenantUserRequest, global::Chirpstack.Api.GetTenantUserResponse>(serviceImpl.GetUser));
+      serviceBinder.AddMethod(__Method_UpdateUser, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateTenantUserRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdateUser));
+      serviceBinder.AddMethod(__Method_DeleteUser, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteTenantUserRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.DeleteUser));
+      serviceBinder.AddMethod(__Method_ListUsers, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListTenantUsersRequest, global::Chirpstack.Api.ListTenantUsersResponse>(serviceImpl.ListUsers));
+    }
+
+  }
+}
+#endregion
diff --git a/api/csharp/Chirpstack/api/User.cs b/api/csharp/Chirpstack/api/User.cs
new file mode 100644
index 00000000..87d1283a
--- /dev/null
+++ b/api/csharp/Chirpstack/api/User.cs
@@ -0,0 +1,3198 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/user.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Api {
+
+  /// <summary>Holder for reflection information generated from api/user.proto</summary>
+  public static partial class UserReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for api/user.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static UserReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "Cg5hcGkvdXNlci5wcm90bxIDYXBpGhxnb29nbGUvYXBpL2Fubm90YXRpb25z",
+            "LnByb3RvGh9nb29nbGUvcHJvdG9idWYvdGltZXN0YW1wLnByb3RvGhtnb29n",
+            "bGUvcHJvdG9idWYvZW1wdHkucHJvdG8iVAoEVXNlchIKCgJpZBgBIAEoCRIQ",
+            "Cghpc19hZG1pbhgEIAEoCBIRCglpc19hY3RpdmUYBSABKAgSDQoFZW1haWwY",
+            "BiABKAkSDAoEbm90ZRgHIAEoCSKuAQoMVXNlckxpc3RJdGVtEgoKAmlkGAEg",
+            "ASgJEi4KCmNyZWF0ZWRfYXQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGlt",
+            "ZXN0YW1wEi4KCnVwZGF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYu",
+            "VGltZXN0YW1wEg0KBWVtYWlsGAQgASgJEhAKCGlzX2FkbWluGAUgASgIEhEK",
+            "CWlzX2FjdGl2ZRgGIAEoCCJkCgpVc2VyVGVuYW50EhEKCXRlbmFudF9pZBgB",
+            "IAEoCRIQCghpc19hZG1pbhgCIAEoCBIXCg9pc19kZXZpY2VfYWRtaW4YAyAB",
+            "KAgSGAoQaXNfZ2F0ZXdheV9hZG1pbhgEIAEoCCJgChFDcmVhdGVVc2VyUmVx",
+            "dWVzdBIXCgR1c2VyGAEgASgLMgkuYXBpLlVzZXISEAoIcGFzc3dvcmQYAiAB",
+            "KAkSIAoHdGVuYW50cxgDIAMoCzIPLmFwaS5Vc2VyVGVuYW50IiAKEkNyZWF0",
+            "ZVVzZXJSZXNwb25zZRIKCgJpZBgBIAEoCSIcCg5HZXRVc2VyUmVxdWVzdBIK",
+            "CgJpZBgBIAEoCSKKAQoPR2V0VXNlclJlc3BvbnNlEhcKBHVzZXIYASABKAsy",
+            "CS5hcGkuVXNlchIuCgpjcmVhdGVkX2F0GAIgASgLMhouZ29vZ2xlLnByb3Rv",
+            "YnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVkX2F0GAMgASgLMhouZ29vZ2xlLnBy",
+            "b3RvYnVmLlRpbWVzdGFtcCIsChFVcGRhdGVVc2VyUmVxdWVzdBIXCgR1c2Vy",
+            "GAEgASgLMgkuYXBpLlVzZXIiHwoRRGVsZXRlVXNlclJlcXVlc3QSCgoCaWQY",
+            "ASABKAkiMQoQTGlzdFVzZXJzUmVxdWVzdBINCgVsaW1pdBgBIAEoDRIOCgZv",
+            "ZmZzZXQYAiABKA0iSwoRTGlzdFVzZXJzUmVzcG9uc2USEwoLdG90YWxfY291",
+            "bnQYASABKA0SIQoGcmVzdWx0GAIgAygLMhEuYXBpLlVzZXJMaXN0SXRlbSI+",
+            "ChlVcGRhdGVVc2VyUGFzc3dvcmRSZXF1ZXN0Eg8KB3VzZXJfaWQYASABKAkS",
+            "EAoIcGFzc3dvcmQYAiABKAkylwQKC1VzZXJTZXJ2aWNlElAKBkNyZWF0ZRIW",
+            "LmFwaS5DcmVhdGVVc2VyUmVxdWVzdBoXLmFwaS5DcmVhdGVVc2VyUmVzcG9u",
+            "c2UiFYLT5JMCDyIKL2FwaS91c2VyczoBKhJJCgNHZXQSEy5hcGkuR2V0VXNl",
+            "clJlcXVlc3QaFC5hcGkuR2V0VXNlclJlc3BvbnNlIheC0+STAhESDy9hcGkv",
+            "dXNlcnMve2lkfRJZCgZVcGRhdGUSFi5hcGkuVXBkYXRlVXNlclJlcXVlc3Qa",
+            "Fi5nb29nbGUucHJvdG9idWYuRW1wdHkiH4LT5JMCGRoUL2FwaS91c2Vycy97",
+            "dXNlci5pZH06ASoSUQoGRGVsZXRlEhYuYXBpLkRlbGV0ZVVzZXJSZXF1ZXN0",
+            "GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IheC0+STAhEqDy9hcGkvdXNlcnMv",
+            "e2lkfRJJCgRMaXN0EhUuYXBpLkxpc3RVc2Vyc1JlcXVlc3QaFi5hcGkuTGlz",
+            "dFVzZXJzUmVzcG9uc2UiEoLT5JMCDBIKL2FwaS91c2VycxJyCg5VcGRhdGVQ",
+            "YXNzd29yZBIeLmFwaS5VcGRhdGVVc2VyUGFzc3dvcmRSZXF1ZXN0GhYuZ29v",
+            "Z2xlLnByb3RvYnVmLkVtcHR5IiiC0+STAiIiHS9hcGkvdXNlcnMve3VzZXJf",
+            "aWR9L3Bhc3N3b3JkOgEqQmEKEWlvLmNoaXJwc3RhY2suYXBpQglVc2VyUHJv",
+            "dG9QAVouZ2l0aHViLmNvbS9jaGlycHN0YWNrL2NoaXJwc3RhY2svYXBpL2dv",
+            "L3Y0L2FwaaoCDkNoaXJwc3RhY2suQXBpYgZwcm90bzM="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Api.AnnotationsReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.User), global::Chirpstack.Api.User.Parser, new[]{ "Id", "IsAdmin", "IsActive", "Email", "Note" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UserListItem), global::Chirpstack.Api.UserListItem.Parser, new[]{ "Id", "CreatedAt", "UpdatedAt", "Email", "IsAdmin", "IsActive" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UserTenant), global::Chirpstack.Api.UserTenant.Parser, new[]{ "TenantId", "IsAdmin", "IsDeviceAdmin", "IsGatewayAdmin" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateUserRequest), global::Chirpstack.Api.CreateUserRequest.Parser, new[]{ "User", "Password", "Tenants" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.CreateUserResponse), global::Chirpstack.Api.CreateUserResponse.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetUserRequest), global::Chirpstack.Api.GetUserRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.GetUserResponse), global::Chirpstack.Api.GetUserResponse.Parser, new[]{ "User", "CreatedAt", "UpdatedAt" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateUserRequest), global::Chirpstack.Api.UpdateUserRequest.Parser, new[]{ "User" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.DeleteUserRequest), global::Chirpstack.Api.DeleteUserRequest.Parser, new[]{ "Id" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListUsersRequest), global::Chirpstack.Api.ListUsersRequest.Parser, new[]{ "Limit", "Offset" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.ListUsersResponse), global::Chirpstack.Api.ListUsersResponse.Parser, new[]{ "TotalCount", "Result" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Api.UpdateUserPasswordRequest), global::Chirpstack.Api.UpdateUserPasswordRequest.Parser, new[]{ "UserId", "Password" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  public sealed partial class User : pb::IMessage<User>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<User> _parser = new pb::MessageParser<User>(() => new User());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<User> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public User() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public User(User other) : this() {
+      id_ = other.id_;
+      isAdmin_ = other.isAdmin_;
+      isActive_ = other.isActive_;
+      email_ = other.email_;
+      note_ = other.note_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public User Clone() {
+      return new User(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// User ID (UUID).
+    /// Will be set automatically on create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "is_admin" field.</summary>
+    public const int IsAdminFieldNumber = 4;
+    private bool isAdmin_;
+    /// <summary>
+    /// Set to true to make the user a global administrator.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsAdmin {
+      get { return isAdmin_; }
+      set {
+        isAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_active" field.</summary>
+    public const int IsActiveFieldNumber = 5;
+    private bool isActive_;
+    /// <summary>
+    /// Set to false to disable the user.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsActive {
+      get { return isActive_; }
+      set {
+        isActive_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "email" field.</summary>
+    public const int EmailFieldNumber = 6;
+    private string email_ = "";
+    /// <summary>
+    /// E-mail of the user.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Email {
+      get { return email_; }
+      set {
+        email_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "note" field.</summary>
+    public const int NoteFieldNumber = 7;
+    private string note_ = "";
+    /// <summary>
+    /// Optional note to store with the user.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Note {
+      get { return note_; }
+      set {
+        note_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as User);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(User other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (IsAdmin != other.IsAdmin) return false;
+      if (IsActive != other.IsActive) return false;
+      if (Email != other.Email) return false;
+      if (Note != other.Note) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (IsAdmin != false) hash ^= IsAdmin.GetHashCode();
+      if (IsActive != false) hash ^= IsActive.GetHashCode();
+      if (Email.Length != 0) hash ^= Email.GetHashCode();
+      if (Note.Length != 0) hash ^= Note.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsActive != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(IsActive);
+      }
+      if (Email.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(Email);
+      }
+      if (Note.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(Note);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsActive != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(IsActive);
+      }
+      if (Email.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(Email);
+      }
+      if (Note.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(Note);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (IsAdmin != false) {
+        size += 1 + 1;
+      }
+      if (IsActive != false) {
+        size += 1 + 1;
+      }
+      if (Email.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);
+      }
+      if (Note.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Note);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(User other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.IsAdmin != false) {
+        IsAdmin = other.IsAdmin;
+      }
+      if (other.IsActive != false) {
+        IsActive = other.IsActive;
+      }
+      if (other.Email.Length != 0) {
+        Email = other.Email;
+      }
+      if (other.Note.Length != 0) {
+        Note = other.Note;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 32: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 40: {
+            IsActive = input.ReadBool();
+            break;
+          }
+          case 50: {
+            Email = input.ReadString();
+            break;
+          }
+          case 58: {
+            Note = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 32: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 40: {
+            IsActive = input.ReadBool();
+            break;
+          }
+          case 50: {
+            Email = input.ReadString();
+            break;
+          }
+          case 58: {
+            Note = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UserListItem : pb::IMessage<UserListItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UserListItem> _parser = new pb::MessageParser<UserListItem>(() => new UserListItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UserListItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UserListItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UserListItem(UserListItem other) : this() {
+      id_ = other.id_;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      email_ = other.email_;
+      isAdmin_ = other.isAdmin_;
+      isActive_ = other.isActive_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UserListItem Clone() {
+      return new UserListItem(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// User ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "email" field.</summary>
+    public const int EmailFieldNumber = 4;
+    private string email_ = "";
+    /// <summary>
+    /// Email of the user.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Email {
+      get { return email_; }
+      set {
+        email_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "is_admin" field.</summary>
+    public const int IsAdminFieldNumber = 5;
+    private bool isAdmin_;
+    /// <summary>
+    /// Set to true to make the user a global administrator.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsAdmin {
+      get { return isAdmin_; }
+      set {
+        isAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_active" field.</summary>
+    public const int IsActiveFieldNumber = 6;
+    private bool isActive_;
+    /// <summary>
+    /// Set to false to disable the user.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsActive {
+      get { return isActive_; }
+      set {
+        isActive_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UserListItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UserListItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      if (Email != other.Email) return false;
+      if (IsAdmin != other.IsAdmin) return false;
+      if (IsActive != other.IsActive) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (Email.Length != 0) hash ^= Email.GetHashCode();
+      if (IsAdmin != false) hash ^= IsAdmin.GetHashCode();
+      if (IsActive != false) hash ^= IsActive.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Email.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Email);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsActive != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(IsActive);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (Email.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(Email);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsActive != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(IsActive);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (Email.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);
+      }
+      if (IsAdmin != false) {
+        size += 1 + 1;
+      }
+      if (IsActive != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UserListItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      if (other.Email.Length != 0) {
+        Email = other.Email;
+      }
+      if (other.IsAdmin != false) {
+        IsAdmin = other.IsAdmin;
+      }
+      if (other.IsActive != false) {
+        IsActive = other.IsActive;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Email = input.ReadString();
+            break;
+          }
+          case 40: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 48: {
+            IsActive = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+          case 34: {
+            Email = input.ReadString();
+            break;
+          }
+          case 40: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 48: {
+            IsActive = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UserTenant : pb::IMessage<UserTenant>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UserTenant> _parser = new pb::MessageParser<UserTenant>(() => new UserTenant());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UserTenant> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UserTenant() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UserTenant(UserTenant other) : this() {
+      tenantId_ = other.tenantId_;
+      isAdmin_ = other.isAdmin_;
+      isDeviceAdmin_ = other.isDeviceAdmin_;
+      isGatewayAdmin_ = other.isGatewayAdmin_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UserTenant Clone() {
+      return new UserTenant(this);
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 1;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "is_admin" field.</summary>
+    public const int IsAdminFieldNumber = 2;
+    private bool isAdmin_;
+    /// <summary>
+    /// User is admin within the context of the tenant.
+    /// There is no need to set the is_device_admin and is_gateway_admin flags.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsAdmin {
+      get { return isAdmin_; }
+      set {
+        isAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_device_admin" field.</summary>
+    public const int IsDeviceAdminFieldNumber = 3;
+    private bool isDeviceAdmin_;
+    /// <summary>
+    /// User is able to modify device related resources (applications,
+    /// device-profiles, devices, multicast-groups).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsDeviceAdmin {
+      get { return isDeviceAdmin_; }
+      set {
+        isDeviceAdmin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "is_gateway_admin" field.</summary>
+    public const int IsGatewayAdminFieldNumber = 4;
+    private bool isGatewayAdmin_;
+    /// <summary>
+    /// User is able to modify gateways.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool IsGatewayAdmin {
+      get { return isGatewayAdmin_; }
+      set {
+        isGatewayAdmin_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UserTenant);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UserTenant other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TenantId != other.TenantId) return false;
+      if (IsAdmin != other.IsAdmin) return false;
+      if (IsDeviceAdmin != other.IsDeviceAdmin) return false;
+      if (IsGatewayAdmin != other.IsGatewayAdmin) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (IsAdmin != false) hash ^= IsAdmin.GetHashCode();
+      if (IsDeviceAdmin != false) hash ^= IsDeviceAdmin.GetHashCode();
+      if (IsGatewayAdmin != false) hash ^= IsGatewayAdmin.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(16);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsDeviceAdmin != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(IsDeviceAdmin);
+      }
+      if (IsGatewayAdmin != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(IsGatewayAdmin);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (IsAdmin != false) {
+        output.WriteRawTag(16);
+        output.WriteBool(IsAdmin);
+      }
+      if (IsDeviceAdmin != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(IsDeviceAdmin);
+      }
+      if (IsGatewayAdmin != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(IsGatewayAdmin);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (IsAdmin != false) {
+        size += 1 + 1;
+      }
+      if (IsDeviceAdmin != false) {
+        size += 1 + 1;
+      }
+      if (IsGatewayAdmin != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UserTenant other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.IsAdmin != false) {
+        IsAdmin = other.IsAdmin;
+      }
+      if (other.IsDeviceAdmin != false) {
+        IsDeviceAdmin = other.IsDeviceAdmin;
+      }
+      if (other.IsGatewayAdmin != false) {
+        IsGatewayAdmin = other.IsGatewayAdmin;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 16: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 24: {
+            IsDeviceAdmin = input.ReadBool();
+            break;
+          }
+          case 32: {
+            IsGatewayAdmin = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 16: {
+            IsAdmin = input.ReadBool();
+            break;
+          }
+          case 24: {
+            IsDeviceAdmin = input.ReadBool();
+            break;
+          }
+          case 32: {
+            IsGatewayAdmin = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateUserRequest : pb::IMessage<CreateUserRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateUserRequest> _parser = new pb::MessageParser<CreateUserRequest>(() => new CreateUserRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateUserRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateUserRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateUserRequest(CreateUserRequest other) : this() {
+      user_ = other.user_ != null ? other.user_.Clone() : null;
+      password_ = other.password_;
+      tenants_ = other.tenants_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateUserRequest Clone() {
+      return new CreateUserRequest(this);
+    }
+
+    /// <summary>Field number for the "user" field.</summary>
+    public const int UserFieldNumber = 1;
+    private global::Chirpstack.Api.User user_;
+    /// <summary>
+    /// User object to create.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.User User {
+      get { return user_; }
+      set {
+        user_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "password" field.</summary>
+    public const int PasswordFieldNumber = 2;
+    private string password_ = "";
+    /// <summary>
+    /// Password to set for the user.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Password {
+      get { return password_; }
+      set {
+        password_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tenants" field.</summary>
+    public const int TenantsFieldNumber = 3;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.UserTenant> _repeated_tenants_codec
+        = pb::FieldCodec.ForMessage(26, global::Chirpstack.Api.UserTenant.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.UserTenant> tenants_ = new pbc::RepeatedField<global::Chirpstack.Api.UserTenant>();
+    /// <summary>
+    /// Add the user to the following tenants.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.UserTenant> Tenants {
+      get { return tenants_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateUserRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateUserRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(User, other.User)) return false;
+      if (Password != other.Password) return false;
+      if(!tenants_.Equals(other.tenants_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (user_ != null) hash ^= User.GetHashCode();
+      if (Password.Length != 0) hash ^= Password.GetHashCode();
+      hash ^= tenants_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (user_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(User);
+      }
+      if (Password.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Password);
+      }
+      tenants_.WriteTo(output, _repeated_tenants_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (user_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(User);
+      }
+      if (Password.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Password);
+      }
+      tenants_.WriteTo(ref output, _repeated_tenants_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (user_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(User);
+      }
+      if (Password.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Password);
+      }
+      size += tenants_.CalculateSize(_repeated_tenants_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateUserRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.user_ != null) {
+        if (user_ == null) {
+          User = new global::Chirpstack.Api.User();
+        }
+        User.MergeFrom(other.User);
+      }
+      if (other.Password.Length != 0) {
+        Password = other.Password;
+      }
+      tenants_.Add(other.tenants_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (user_ == null) {
+              User = new global::Chirpstack.Api.User();
+            }
+            input.ReadMessage(User);
+            break;
+          }
+          case 18: {
+            Password = input.ReadString();
+            break;
+          }
+          case 26: {
+            tenants_.AddEntriesFrom(input, _repeated_tenants_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (user_ == null) {
+              User = new global::Chirpstack.Api.User();
+            }
+            input.ReadMessage(User);
+            break;
+          }
+          case 18: {
+            Password = input.ReadString();
+            break;
+          }
+          case 26: {
+            tenants_.AddEntriesFrom(ref input, _repeated_tenants_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class CreateUserResponse : pb::IMessage<CreateUserResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CreateUserResponse> _parser = new pb::MessageParser<CreateUserResponse>(() => new CreateUserResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CreateUserResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateUserResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateUserResponse(CreateUserResponse other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CreateUserResponse Clone() {
+      return new CreateUserResponse(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// User ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CreateUserResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CreateUserResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CreateUserResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetUserRequest : pb::IMessage<GetUserRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetUserRequest> _parser = new pb::MessageParser<GetUserRequest>(() => new GetUserRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetUserRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetUserRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetUserRequest(GetUserRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetUserRequest Clone() {
+      return new GetUserRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// User ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetUserRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetUserRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetUserRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GetUserResponse : pb::IMessage<GetUserResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GetUserResponse> _parser = new pb::MessageParser<GetUserResponse>(() => new GetUserResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GetUserResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetUserResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetUserResponse(GetUserResponse other) : this() {
+      user_ = other.user_ != null ? other.user_.Clone() : null;
+      createdAt_ = other.createdAt_ != null ? other.createdAt_.Clone() : null;
+      updatedAt_ = other.updatedAt_ != null ? other.updatedAt_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GetUserResponse Clone() {
+      return new GetUserResponse(this);
+    }
+
+    /// <summary>Field number for the "user" field.</summary>
+    public const int UserFieldNumber = 1;
+    private global::Chirpstack.Api.User user_;
+    /// <summary>
+    /// User object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.User User {
+      get { return user_; }
+      set {
+        user_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "created_at" field.</summary>
+    public const int CreatedAtFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp createdAt_;
+    /// <summary>
+    /// Created at timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp CreatedAt {
+      get { return createdAt_; }
+      set {
+        createdAt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "updated_at" field.</summary>
+    public const int UpdatedAtFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp updatedAt_;
+    /// <summary>
+    /// Last update timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp UpdatedAt {
+      get { return updatedAt_; }
+      set {
+        updatedAt_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GetUserResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GetUserResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(User, other.User)) return false;
+      if (!object.Equals(CreatedAt, other.CreatedAt)) return false;
+      if (!object.Equals(UpdatedAt, other.UpdatedAt)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (user_ != null) hash ^= User.GetHashCode();
+      if (createdAt_ != null) hash ^= CreatedAt.GetHashCode();
+      if (updatedAt_ != null) hash ^= UpdatedAt.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (user_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(User);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (user_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(User);
+      }
+      if (createdAt_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (user_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(User);
+      }
+      if (createdAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(CreatedAt);
+      }
+      if (updatedAt_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(UpdatedAt);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GetUserResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.user_ != null) {
+        if (user_ == null) {
+          User = new global::Chirpstack.Api.User();
+        }
+        User.MergeFrom(other.User);
+      }
+      if (other.createdAt_ != null) {
+        if (createdAt_ == null) {
+          CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        CreatedAt.MergeFrom(other.CreatedAt);
+      }
+      if (other.updatedAt_ != null) {
+        if (updatedAt_ == null) {
+          UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        UpdatedAt.MergeFrom(other.UpdatedAt);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (user_ == null) {
+              User = new global::Chirpstack.Api.User();
+            }
+            input.ReadMessage(User);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (user_ == null) {
+              User = new global::Chirpstack.Api.User();
+            }
+            input.ReadMessage(User);
+            break;
+          }
+          case 18: {
+            if (createdAt_ == null) {
+              CreatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(CreatedAt);
+            break;
+          }
+          case 26: {
+            if (updatedAt_ == null) {
+              UpdatedAt = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(UpdatedAt);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateUserRequest : pb::IMessage<UpdateUserRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateUserRequest> _parser = new pb::MessageParser<UpdateUserRequest>(() => new UpdateUserRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateUserRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateUserRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateUserRequest(UpdateUserRequest other) : this() {
+      user_ = other.user_ != null ? other.user_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateUserRequest Clone() {
+      return new UpdateUserRequest(this);
+    }
+
+    /// <summary>Field number for the "user" field.</summary>
+    public const int UserFieldNumber = 1;
+    private global::Chirpstack.Api.User user_;
+    /// <summary>
+    /// User object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Api.User User {
+      get { return user_; }
+      set {
+        user_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateUserRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateUserRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(User, other.User)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (user_ != null) hash ^= User.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (user_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(User);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (user_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(User);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (user_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(User);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateUserRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.user_ != null) {
+        if (user_ == null) {
+          User = new global::Chirpstack.Api.User();
+        }
+        User.MergeFrom(other.User);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (user_ == null) {
+              User = new global::Chirpstack.Api.User();
+            }
+            input.ReadMessage(User);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (user_ == null) {
+              User = new global::Chirpstack.Api.User();
+            }
+            input.ReadMessage(User);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DeleteUserRequest : pb::IMessage<DeleteUserRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeleteUserRequest> _parser = new pb::MessageParser<DeleteUserRequest>(() => new DeleteUserRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeleteUserRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteUserRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteUserRequest(DeleteUserRequest other) : this() {
+      id_ = other.id_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeleteUserRequest Clone() {
+      return new DeleteUserRequest(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// User ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeleteUserRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeleteUserRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeleteUserRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListUsersRequest : pb::IMessage<ListUsersRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListUsersRequest> _parser = new pb::MessageParser<ListUsersRequest>(() => new ListUsersRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListUsersRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListUsersRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListUsersRequest(ListUsersRequest other) : this() {
+      limit_ = other.limit_;
+      offset_ = other.offset_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListUsersRequest Clone() {
+      return new ListUsersRequest(this);
+    }
+
+    /// <summary>Field number for the "limit" field.</summary>
+    public const int LimitFieldNumber = 1;
+    private uint limit_;
+    /// <summary>
+    /// Max number of tenants to return in the result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Limit {
+      get { return limit_; }
+      set {
+        limit_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "offset" field.</summary>
+    public const int OffsetFieldNumber = 2;
+    private uint offset_;
+    /// <summary>
+    /// Offset in the result-set (for pagination).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Offset {
+      get { return offset_; }
+      set {
+        offset_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListUsersRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListUsersRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Limit != other.Limit) return false;
+      if (Offset != other.Offset) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Limit != 0) hash ^= Limit.GetHashCode();
+      if (Offset != 0) hash ^= Offset.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Limit != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Limit);
+      }
+      if (Offset != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Offset);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Limit != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Limit);
+      }
+      if (Offset != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Offset);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListUsersRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Limit != 0) {
+        Limit = other.Limit;
+      }
+      if (other.Offset != 0) {
+        Offset = other.Offset;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Limit = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Offset = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ListUsersResponse : pb::IMessage<ListUsersResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ListUsersResponse> _parser = new pb::MessageParser<ListUsersResponse>(() => new ListUsersResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ListUsersResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[10]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListUsersResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListUsersResponse(ListUsersResponse other) : this() {
+      totalCount_ = other.totalCount_;
+      result_ = other.result_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ListUsersResponse Clone() {
+      return new ListUsersResponse(this);
+    }
+
+    /// <summary>Field number for the "total_count" field.</summary>
+    public const int TotalCountFieldNumber = 1;
+    private uint totalCount_;
+    /// <summary>
+    /// Total number of users.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TotalCount {
+      get { return totalCount_; }
+      set {
+        totalCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "result" field.</summary>
+    public const int ResultFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Chirpstack.Api.UserListItem> _repeated_result_codec
+        = pb::FieldCodec.ForMessage(18, global::Chirpstack.Api.UserListItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Api.UserListItem> result_ = new pbc::RepeatedField<global::Chirpstack.Api.UserListItem>();
+    /// <summary>
+    /// Result-set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Api.UserListItem> Result {
+      get { return result_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ListUsersResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ListUsersResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TotalCount != other.TotalCount) return false;
+      if(!result_.Equals(other.result_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TotalCount != 0) hash ^= TotalCount.GetHashCode();
+      hash ^= result_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TotalCount != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(TotalCount);
+      }
+      result_.WriteTo(ref output, _repeated_result_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TotalCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TotalCount);
+      }
+      size += result_.CalculateSize(_repeated_result_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ListUsersResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TotalCount != 0) {
+        TotalCount = other.TotalCount;
+      }
+      result_.Add(other.result_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            TotalCount = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            result_.AddEntriesFrom(ref input, _repeated_result_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UpdateUserPasswordRequest : pb::IMessage<UpdateUserPasswordRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UpdateUserPasswordRequest> _parser = new pb::MessageParser<UpdateUserPasswordRequest>(() => new UpdateUserPasswordRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UpdateUserPasswordRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.MessageTypes[11]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateUserPasswordRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateUserPasswordRequest(UpdateUserPasswordRequest other) : this() {
+      userId_ = other.userId_;
+      password_ = other.password_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UpdateUserPasswordRequest Clone() {
+      return new UpdateUserPasswordRequest(this);
+    }
+
+    /// <summary>Field number for the "user_id" field.</summary>
+    public const int UserIdFieldNumber = 1;
+    private string userId_ = "";
+    /// <summary>
+    /// User ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string UserId {
+      get { return userId_; }
+      set {
+        userId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "password" field.</summary>
+    public const int PasswordFieldNumber = 2;
+    private string password_ = "";
+    /// <summary>
+    /// Password to set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Password {
+      get { return password_; }
+      set {
+        password_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UpdateUserPasswordRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UpdateUserPasswordRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (UserId != other.UserId) return false;
+      if (Password != other.Password) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (UserId.Length != 0) hash ^= UserId.GetHashCode();
+      if (Password.Length != 0) hash ^= Password.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (UserId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(UserId);
+      }
+      if (Password.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Password);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (UserId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(UserId);
+      }
+      if (Password.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Password);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (UserId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(UserId);
+      }
+      if (Password.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Password);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UpdateUserPasswordRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.UserId.Length != 0) {
+        UserId = other.UserId;
+      }
+      if (other.Password.Length != 0) {
+        Password = other.Password;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            UserId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Password = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            UserId = input.ReadString();
+            break;
+          }
+          case 18: {
+            Password = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/api/UserGrpc.cs b/api/csharp/Chirpstack/api/UserGrpc.cs
new file mode 100644
index 00000000..7a3617ee
--- /dev/null
+++ b/api/csharp/Chirpstack/api/UserGrpc.cs
@@ -0,0 +1,558 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: api/user.proto
+// </auto-generated>
+#pragma warning disable 0414, 1591, 8981
+#region Designer generated code
+
+using grpc = global::Grpc.Core;
+
+namespace Chirpstack.Api {
+  /// <summary>
+  /// UserService is the service providing API methods for managing users.
+  /// </summary>
+  public static partial class UserService
+  {
+    static readonly string __ServiceName = "api.UserService";
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context)
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (message is global::Google.Protobuf.IBufferMessage)
+      {
+        context.SetPayloadLength(message.CalculateSize());
+        global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
+        context.Complete();
+        return;
+      }
+      #endif
+      context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static class __Helper_MessageCache<T>
+    {
+      public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static T __Helper_DeserializeMessage<T>(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser<T> parser) where T : global::Google.Protobuf.IMessage<T>
+    {
+      #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION
+      if (__Helper_MessageCache<T>.IsBufferMessage)
+      {
+        return parser.ParseFrom(context.PayloadAsReadOnlySequence());
+      }
+      #endif
+      return parser.ParseFrom(context.PayloadAsNewBuffer());
+    }
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateUserRequest> __Marshaller_api_CreateUserRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateUserRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.CreateUserResponse> __Marshaller_api_CreateUserResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.CreateUserResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetUserRequest> __Marshaller_api_GetUserRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetUserRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.GetUserResponse> __Marshaller_api_GetUserResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.GetUserResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateUserRequest> __Marshaller_api_UpdateUserRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateUserRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Google.Protobuf.WellKnownTypes.Empty> __Marshaller_google_protobuf_Empty = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Google.Protobuf.WellKnownTypes.Empty.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.DeleteUserRequest> __Marshaller_api_DeleteUserRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.DeleteUserRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListUsersRequest> __Marshaller_api_ListUsersRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListUsersRequest.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.ListUsersResponse> __Marshaller_api_ListUsersResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.ListUsersResponse.Parser));
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Marshaller<global::Chirpstack.Api.UpdateUserPasswordRequest> __Marshaller_api_UpdateUserPasswordRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Chirpstack.Api.UpdateUserPasswordRequest.Parser));
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.CreateUserRequest, global::Chirpstack.Api.CreateUserResponse> __Method_Create = new grpc::Method<global::Chirpstack.Api.CreateUserRequest, global::Chirpstack.Api.CreateUserResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Create",
+        __Marshaller_api_CreateUserRequest,
+        __Marshaller_api_CreateUserResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.GetUserRequest, global::Chirpstack.Api.GetUserResponse> __Method_Get = new grpc::Method<global::Chirpstack.Api.GetUserRequest, global::Chirpstack.Api.GetUserResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Get",
+        __Marshaller_api_GetUserRequest,
+        __Marshaller_api_GetUserResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateUserRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Update = new grpc::Method<global::Chirpstack.Api.UpdateUserRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Update",
+        __Marshaller_api_UpdateUserRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.DeleteUserRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_Delete = new grpc::Method<global::Chirpstack.Api.DeleteUserRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "Delete",
+        __Marshaller_api_DeleteUserRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.ListUsersRequest, global::Chirpstack.Api.ListUsersResponse> __Method_List = new grpc::Method<global::Chirpstack.Api.ListUsersRequest, global::Chirpstack.Api.ListUsersResponse>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "List",
+        __Marshaller_api_ListUsersRequest,
+        __Marshaller_api_ListUsersResponse);
+
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    static readonly grpc::Method<global::Chirpstack.Api.UpdateUserPasswordRequest, global::Google.Protobuf.WellKnownTypes.Empty> __Method_UpdatePassword = new grpc::Method<global::Chirpstack.Api.UpdateUserPasswordRequest, global::Google.Protobuf.WellKnownTypes.Empty>(
+        grpc::MethodType.Unary,
+        __ServiceName,
+        "UpdatePassword",
+        __Marshaller_api_UpdateUserPasswordRequest,
+        __Marshaller_google_protobuf_Empty);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Chirpstack.Api.UserReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of UserService</summary>
+    [grpc::BindServiceMethod(typeof(UserService), "BindService")]
+    public abstract partial class UserServiceBase
+    {
+      /// <summary>
+      /// Create a new user.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.CreateUserResponse> Create(global::Chirpstack.Api.CreateUserRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the user for the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.GetUserResponse> Get(global::Chirpstack.Api.GetUserRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update the given user.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Update(global::Chirpstack.Api.UpdateUserRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Delete the user with the given ID.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> Delete(global::Chirpstack.Api.DeleteUserRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Get the list of users.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Chirpstack.Api.ListUsersResponse> List(global::Chirpstack.Api.ListUsersRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+      /// <summary>
+      /// Update the password for the given user.
+      /// </summary>
+      /// <param name="request">The request received from the client.</param>
+      /// <param name="context">The context of the server-side call handler being invoked.</param>
+      /// <returns>The response to send back to the client (wrapped by a task).</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::System.Threading.Tasks.Task<global::Google.Protobuf.WellKnownTypes.Empty> UpdatePassword(global::Chirpstack.Api.UpdateUserPasswordRequest request, grpc::ServerCallContext context)
+      {
+        throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for UserService</summary>
+    public partial class UserServiceClient : grpc::ClientBase<UserServiceClient>
+    {
+      /// <summary>Creates a new client for UserService</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public UserServiceClient(grpc::ChannelBase channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for UserService that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public UserServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected UserServiceClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected UserServiceClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      /// Create a new user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateUserResponse Create(global::Chirpstack.Api.CreateUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Create(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create a new user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.CreateUserResponse Create(global::Chirpstack.Api.CreateUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Create a new user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateUserResponse> CreateAsync(global::Chirpstack.Api.CreateUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return CreateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Create a new user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.CreateUserResponse> CreateAsync(global::Chirpstack.Api.CreateUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Create, null, options, request);
+      }
+      /// <summary>
+      /// Get the user for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetUserResponse Get(global::Chirpstack.Api.GetUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Get(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the user for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.GetUserResponse Get(global::Chirpstack.Api.GetUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Get the user for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetUserResponse> GetAsync(global::Chirpstack.Api.GetUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return GetAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the user for the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.GetUserResponse> GetAsync(global::Chirpstack.Api.GetUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Get, null, options, request);
+      }
+      /// <summary>
+      /// Update the given user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Update(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Update(global::Chirpstack.Api.UpdateUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Update the given user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdateAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the given user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdateAsync(global::Chirpstack.Api.UpdateUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Update, null, options, request);
+      }
+      /// <summary>
+      /// Delete the user with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return Delete(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the user with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty Delete(global::Chirpstack.Api.DeleteUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Delete the user with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteUserRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return DeleteAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Delete the user with the given ID.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> DeleteAsync(global::Chirpstack.Api.DeleteUserRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_Delete, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of users.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListUsersResponse List(global::Chirpstack.Api.ListUsersRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return List(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of users.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Chirpstack.Api.ListUsersResponse List(global::Chirpstack.Api.ListUsersRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// Get the list of users.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListUsersResponse> ListAsync(global::Chirpstack.Api.ListUsersRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return ListAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Get the list of users.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Chirpstack.Api.ListUsersResponse> ListAsync(global::Chirpstack.Api.ListUsersRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_List, null, options, request);
+      }
+      /// <summary>
+      /// Update the password for the given user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdatePassword(global::Chirpstack.Api.UpdateUserPasswordRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdatePassword(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the password for the given user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The response received from the server.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual global::Google.Protobuf.WellKnownTypes.Empty UpdatePassword(global::Chirpstack.Api.UpdateUserPasswordRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.BlockingUnaryCall(__Method_UpdatePassword, null, options, request);
+      }
+      /// <summary>
+      /// Update the password for the given user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
+      /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
+      /// <param name="cancellationToken">An optional token for canceling the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdatePasswordAsync(global::Chirpstack.Api.UpdateUserPasswordRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+      {
+        return UpdatePasswordAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      /// Update the password for the given user.
+      /// </summary>
+      /// <param name="request">The request to send to the server.</param>
+      /// <param name="options">The options for the call.</param>
+      /// <returns>The call object.</returns>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      public virtual grpc::AsyncUnaryCall<global::Google.Protobuf.WellKnownTypes.Empty> UpdatePasswordAsync(global::Chirpstack.Api.UpdateUserPasswordRequest request, grpc::CallOptions options)
+      {
+        return CallInvoker.AsyncUnaryCall(__Method_UpdatePassword, null, options, request);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+      protected override UserServiceClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new UserServiceClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static grpc::ServerServiceDefinition BindService(UserServiceBase serviceImpl)
+    {
+      return grpc::ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_Create, serviceImpl.Create)
+          .AddMethod(__Method_Get, serviceImpl.Get)
+          .AddMethod(__Method_Update, serviceImpl.Update)
+          .AddMethod(__Method_Delete, serviceImpl.Delete)
+          .AddMethod(__Method_List, serviceImpl.List)
+          .AddMethod(__Method_UpdatePassword, serviceImpl.UpdatePassword).Build();
+    }
+
+    /// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
+    /// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+    /// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+    /// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+    [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
+    public static void BindService(grpc::ServiceBinderBase serviceBinder, UserServiceBase serviceImpl)
+    {
+      serviceBinder.AddMethod(__Method_Create, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.CreateUserRequest, global::Chirpstack.Api.CreateUserResponse>(serviceImpl.Create));
+      serviceBinder.AddMethod(__Method_Get, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.GetUserRequest, global::Chirpstack.Api.GetUserResponse>(serviceImpl.Get));
+      serviceBinder.AddMethod(__Method_Update, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateUserRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Update));
+      serviceBinder.AddMethod(__Method_Delete, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.DeleteUserRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.Delete));
+      serviceBinder.AddMethod(__Method_List, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.ListUsersRequest, global::Chirpstack.Api.ListUsersResponse>(serviceImpl.List));
+      serviceBinder.AddMethod(__Method_UpdatePassword, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Chirpstack.Api.UpdateUserPasswordRequest, global::Google.Protobuf.WellKnownTypes.Empty>(serviceImpl.UpdatePassword));
+    }
+
+  }
+}
+#endregion
diff --git a/api/csharp/Chirpstack/common/Common.cs b/api/csharp/Chirpstack/common/Common.cs
new file mode 100644
index 00000000..255ea061
--- /dev/null
+++ b/api/csharp/Chirpstack/common/Common.cs
@@ -0,0 +1,1365 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: common/common.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Common {
+
+  /// <summary>Holder for reflection information generated from common/common.proto</summary>
+  public static partial class CommonReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for common/common.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static CommonReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChNjb21tb24vY29tbW9uLnByb3RvEgZjb21tb24aH2dvb2dsZS9wcm90b2J1",
+            "Zi90aW1lc3RhbXAucHJvdG8iewoITG9jYXRpb24SEAoIbGF0aXR1ZGUYASAB",
+            "KAESEQoJbG9uZ2l0dWRlGAIgASgBEhAKCGFsdGl0dWRlGAMgASgBEiYKBnNv",
+            "dXJjZRgEIAEoDjIWLmNvbW1vbi5Mb2NhdGlvblNvdXJjZRIQCghhY2N1cmFj",
+            "eRgFIAEoAiIxCgtLZXlFbnZlbG9wZRIRCglrZWtfbGFiZWwYASABKAkSDwoH",
+            "YWVzX2tleRgCIAEoDCKRAQoGTWV0cmljEgwKBG5hbWUYASABKAkSLgoKdGlt",
+            "ZXN0YW1wcxgCIAMoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASJwoI",
+            "ZGF0YXNldHMYAyADKAsyFS5jb21tb24uTWV0cmljRGF0YXNldBIgCgRraW5k",
+            "GAQgASgOMhIuY29tbW9uLk1ldHJpY0tpbmQiLAoNTWV0cmljRGF0YXNldBIN",
+            "CgVsYWJlbBgBIAEoCRIMCgRkYXRhGAIgAygCKiwKCk1vZHVsYXRpb24SCAoE",
+            "TE9SQRAAEgcKA0ZTSxABEgsKB0xSX0ZIU1MQAiqqAQoGUmVnaW9uEgkKBUVV",
+            "ODY4EAASCQoFVVM5MTUQAhIJCgVDTjc3ORADEgkKBUVVNDMzEAQSCQoFQVU5",
+            "MTUQBRIJCgVDTjQ3MBAGEgkKBUFTOTIzEAcSCwoHQVM5MjNfMhAMEgsKB0FT",
+            "OTIzXzMQDRILCgdBUzkyM180EA4SCQoFS1I5MjAQCBIJCgVJTjg2NRAJEgkK",
+            "BVJVODY0EAoSCwoHSVNNMjQwMBALKrMBCgVNVHlwZRIQCgxKT0lOX1JFUVVF",
+            "U1QQABIPCgtKT0lOX0FDQ0VQVBABEhcKE1VOQ09ORklSTUVEX0RBVEFfVVAQ",
+            "AhIZChVVTkNPTkZJUk1FRF9EQVRBX0RPV04QAxIVChFDT05GSVJNRURfREFU",
+            "QV9VUBAEEhcKE0NPTkZJUk1FRF9EQVRBX0RPV04QBRISCg5SRUpPSU5fUkVR",
+            "VUVTVBAGEg8KC1BST1BSSUVUQVJZEAcqfgoKTWFjVmVyc2lvbhIRCg1MT1JB",
+            "V0FOXzFfMF8wEAASEQoNTE9SQVdBTl8xXzBfMRABEhEKDUxPUkFXQU5fMV8w",
+            "XzIQAhIRCg1MT1JBV0FOXzFfMF8zEAMSEQoNTE9SQVdBTl8xXzBfNBAEEhEK",
+            "DUxPUkFXQU5fMV8xXzAQBSplChFSZWdQYXJhbXNSZXZpc2lvbhIFCgFBEAAS",
+            "BQoBQhABEg8KC1JQMDAyXzFfMF8wEAISDwoLUlAwMDJfMV8wXzEQAxIPCgtS",
+            "UDAwMl8xXzBfMhAEEg8KC1JQMDAyXzFfMF8zEAUqjgEKDkxvY2F0aW9uU291",
+            "cmNlEgsKB1VOS05PV04QABIHCgNHUFMQARIKCgZDT05GSUcQAhIVChFHRU9f",
+            "UkVTT0xWRVJfVERPQRADEhUKEUdFT19SRVNPTFZFUl9SU1NJEAQSFQoRR0VP",
+            "X1JFU09MVkVSX0dOU1MQBRIVChFHRU9fUkVTT0xWRVJfV0lGSRAGKisKC0Fn",
+            "Z3JlZ2F0aW9uEggKBEhPVVIQABIHCgNEQVkQARIJCgVNT05USBACKjIKCk1l",
+            "dHJpY0tpbmQSCwoHQ09VTlRFUhAAEgwKCEFCU09MVVRFEAESCQoFR0FVR0UQ",
+            "AkJpChFpby5jaGlycHN0YWNrLmFwaUILQ29tbW9uUHJvdG9QAVoxZ2l0aHVi",
+            "LmNvbS9jaGlycHN0YWNrL2NoaXJwc3RhY2svYXBpL2dvL3Y0L2NvbW1vbqoC",
+            "EUNoaXJwc3RhY2suQ29tbW9uYgZwcm90bzM="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Chirpstack.Common.Modulation), typeof(global::Chirpstack.Common.Region), typeof(global::Chirpstack.Common.MType), typeof(global::Chirpstack.Common.MacVersion), typeof(global::Chirpstack.Common.RegParamsRevision), typeof(global::Chirpstack.Common.LocationSource), typeof(global::Chirpstack.Common.Aggregation), typeof(global::Chirpstack.Common.MetricKind), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Common.Location), global::Chirpstack.Common.Location.Parser, new[]{ "Latitude", "Longitude", "Altitude", "Source", "Accuracy" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Common.KeyEnvelope), global::Chirpstack.Common.KeyEnvelope.Parser, new[]{ "KekLabel", "AesKey" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Common.Metric), global::Chirpstack.Common.Metric.Parser, new[]{ "Name", "Timestamps", "Datasets", "Kind" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Common.MetricDataset), global::Chirpstack.Common.MetricDataset.Parser, new[]{ "Label", "Data" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Enums
+  public enum Modulation {
+    /// <summary>
+    /// LoRa
+    /// </summary>
+    [pbr::OriginalName("LORA")] Lora = 0,
+    /// <summary>
+    /// FSK
+    /// </summary>
+    [pbr::OriginalName("FSK")] Fsk = 1,
+    /// <summary>
+    /// LR-FHSS
+    /// </summary>
+    [pbr::OriginalName("LR_FHSS")] LrFhss = 2,
+  }
+
+  public enum Region {
+    /// <summary>
+    /// EU868
+    /// </summary>
+    [pbr::OriginalName("EU868")] Eu868 = 0,
+    /// <summary>
+    /// US915
+    /// </summary>
+    [pbr::OriginalName("US915")] Us915 = 2,
+    /// <summary>
+    /// CN779
+    /// </summary>
+    [pbr::OriginalName("CN779")] Cn779 = 3,
+    /// <summary>
+    /// EU433
+    /// </summary>
+    [pbr::OriginalName("EU433")] Eu433 = 4,
+    /// <summary>
+    /// AU915
+    /// </summary>
+    [pbr::OriginalName("AU915")] Au915 = 5,
+    /// <summary>
+    /// CN470
+    /// </summary>
+    [pbr::OriginalName("CN470")] Cn470 = 6,
+    /// <summary>
+    /// AS923
+    /// </summary>
+    [pbr::OriginalName("AS923")] As923 = 7,
+    /// <summary>
+    /// AS923 with -1.80 MHz frequency offset
+    /// </summary>
+    [pbr::OriginalName("AS923_2")] As9232 = 12,
+    /// <summary>
+    /// AS923 with -6.60 MHz frequency offset
+    /// </summary>
+    [pbr::OriginalName("AS923_3")] As9233 = 13,
+    /// <summary>
+    /// (AS923 with -5.90 MHz frequency offset).
+    /// </summary>
+    [pbr::OriginalName("AS923_4")] As9234 = 14,
+    /// <summary>
+    /// KR920
+    /// </summary>
+    [pbr::OriginalName("KR920")] Kr920 = 8,
+    /// <summary>
+    /// IN865
+    /// </summary>
+    [pbr::OriginalName("IN865")] In865 = 9,
+    /// <summary>
+    /// RU864
+    /// </summary>
+    [pbr::OriginalName("RU864")] Ru864 = 10,
+    /// <summary>
+    /// ISM2400 (LoRaWAN 2.4 GHz)
+    /// </summary>
+    [pbr::OriginalName("ISM2400")] Ism2400 = 11,
+  }
+
+  public enum MType {
+    /// <summary>
+    /// JoinRequest.
+    /// </summary>
+    [pbr::OriginalName("JOIN_REQUEST")] JoinRequest = 0,
+    /// <summary>
+    /// JoinAccept.
+    /// </summary>
+    [pbr::OriginalName("JOIN_ACCEPT")] JoinAccept = 1,
+    /// <summary>
+    /// UnconfirmedDataUp.
+    /// </summary>
+    [pbr::OriginalName("UNCONFIRMED_DATA_UP")] UnconfirmedDataUp = 2,
+    /// <summary>
+    /// UnconfirmedDataDown.
+    /// </summary>
+    [pbr::OriginalName("UNCONFIRMED_DATA_DOWN")] UnconfirmedDataDown = 3,
+    /// <summary>
+    /// ConfirmedDataUp.
+    /// </summary>
+    [pbr::OriginalName("CONFIRMED_DATA_UP")] ConfirmedDataUp = 4,
+    /// <summary>
+    /// ConfirmedDataDown.
+    /// </summary>
+    [pbr::OriginalName("CONFIRMED_DATA_DOWN")] ConfirmedDataDown = 5,
+    /// <summary>
+    /// RejoinRequest.
+    /// </summary>
+    [pbr::OriginalName("REJOIN_REQUEST")] RejoinRequest = 6,
+    /// <summary>
+    /// Proprietary.
+    /// </summary>
+    [pbr::OriginalName("PROPRIETARY")] Proprietary = 7,
+  }
+
+  public enum MacVersion {
+    [pbr::OriginalName("LORAWAN_1_0_0")] Lorawan100 = 0,
+    [pbr::OriginalName("LORAWAN_1_0_1")] Lorawan101 = 1,
+    [pbr::OriginalName("LORAWAN_1_0_2")] Lorawan102 = 2,
+    [pbr::OriginalName("LORAWAN_1_0_3")] Lorawan103 = 3,
+    [pbr::OriginalName("LORAWAN_1_0_4")] Lorawan104 = 4,
+    [pbr::OriginalName("LORAWAN_1_1_0")] Lorawan110 = 5,
+  }
+
+  public enum RegParamsRevision {
+    [pbr::OriginalName("A")] A = 0,
+    [pbr::OriginalName("B")] B = 1,
+    [pbr::OriginalName("RP002_1_0_0")] Rp002100 = 2,
+    [pbr::OriginalName("RP002_1_0_1")] Rp002101 = 3,
+    [pbr::OriginalName("RP002_1_0_2")] Rp002102 = 4,
+    [pbr::OriginalName("RP002_1_0_3")] Rp002103 = 5,
+  }
+
+  public enum LocationSource {
+    /// <summary>
+    /// Unknown.
+    /// </summary>
+    [pbr::OriginalName("UNKNOWN")] Unknown = 0,
+    /// <summary>
+    /// GPS.
+    /// </summary>
+    [pbr::OriginalName("GPS")] Gps = 1,
+    /// <summary>
+    /// Manually configured.
+    /// </summary>
+    [pbr::OriginalName("CONFIG")] Config = 2,
+    /// <summary>
+    /// Geo resolver (TDOA).
+    /// </summary>
+    [pbr::OriginalName("GEO_RESOLVER_TDOA")] GeoResolverTdoa = 3,
+    /// <summary>
+    /// Geo resolver (RSSI).
+    /// </summary>
+    [pbr::OriginalName("GEO_RESOLVER_RSSI")] GeoResolverRssi = 4,
+    /// <summary>
+    /// Geo resolver (GNSS).
+    /// </summary>
+    [pbr::OriginalName("GEO_RESOLVER_GNSS")] GeoResolverGnss = 5,
+    /// <summary>
+    /// Geo resolver (WIFI).
+    /// </summary>
+    [pbr::OriginalName("GEO_RESOLVER_WIFI")] GeoResolverWifi = 6,
+  }
+
+  public enum Aggregation {
+    /// <summary>
+    /// Hour.
+    /// </summary>
+    [pbr::OriginalName("HOUR")] Hour = 0,
+    /// <summary>
+    /// Day.
+    /// </summary>
+    [pbr::OriginalName("DAY")] Day = 1,
+    /// <summary>
+    /// Month.
+    /// </summary>
+    [pbr::OriginalName("MONTH")] Month = 2,
+  }
+
+  public enum MetricKind {
+    /// <summary>
+    /// Incrementing counters that never decrease (these are not reset on each reading).
+    /// </summary>
+    [pbr::OriginalName("COUNTER")] Counter = 0,
+    /// <summary>
+    /// Counters that do get reset upon reading.
+    /// </summary>
+    [pbr::OriginalName("ABSOLUTE")] Absolute = 1,
+    /// <summary>
+    /// E.g. a temperature value.
+    /// </summary>
+    [pbr::OriginalName("GAUGE")] Gauge = 2,
+  }
+
+  #endregion
+
+  #region Messages
+  public sealed partial class Location : pb::IMessage<Location>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<Location> _parser = new pb::MessageParser<Location>(() => new Location());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<Location> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Common.CommonReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Location() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Location(Location other) : this() {
+      latitude_ = other.latitude_;
+      longitude_ = other.longitude_;
+      altitude_ = other.altitude_;
+      source_ = other.source_;
+      accuracy_ = other.accuracy_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Location Clone() {
+      return new Location(this);
+    }
+
+    /// <summary>Field number for the "latitude" field.</summary>
+    public const int LatitudeFieldNumber = 1;
+    private double latitude_;
+    /// <summary>
+    /// Latitude.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public double Latitude {
+      get { return latitude_; }
+      set {
+        latitude_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "longitude" field.</summary>
+    public const int LongitudeFieldNumber = 2;
+    private double longitude_;
+    /// <summary>
+    /// Longitude.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public double Longitude {
+      get { return longitude_; }
+      set {
+        longitude_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "altitude" field.</summary>
+    public const int AltitudeFieldNumber = 3;
+    private double altitude_;
+    /// <summary>
+    /// Altitude.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public double Altitude {
+      get { return altitude_; }
+      set {
+        altitude_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "source" field.</summary>
+    public const int SourceFieldNumber = 4;
+    private global::Chirpstack.Common.LocationSource source_ = global::Chirpstack.Common.LocationSource.Unknown;
+    /// <summary>
+    /// Location source.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.LocationSource Source {
+      get { return source_; }
+      set {
+        source_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "accuracy" field.</summary>
+    public const int AccuracyFieldNumber = 5;
+    private float accuracy_;
+    /// <summary>
+    /// Accuracy.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public float Accuracy {
+      get { return accuracy_; }
+      set {
+        accuracy_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as Location);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(Location other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(Latitude, other.Latitude)) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(Longitude, other.Longitude)) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(Altitude, other.Altitude)) return false;
+      if (Source != other.Source) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(Accuracy, other.Accuracy)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Latitude != 0D) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(Latitude);
+      if (Longitude != 0D) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(Longitude);
+      if (Altitude != 0D) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(Altitude);
+      if (Source != global::Chirpstack.Common.LocationSource.Unknown) hash ^= Source.GetHashCode();
+      if (Accuracy != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(Accuracy);
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Latitude != 0D) {
+        output.WriteRawTag(9);
+        output.WriteDouble(Latitude);
+      }
+      if (Longitude != 0D) {
+        output.WriteRawTag(17);
+        output.WriteDouble(Longitude);
+      }
+      if (Altitude != 0D) {
+        output.WriteRawTag(25);
+        output.WriteDouble(Altitude);
+      }
+      if (Source != global::Chirpstack.Common.LocationSource.Unknown) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Source);
+      }
+      if (Accuracy != 0F) {
+        output.WriteRawTag(45);
+        output.WriteFloat(Accuracy);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Latitude != 0D) {
+        output.WriteRawTag(9);
+        output.WriteDouble(Latitude);
+      }
+      if (Longitude != 0D) {
+        output.WriteRawTag(17);
+        output.WriteDouble(Longitude);
+      }
+      if (Altitude != 0D) {
+        output.WriteRawTag(25);
+        output.WriteDouble(Altitude);
+      }
+      if (Source != global::Chirpstack.Common.LocationSource.Unknown) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Source);
+      }
+      if (Accuracy != 0F) {
+        output.WriteRawTag(45);
+        output.WriteFloat(Accuracy);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Latitude != 0D) {
+        size += 1 + 8;
+      }
+      if (Longitude != 0D) {
+        size += 1 + 8;
+      }
+      if (Altitude != 0D) {
+        size += 1 + 8;
+      }
+      if (Source != global::Chirpstack.Common.LocationSource.Unknown) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Source);
+      }
+      if (Accuracy != 0F) {
+        size += 1 + 4;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(Location other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Latitude != 0D) {
+        Latitude = other.Latitude;
+      }
+      if (other.Longitude != 0D) {
+        Longitude = other.Longitude;
+      }
+      if (other.Altitude != 0D) {
+        Altitude = other.Altitude;
+      }
+      if (other.Source != global::Chirpstack.Common.LocationSource.Unknown) {
+        Source = other.Source;
+      }
+      if (other.Accuracy != 0F) {
+        Accuracy = other.Accuracy;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 9: {
+            Latitude = input.ReadDouble();
+            break;
+          }
+          case 17: {
+            Longitude = input.ReadDouble();
+            break;
+          }
+          case 25: {
+            Altitude = input.ReadDouble();
+            break;
+          }
+          case 32: {
+            Source = (global::Chirpstack.Common.LocationSource) input.ReadEnum();
+            break;
+          }
+          case 45: {
+            Accuracy = input.ReadFloat();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 9: {
+            Latitude = input.ReadDouble();
+            break;
+          }
+          case 17: {
+            Longitude = input.ReadDouble();
+            break;
+          }
+          case 25: {
+            Altitude = input.ReadDouble();
+            break;
+          }
+          case 32: {
+            Source = (global::Chirpstack.Common.LocationSource) input.ReadEnum();
+            break;
+          }
+          case 45: {
+            Accuracy = input.ReadFloat();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class KeyEnvelope : pb::IMessage<KeyEnvelope>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<KeyEnvelope> _parser = new pb::MessageParser<KeyEnvelope>(() => new KeyEnvelope());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<KeyEnvelope> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Common.CommonReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public KeyEnvelope() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public KeyEnvelope(KeyEnvelope other) : this() {
+      kekLabel_ = other.kekLabel_;
+      aesKey_ = other.aesKey_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public KeyEnvelope Clone() {
+      return new KeyEnvelope(this);
+    }
+
+    /// <summary>Field number for the "kek_label" field.</summary>
+    public const int KekLabelFieldNumber = 1;
+    private string kekLabel_ = "";
+    /// <summary>
+    /// KEK label.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string KekLabel {
+      get { return kekLabel_; }
+      set {
+        kekLabel_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "aes_key" field.</summary>
+    public const int AesKeyFieldNumber = 2;
+    private pb::ByteString aesKey_ = pb::ByteString.Empty;
+    /// <summary>
+    /// AES key (when the kek_label is set, this value must first be decrypted).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString AesKey {
+      get { return aesKey_; }
+      set {
+        aesKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as KeyEnvelope);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(KeyEnvelope other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (KekLabel != other.KekLabel) return false;
+      if (AesKey != other.AesKey) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (KekLabel.Length != 0) hash ^= KekLabel.GetHashCode();
+      if (AesKey.Length != 0) hash ^= AesKey.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (KekLabel.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(KekLabel);
+      }
+      if (AesKey.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteBytes(AesKey);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (KekLabel.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(KekLabel);
+      }
+      if (AesKey.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteBytes(AesKey);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (KekLabel.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(KekLabel);
+      }
+      if (AesKey.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(AesKey);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(KeyEnvelope other) {
+      if (other == null) {
+        return;
+      }
+      if (other.KekLabel.Length != 0) {
+        KekLabel = other.KekLabel;
+      }
+      if (other.AesKey.Length != 0) {
+        AesKey = other.AesKey;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            KekLabel = input.ReadString();
+            break;
+          }
+          case 18: {
+            AesKey = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            KekLabel = input.ReadString();
+            break;
+          }
+          case 18: {
+            AesKey = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class Metric : pb::IMessage<Metric>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<Metric> _parser = new pb::MessageParser<Metric>(() => new Metric());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<Metric> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Common.CommonReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Metric() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Metric(Metric other) : this() {
+      name_ = other.name_;
+      timestamps_ = other.timestamps_.Clone();
+      datasets_ = other.datasets_.Clone();
+      kind_ = other.kind_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Metric Clone() {
+      return new Metric(this);
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 1;
+    private string name_ = "";
+    /// <summary>
+    /// Name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "timestamps" field.</summary>
+    public const int TimestampsFieldNumber = 2;
+    private static readonly pb::FieldCodec<global::Google.Protobuf.WellKnownTypes.Timestamp> _repeated_timestamps_codec
+        = pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Timestamp.Parser);
+    private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Timestamp> timestamps_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Timestamp>();
+    /// <summary>
+    /// Timestamps.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Timestamp> Timestamps {
+      get { return timestamps_; }
+    }
+
+    /// <summary>Field number for the "datasets" field.</summary>
+    public const int DatasetsFieldNumber = 3;
+    private static readonly pb::FieldCodec<global::Chirpstack.Common.MetricDataset> _repeated_datasets_codec
+        = pb::FieldCodec.ForMessage(26, global::Chirpstack.Common.MetricDataset.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Common.MetricDataset> datasets_ = new pbc::RepeatedField<global::Chirpstack.Common.MetricDataset>();
+    /// <summary>
+    /// Datasets.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Common.MetricDataset> Datasets {
+      get { return datasets_; }
+    }
+
+    /// <summary>Field number for the "kind" field.</summary>
+    public const int KindFieldNumber = 4;
+    private global::Chirpstack.Common.MetricKind kind_ = global::Chirpstack.Common.MetricKind.Counter;
+    /// <summary>
+    /// Kind.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.MetricKind Kind {
+      get { return kind_; }
+      set {
+        kind_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as Metric);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(Metric other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Name != other.Name) return false;
+      if(!timestamps_.Equals(other.timestamps_)) return false;
+      if(!datasets_.Equals(other.datasets_)) return false;
+      if (Kind != other.Kind) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      hash ^= timestamps_.GetHashCode();
+      hash ^= datasets_.GetHashCode();
+      if (Kind != global::Chirpstack.Common.MetricKind.Counter) hash ^= Kind.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      timestamps_.WriteTo(output, _repeated_timestamps_codec);
+      datasets_.WriteTo(output, _repeated_datasets_codec);
+      if (Kind != global::Chirpstack.Common.MetricKind.Counter) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Kind);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+      timestamps_.WriteTo(ref output, _repeated_timestamps_codec);
+      datasets_.WriteTo(ref output, _repeated_datasets_codec);
+      if (Kind != global::Chirpstack.Common.MetricKind.Counter) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Kind);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      size += timestamps_.CalculateSize(_repeated_timestamps_codec);
+      size += datasets_.CalculateSize(_repeated_datasets_codec);
+      if (Kind != global::Chirpstack.Common.MetricKind.Counter) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Kind);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(Metric other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+      timestamps_.Add(other.timestamps_);
+      datasets_.Add(other.datasets_);
+      if (other.Kind != global::Chirpstack.Common.MetricKind.Counter) {
+        Kind = other.Kind;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Name = input.ReadString();
+            break;
+          }
+          case 18: {
+            timestamps_.AddEntriesFrom(input, _repeated_timestamps_codec);
+            break;
+          }
+          case 26: {
+            datasets_.AddEntriesFrom(input, _repeated_datasets_codec);
+            break;
+          }
+          case 32: {
+            Kind = (global::Chirpstack.Common.MetricKind) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Name = input.ReadString();
+            break;
+          }
+          case 18: {
+            timestamps_.AddEntriesFrom(ref input, _repeated_timestamps_codec);
+            break;
+          }
+          case 26: {
+            datasets_.AddEntriesFrom(ref input, _repeated_datasets_codec);
+            break;
+          }
+          case 32: {
+            Kind = (global::Chirpstack.Common.MetricKind) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class MetricDataset : pb::IMessage<MetricDataset>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<MetricDataset> _parser = new pb::MessageParser<MetricDataset>(() => new MetricDataset());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<MetricDataset> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Common.CommonReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MetricDataset() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MetricDataset(MetricDataset other) : this() {
+      label_ = other.label_;
+      data_ = other.data_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public MetricDataset Clone() {
+      return new MetricDataset(this);
+    }
+
+    /// <summary>Field number for the "label" field.</summary>
+    public const int LabelFieldNumber = 1;
+    private string label_ = "";
+    /// <summary>
+    /// Label.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Label {
+      get { return label_; }
+      set {
+        label_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "data" field.</summary>
+    public const int DataFieldNumber = 2;
+    private static readonly pb::FieldCodec<float> _repeated_data_codec
+        = pb::FieldCodec.ForFloat(18);
+    private readonly pbc::RepeatedField<float> data_ = new pbc::RepeatedField<float>();
+    /// <summary>
+    /// Data.
+    /// Each value index corresponds with the same timestamp index of the Metric.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<float> Data {
+      get { return data_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as MetricDataset);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(MetricDataset other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Label != other.Label) return false;
+      if(!data_.Equals(other.data_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Label.Length != 0) hash ^= Label.GetHashCode();
+      hash ^= data_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Label.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Label);
+      }
+      data_.WriteTo(output, _repeated_data_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Label.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Label);
+      }
+      data_.WriteTo(ref output, _repeated_data_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Label.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Label);
+      }
+      size += data_.CalculateSize(_repeated_data_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(MetricDataset other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Label.Length != 0) {
+        Label = other.Label;
+      }
+      data_.Add(other.data_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Label = input.ReadString();
+            break;
+          }
+          case 18:
+          case 21: {
+            data_.AddEntriesFrom(input, _repeated_data_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Label = input.ReadString();
+            break;
+          }
+          case 18:
+          case 21: {
+            data_.AddEntriesFrom(ref input, _repeated_data_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/google/api/Annotations.cs b/api/csharp/Chirpstack/google/api/Annotations.cs
new file mode 100644
index 00000000..b89a53c6
--- /dev/null
+++ b/api/csharp/Chirpstack/google/api/Annotations.cs
@@ -0,0 +1,52 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: google/api/annotations.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Google.Api {
+
+  /// <summary>Holder for reflection information generated from google/api/annotations.proto</summary>
+  public static partial class AnnotationsReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for google/api/annotations.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static AnnotationsReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "Chxnb29nbGUvYXBpL2Fubm90YXRpb25zLnByb3RvEgpnb29nbGUuYXBpGhVn",
+            "b29nbGUvYXBpL2h0dHAucHJvdG8aIGdvb2dsZS9wcm90b2J1Zi9kZXNjcmlw",
+            "dG9yLnByb3RvOkUKBGh0dHASHi5nb29nbGUucHJvdG9idWYuTWV0aG9kT3B0",
+            "aW9ucxiwyrwiIAEoCzIULmdvb2dsZS5hcGkuSHR0cFJ1bGVCbgoOY29tLmdv",
+            "b2dsZS5hcGlCEEFubm90YXRpb25zUHJvdG9QAVpBZ29vZ2xlLmdvbGFuZy5v",
+            "cmcvZ2VucHJvdG8vZ29vZ2xlYXBpcy9hcGkvYW5ub3RhdGlvbnM7YW5ub3Rh",
+            "dGlvbnOiAgRHQVBJYgZwcm90bzM="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Google.Api.HttpReflection.Descriptor, global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(null, new pb::Extension[] { AnnotationsExtensions.Http }, null));
+    }
+    #endregion
+
+  }
+  /// <summary>Holder for extension identifiers generated from the top level of google/api/annotations.proto</summary>
+  public static partial class AnnotationsExtensions {
+    /// <summary>
+    /// See `HttpRule`.
+    /// </summary>
+    public static readonly pb::Extension<global::Google.Protobuf.Reflection.MethodOptions, global::Google.Api.HttpRule> Http =
+      new pb::Extension<global::Google.Protobuf.Reflection.MethodOptions, global::Google.Api.HttpRule>(72295728, pb::FieldCodec.ForMessage(578365826, global::Google.Api.HttpRule.Parser));
+  }
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/google/api/Http.cs b/api/csharp/Chirpstack/google/api/Http.cs
new file mode 100644
index 00000000..c9611753
--- /dev/null
+++ b/api/csharp/Chirpstack/google/api/Http.cs
@@ -0,0 +1,1411 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: google/api/http.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Google.Api {
+
+  /// <summary>Holder for reflection information generated from google/api/http.proto</summary>
+  public static partial class HttpReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for google/api/http.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static HttpReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChVnb29nbGUvYXBpL2h0dHAucHJvdG8SCmdvb2dsZS5hcGkiVAoESHR0cBIj",
+            "CgVydWxlcxgBIAMoCzIULmdvb2dsZS5hcGkuSHR0cFJ1bGUSJwofZnVsbHlf",
+            "ZGVjb2RlX3Jlc2VydmVkX2V4cGFuc2lvbhgCIAEoCCKBAgoISHR0cFJ1bGUS",
+            "EAoIc2VsZWN0b3IYASABKAkSDQoDZ2V0GAIgASgJSAASDQoDcHV0GAMgASgJ",
+            "SAASDgoEcG9zdBgEIAEoCUgAEhAKBmRlbGV0ZRgFIAEoCUgAEg8KBXBhdGNo",
+            "GAYgASgJSAASLwoGY3VzdG9tGAggASgLMh0uZ29vZ2xlLmFwaS5DdXN0b21I",
+            "dHRwUGF0dGVybkgAEgwKBGJvZHkYByABKAkSFQoNcmVzcG9uc2VfYm9keRgM",
+            "IAEoCRIxChNhZGRpdGlvbmFsX2JpbmRpbmdzGAsgAygLMhQuZ29vZ2xlLmFw",
+            "aS5IdHRwUnVsZUIJCgdwYXR0ZXJuIi8KEUN1c3RvbUh0dHBQYXR0ZXJuEgwK",
+            "BGtpbmQYASABKAkSDAoEcGF0aBgCIAEoCUJqCg5jb20uZ29vZ2xlLmFwaUIJ",
+            "SHR0cFByb3RvUAFaQWdvb2dsZS5nb2xhbmcub3JnL2dlbnByb3RvL2dvb2ds",
+            "ZWFwaXMvYXBpL2Fubm90YXRpb25zO2Fubm90YXRpb25z+AEBogIER0FQSWIG",
+            "cHJvdG8z"));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { },
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Api.Http), global::Google.Api.Http.Parser, new[]{ "Rules", "FullyDecodeReservedExpansion" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Api.HttpRule), global::Google.Api.HttpRule.Parser, new[]{ "Selector", "Get", "Put", "Post", "Delete", "Patch", "Custom", "Body", "ResponseBody", "AdditionalBindings" }, new[]{ "Pattern" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Api.CustomHttpPattern), global::Google.Api.CustomHttpPattern.Parser, new[]{ "Kind", "Path" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  /// <summary>
+  /// Defines the HTTP configuration for an API service. It contains a list of
+  /// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
+  /// to one or more HTTP REST API methods.
+  /// </summary>
+  public sealed partial class Http : pb::IMessage<Http>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<Http> _parser = new pb::MessageParser<Http>(() => new Http());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<Http> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Google.Api.HttpReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Http() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Http(Http other) : this() {
+      rules_ = other.rules_.Clone();
+      fullyDecodeReservedExpansion_ = other.fullyDecodeReservedExpansion_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Http Clone() {
+      return new Http(this);
+    }
+
+    /// <summary>Field number for the "rules" field.</summary>
+    public const int RulesFieldNumber = 1;
+    private static readonly pb::FieldCodec<global::Google.Api.HttpRule> _repeated_rules_codec
+        = pb::FieldCodec.ForMessage(10, global::Google.Api.HttpRule.Parser);
+    private readonly pbc::RepeatedField<global::Google.Api.HttpRule> rules_ = new pbc::RepeatedField<global::Google.Api.HttpRule>();
+    /// <summary>
+    /// A list of HTTP configuration rules that apply to individual API methods.
+    ///
+    /// **NOTE:** All service configuration rules follow "last one wins" order.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Google.Api.HttpRule> Rules {
+      get { return rules_; }
+    }
+
+    /// <summary>Field number for the "fully_decode_reserved_expansion" field.</summary>
+    public const int FullyDecodeReservedExpansionFieldNumber = 2;
+    private bool fullyDecodeReservedExpansion_;
+    /// <summary>
+    /// When set to true, URL path parameters will be fully URI-decoded except in
+    /// cases of single segment matches in reserved expansion, where "%2F" will be
+    /// left encoded.
+    ///
+    /// The default behavior is to not decode RFC 6570 reserved characters in multi
+    /// segment matches.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool FullyDecodeReservedExpansion {
+      get { return fullyDecodeReservedExpansion_; }
+      set {
+        fullyDecodeReservedExpansion_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as Http);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(Http other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if(!rules_.Equals(other.rules_)) return false;
+      if (FullyDecodeReservedExpansion != other.FullyDecodeReservedExpansion) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= rules_.GetHashCode();
+      if (FullyDecodeReservedExpansion != false) hash ^= FullyDecodeReservedExpansion.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      rules_.WriteTo(output, _repeated_rules_codec);
+      if (FullyDecodeReservedExpansion != false) {
+        output.WriteRawTag(16);
+        output.WriteBool(FullyDecodeReservedExpansion);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      rules_.WriteTo(ref output, _repeated_rules_codec);
+      if (FullyDecodeReservedExpansion != false) {
+        output.WriteRawTag(16);
+        output.WriteBool(FullyDecodeReservedExpansion);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      size += rules_.CalculateSize(_repeated_rules_codec);
+      if (FullyDecodeReservedExpansion != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(Http other) {
+      if (other == null) {
+        return;
+      }
+      rules_.Add(other.rules_);
+      if (other.FullyDecodeReservedExpansion != false) {
+        FullyDecodeReservedExpansion = other.FullyDecodeReservedExpansion;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            rules_.AddEntriesFrom(input, _repeated_rules_codec);
+            break;
+          }
+          case 16: {
+            FullyDecodeReservedExpansion = input.ReadBool();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            rules_.AddEntriesFrom(ref input, _repeated_rules_codec);
+            break;
+          }
+          case 16: {
+            FullyDecodeReservedExpansion = input.ReadBool();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// # gRPC Transcoding
+  ///
+  /// gRPC Transcoding is a feature for mapping between a gRPC method and one or
+  /// more HTTP REST endpoints. It allows developers to build a single API service
+  /// that supports both gRPC APIs and REST APIs. Many systems, including [Google
+  /// APIs](https://github.com/googleapis/googleapis),
+  /// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC
+  /// Gateway](https://github.com/grpc-ecosystem/grpc-gateway),
+  /// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature
+  /// and use it for large scale production services.
+  ///
+  /// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies
+  /// how different portions of the gRPC request message are mapped to the URL
+  /// path, URL query parameters, and HTTP request body. It also controls how the
+  /// gRPC response message is mapped to the HTTP response body. `HttpRule` is
+  /// typically specified as an `google.api.http` annotation on the gRPC method.
+  ///
+  /// Each mapping specifies a URL path template and an HTTP method. The path
+  /// template may refer to one or more fields in the gRPC request message, as long
+  /// as each field is a non-repeated field with a primitive (non-message) type.
+  /// The path template controls how fields of the request message are mapped to
+  /// the URL path.
+  ///
+  /// Example:
+  ///
+  ///     service Messaging {
+  ///       rpc GetMessage(GetMessageRequest) returns (Message) {
+  ///         option (google.api.http) = {
+  ///             get: "/v1/{name=messages/*}"
+  ///         };
+  ///       }
+  ///     }
+  ///     message GetMessageRequest {
+  ///       string name = 1; // Mapped to URL path.
+  ///     }
+  ///     message Message {
+  ///       string text = 1; // The resource content.
+  ///     }
+  ///
+  /// This enables an HTTP REST to gRPC mapping as below:
+  ///
+  /// HTTP | gRPC
+  /// -----|-----
+  /// `GET /v1/messages/123456`  | `GetMessage(name: "messages/123456")`
+  ///
+  /// Any fields in the request message which are not bound by the path template
+  /// automatically become HTTP query parameters if there is no HTTP request body.
+  /// For example:
+  ///
+  ///     service Messaging {
+  ///       rpc GetMessage(GetMessageRequest) returns (Message) {
+  ///         option (google.api.http) = {
+  ///             get:"/v1/messages/{message_id}"
+  ///         };
+  ///       }
+  ///     }
+  ///     message GetMessageRequest {
+  ///       message SubMessage {
+  ///         string subfield = 1;
+  ///       }
+  ///       string message_id = 1; // Mapped to URL path.
+  ///       int64 revision = 2;    // Mapped to URL query parameter `revision`.
+  ///       SubMessage sub = 3;    // Mapped to URL query parameter `sub.subfield`.
+  ///     }
+  ///
+  /// This enables a HTTP JSON to RPC mapping as below:
+  ///
+  /// HTTP | gRPC
+  /// -----|-----
+  /// `GET /v1/messages/123456?revision=2&amp;sub.subfield=foo` |
+  /// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield:
+  /// "foo"))`
+  ///
+  /// Note that fields which are mapped to URL query parameters must have a
+  /// primitive type or a repeated primitive type or a non-repeated message type.
+  /// In the case of a repeated type, the parameter can be repeated in the URL
+  /// as `...?param=A&amp;param=B`. In the case of a message type, each field of the
+  /// message is mapped to a separate parameter, such as
+  /// `...?foo.a=A&amp;foo.b=B&amp;foo.c=C`.
+  ///
+  /// For HTTP methods that allow a request body, the `body` field
+  /// specifies the mapping. Consider a REST update method on the
+  /// message resource collection:
+  ///
+  ///     service Messaging {
+  ///       rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
+  ///         option (google.api.http) = {
+  ///           patch: "/v1/messages/{message_id}"
+  ///           body: "message"
+  ///         };
+  ///       }
+  ///     }
+  ///     message UpdateMessageRequest {
+  ///       string message_id = 1; // mapped to the URL
+  ///       Message message = 2;   // mapped to the body
+  ///     }
+  ///
+  /// The following HTTP JSON to RPC mapping is enabled, where the
+  /// representation of the JSON in the request body is determined by
+  /// protos JSON encoding:
+  ///
+  /// HTTP | gRPC
+  /// -----|-----
+  /// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+  /// "123456" message { text: "Hi!" })`
+  ///
+  /// The special name `*` can be used in the body mapping to define that
+  /// every field not bound by the path template should be mapped to the
+  /// request body.  This enables the following alternative definition of
+  /// the update method:
+  ///
+  ///     service Messaging {
+  ///       rpc UpdateMessage(Message) returns (Message) {
+  ///         option (google.api.http) = {
+  ///           patch: "/v1/messages/{message_id}"
+  ///           body: "*"
+  ///         };
+  ///       }
+  ///     }
+  ///     message Message {
+  ///       string message_id = 1;
+  ///       string text = 2;
+  ///     }
+  ///
+  /// The following HTTP JSON to RPC mapping is enabled:
+  ///
+  /// HTTP | gRPC
+  /// -----|-----
+  /// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+  /// "123456" text: "Hi!")`
+  ///
+  /// Note that when using `*` in the body mapping, it is not possible to
+  /// have HTTP parameters, as all fields not bound by the path end in
+  /// the body. This makes this option more rarely used in practice when
+  /// defining REST APIs. The common usage of `*` is in custom methods
+  /// which don't use the URL at all for transferring data.
+  ///
+  /// It is possible to define multiple HTTP methods for one RPC by using
+  /// the `additional_bindings` option. Example:
+  ///
+  ///     service Messaging {
+  ///       rpc GetMessage(GetMessageRequest) returns (Message) {
+  ///         option (google.api.http) = {
+  ///           get: "/v1/messages/{message_id}"
+  ///           additional_bindings {
+  ///             get: "/v1/users/{user_id}/messages/{message_id}"
+  ///           }
+  ///         };
+  ///       }
+  ///     }
+  ///     message GetMessageRequest {
+  ///       string message_id = 1;
+  ///       string user_id = 2;
+  ///     }
+  ///
+  /// This enables the following two alternative HTTP JSON to RPC mappings:
+  ///
+  /// HTTP | gRPC
+  /// -----|-----
+  /// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
+  /// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id:
+  /// "123456")`
+  ///
+  /// ## Rules for HTTP mapping
+  ///
+  /// 1. Leaf request fields (recursive expansion nested messages in the request
+  ///    message) are classified into three categories:
+  ///    - Fields referred by the path template. They are passed via the URL path.
+  ///    - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP
+  ///      request body.
+  ///    - All other fields are passed via the URL query parameters, and the
+  ///      parameter name is the field path in the request message. A repeated
+  ///      field can be represented as multiple query parameters under the same
+  ///      name.
+  ///  2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields
+  ///     are passed via URL path and HTTP request body.
+  ///  3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all
+  ///     fields are passed via URL path and URL query parameters.
+  ///
+  /// ### Path template syntax
+  ///
+  ///     Template = "/" Segments [ Verb ] ;
+  ///     Segments = Segment { "/" Segment } ;
+  ///     Segment  = "*" | "**" | LITERAL | Variable ;
+  ///     Variable = "{" FieldPath [ "=" Segments ] "}" ;
+  ///     FieldPath = IDENT { "." IDENT } ;
+  ///     Verb     = ":" LITERAL ;
+  ///
+  /// The syntax `*` matches a single URL path segment. The syntax `**` matches
+  /// zero or more URL path segments, which must be the last part of the URL path
+  /// except the `Verb`.
+  ///
+  /// The syntax `Variable` matches part of the URL path as specified by its
+  /// template. A variable template must not contain other variables. If a variable
+  /// matches a single path segment, its template may be omitted, e.g. `{var}`
+  /// is equivalent to `{var=*}`.
+  ///
+  /// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`
+  /// contains any reserved character, such characters should be percent-encoded
+  /// before the matching.
+  ///
+  /// If a variable contains exactly one path segment, such as `"{var}"` or
+  /// `"{var=*}"`, when such a variable is expanded into a URL path on the client
+  /// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The
+  /// server side does the reverse decoding. Such variables show up in the
+  /// [Discovery
+  /// Document](https://developers.google.com/discovery/v1/reference/apis) as
+  /// `{var}`.
+  ///
+  /// If a variable contains multiple path segments, such as `"{var=foo/*}"`
+  /// or `"{var=**}"`, when such a variable is expanded into a URL path on the
+  /// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.
+  /// The server side does the reverse decoding, except "%2F" and "%2f" are left
+  /// unchanged. Such variables show up in the
+  /// [Discovery
+  /// Document](https://developers.google.com/discovery/v1/reference/apis) as
+  /// `{+var}`.
+  ///
+  /// ## Using gRPC API Service Configuration
+  ///
+  /// gRPC API Service Configuration (service config) is a configuration language
+  /// for configuring a gRPC service to become a user-facing product. The
+  /// service config is simply the YAML representation of the `google.api.Service`
+  /// proto message.
+  ///
+  /// As an alternative to annotating your proto file, you can configure gRPC
+  /// transcoding in your service config YAML files. You do this by specifying a
+  /// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same
+  /// effect as the proto annotation. This can be particularly useful if you
+  /// have a proto that is reused in multiple services. Note that any transcoding
+  /// specified in the service config will override any matching transcoding
+  /// configuration in the proto.
+  ///
+  /// Example:
+  ///
+  ///     http:
+  ///       rules:
+  ///         # Selects a gRPC method and applies HttpRule to it.
+  ///         - selector: example.v1.Messaging.GetMessage
+  ///           get: /v1/messages/{message_id}/{sub.subfield}
+  ///
+  /// ## Special notes
+  ///
+  /// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the
+  /// proto to JSON conversion must follow the [proto3
+  /// specification](https://developers.google.com/protocol-buffers/docs/proto3#json).
+  ///
+  /// While the single segment variable follows the semantics of
+  /// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
+  /// Expansion, the multi segment variable **does not** follow RFC 6570 Section
+  /// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion
+  /// does not expand special characters like `?` and `#`, which would lead
+  /// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding
+  /// for multi segment variables.
+  ///
+  /// The path variables **must not** refer to any repeated or mapped field,
+  /// because client libraries are not capable of handling such variable expansion.
+  ///
+  /// The path variables **must not** capture the leading "/" character. The reason
+  /// is that the most common use case "{var}" does not capture the leading "/"
+  /// character. For consistency, all path variables must share the same behavior.
+  ///
+  /// Repeated message fields must not be mapped to URL query parameters, because
+  /// no client library can support such complicated mapping.
+  ///
+  /// If an API needs to use a JSON array for request or response body, it can map
+  /// the request or response body to a repeated field. However, some gRPC
+  /// Transcoding implementations may not support this feature.
+  /// </summary>
+  public sealed partial class HttpRule : pb::IMessage<HttpRule>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<HttpRule> _parser = new pb::MessageParser<HttpRule>(() => new HttpRule());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<HttpRule> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Google.Api.HttpReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public HttpRule() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public HttpRule(HttpRule other) : this() {
+      selector_ = other.selector_;
+      body_ = other.body_;
+      responseBody_ = other.responseBody_;
+      additionalBindings_ = other.additionalBindings_.Clone();
+      switch (other.PatternCase) {
+        case PatternOneofCase.Get:
+          Get = other.Get;
+          break;
+        case PatternOneofCase.Put:
+          Put = other.Put;
+          break;
+        case PatternOneofCase.Post:
+          Post = other.Post;
+          break;
+        case PatternOneofCase.Delete:
+          Delete = other.Delete;
+          break;
+        case PatternOneofCase.Patch:
+          Patch = other.Patch;
+          break;
+        case PatternOneofCase.Custom:
+          Custom = other.Custom.Clone();
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public HttpRule Clone() {
+      return new HttpRule(this);
+    }
+
+    /// <summary>Field number for the "selector" field.</summary>
+    public const int SelectorFieldNumber = 1;
+    private string selector_ = "";
+    /// <summary>
+    /// Selects a method to which this rule applies.
+    ///
+    /// Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Selector {
+      get { return selector_; }
+      set {
+        selector_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "get" field.</summary>
+    public const int GetFieldNumber = 2;
+    /// <summary>
+    /// Maps to HTTP GET. Used for listing and getting information about
+    /// resources.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Get {
+      get { return patternCase_ == PatternOneofCase.Get ? (string) pattern_ : ""; }
+      set {
+        pattern_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        patternCase_ = PatternOneofCase.Get;
+      }
+    }
+
+    /// <summary>Field number for the "put" field.</summary>
+    public const int PutFieldNumber = 3;
+    /// <summary>
+    /// Maps to HTTP PUT. Used for replacing a resource.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Put {
+      get { return patternCase_ == PatternOneofCase.Put ? (string) pattern_ : ""; }
+      set {
+        pattern_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        patternCase_ = PatternOneofCase.Put;
+      }
+    }
+
+    /// <summary>Field number for the "post" field.</summary>
+    public const int PostFieldNumber = 4;
+    /// <summary>
+    /// Maps to HTTP POST. Used for creating a resource or performing an action.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Post {
+      get { return patternCase_ == PatternOneofCase.Post ? (string) pattern_ : ""; }
+      set {
+        pattern_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        patternCase_ = PatternOneofCase.Post;
+      }
+    }
+
+    /// <summary>Field number for the "delete" field.</summary>
+    public const int DeleteFieldNumber = 5;
+    /// <summary>
+    /// Maps to HTTP DELETE. Used for deleting a resource.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Delete {
+      get { return patternCase_ == PatternOneofCase.Delete ? (string) pattern_ : ""; }
+      set {
+        pattern_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        patternCase_ = PatternOneofCase.Delete;
+      }
+    }
+
+    /// <summary>Field number for the "patch" field.</summary>
+    public const int PatchFieldNumber = 6;
+    /// <summary>
+    /// Maps to HTTP PATCH. Used for updating a resource.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Patch {
+      get { return patternCase_ == PatternOneofCase.Patch ? (string) pattern_ : ""; }
+      set {
+        pattern_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        patternCase_ = PatternOneofCase.Patch;
+      }
+    }
+
+    /// <summary>Field number for the "custom" field.</summary>
+    public const int CustomFieldNumber = 8;
+    /// <summary>
+    /// The custom pattern is used for specifying an HTTP method that is not
+    /// included in the `pattern` field, such as HEAD, or "*" to leave the
+    /// HTTP method unspecified for this rule. The wild-card rule is useful
+    /// for services that provide content to Web (HTML) clients.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Api.CustomHttpPattern Custom {
+      get { return patternCase_ == PatternOneofCase.Custom ? (global::Google.Api.CustomHttpPattern) pattern_ : null; }
+      set {
+        pattern_ = value;
+        patternCase_ = value == null ? PatternOneofCase.None : PatternOneofCase.Custom;
+      }
+    }
+
+    /// <summary>Field number for the "body" field.</summary>
+    public const int BodyFieldNumber = 7;
+    private string body_ = "";
+    /// <summary>
+    /// The name of the request field whose value is mapped to the HTTP request
+    /// body, or `*` for mapping all request fields not captured by the path
+    /// pattern to the HTTP body, or omitted for not having any HTTP request body.
+    ///
+    /// NOTE: the referred field must be present at the top-level of the request
+    /// message type.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Body {
+      get { return body_; }
+      set {
+        body_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "response_body" field.</summary>
+    public const int ResponseBodyFieldNumber = 12;
+    private string responseBody_ = "";
+    /// <summary>
+    /// Optional. The name of the response field whose value is mapped to the HTTP
+    /// response body. When omitted, the entire response message will be used
+    /// as the HTTP response body.
+    ///
+    /// NOTE: The referred field must be present at the top-level of the response
+    /// message type.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ResponseBody {
+      get { return responseBody_; }
+      set {
+        responseBody_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "additional_bindings" field.</summary>
+    public const int AdditionalBindingsFieldNumber = 11;
+    private static readonly pb::FieldCodec<global::Google.Api.HttpRule> _repeated_additionalBindings_codec
+        = pb::FieldCodec.ForMessage(90, global::Google.Api.HttpRule.Parser);
+    private readonly pbc::RepeatedField<global::Google.Api.HttpRule> additionalBindings_ = new pbc::RepeatedField<global::Google.Api.HttpRule>();
+    /// <summary>
+    /// Additional HTTP bindings for the selector. Nested bindings must
+    /// not contain an `additional_bindings` field themselves (that is,
+    /// the nesting may only be one level deep).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Google.Api.HttpRule> AdditionalBindings {
+      get { return additionalBindings_; }
+    }
+
+    private object pattern_;
+    /// <summary>Enum of possible cases for the "pattern" oneof.</summary>
+    public enum PatternOneofCase {
+      None = 0,
+      Get = 2,
+      Put = 3,
+      Post = 4,
+      Delete = 5,
+      Patch = 6,
+      Custom = 8,
+    }
+    private PatternOneofCase patternCase_ = PatternOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public PatternOneofCase PatternCase {
+      get { return patternCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearPattern() {
+      patternCase_ = PatternOneofCase.None;
+      pattern_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as HttpRule);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(HttpRule other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Selector != other.Selector) return false;
+      if (Get != other.Get) return false;
+      if (Put != other.Put) return false;
+      if (Post != other.Post) return false;
+      if (Delete != other.Delete) return false;
+      if (Patch != other.Patch) return false;
+      if (!object.Equals(Custom, other.Custom)) return false;
+      if (Body != other.Body) return false;
+      if (ResponseBody != other.ResponseBody) return false;
+      if(!additionalBindings_.Equals(other.additionalBindings_)) return false;
+      if (PatternCase != other.PatternCase) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Selector.Length != 0) hash ^= Selector.GetHashCode();
+      if (patternCase_ == PatternOneofCase.Get) hash ^= Get.GetHashCode();
+      if (patternCase_ == PatternOneofCase.Put) hash ^= Put.GetHashCode();
+      if (patternCase_ == PatternOneofCase.Post) hash ^= Post.GetHashCode();
+      if (patternCase_ == PatternOneofCase.Delete) hash ^= Delete.GetHashCode();
+      if (patternCase_ == PatternOneofCase.Patch) hash ^= Patch.GetHashCode();
+      if (patternCase_ == PatternOneofCase.Custom) hash ^= Custom.GetHashCode();
+      if (Body.Length != 0) hash ^= Body.GetHashCode();
+      if (ResponseBody.Length != 0) hash ^= ResponseBody.GetHashCode();
+      hash ^= additionalBindings_.GetHashCode();
+      hash ^= (int) patternCase_;
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Selector.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Selector);
+      }
+      if (patternCase_ == PatternOneofCase.Get) {
+        output.WriteRawTag(18);
+        output.WriteString(Get);
+      }
+      if (patternCase_ == PatternOneofCase.Put) {
+        output.WriteRawTag(26);
+        output.WriteString(Put);
+      }
+      if (patternCase_ == PatternOneofCase.Post) {
+        output.WriteRawTag(34);
+        output.WriteString(Post);
+      }
+      if (patternCase_ == PatternOneofCase.Delete) {
+        output.WriteRawTag(42);
+        output.WriteString(Delete);
+      }
+      if (patternCase_ == PatternOneofCase.Patch) {
+        output.WriteRawTag(50);
+        output.WriteString(Patch);
+      }
+      if (Body.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(Body);
+      }
+      if (patternCase_ == PatternOneofCase.Custom) {
+        output.WriteRawTag(66);
+        output.WriteMessage(Custom);
+      }
+      additionalBindings_.WriteTo(output, _repeated_additionalBindings_codec);
+      if (ResponseBody.Length != 0) {
+        output.WriteRawTag(98);
+        output.WriteString(ResponseBody);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Selector.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Selector);
+      }
+      if (patternCase_ == PatternOneofCase.Get) {
+        output.WriteRawTag(18);
+        output.WriteString(Get);
+      }
+      if (patternCase_ == PatternOneofCase.Put) {
+        output.WriteRawTag(26);
+        output.WriteString(Put);
+      }
+      if (patternCase_ == PatternOneofCase.Post) {
+        output.WriteRawTag(34);
+        output.WriteString(Post);
+      }
+      if (patternCase_ == PatternOneofCase.Delete) {
+        output.WriteRawTag(42);
+        output.WriteString(Delete);
+      }
+      if (patternCase_ == PatternOneofCase.Patch) {
+        output.WriteRawTag(50);
+        output.WriteString(Patch);
+      }
+      if (Body.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(Body);
+      }
+      if (patternCase_ == PatternOneofCase.Custom) {
+        output.WriteRawTag(66);
+        output.WriteMessage(Custom);
+      }
+      additionalBindings_.WriteTo(ref output, _repeated_additionalBindings_codec);
+      if (ResponseBody.Length != 0) {
+        output.WriteRawTag(98);
+        output.WriteString(ResponseBody);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Selector.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Selector);
+      }
+      if (patternCase_ == PatternOneofCase.Get) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Get);
+      }
+      if (patternCase_ == PatternOneofCase.Put) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Put);
+      }
+      if (patternCase_ == PatternOneofCase.Post) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Post);
+      }
+      if (patternCase_ == PatternOneofCase.Delete) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Delete);
+      }
+      if (patternCase_ == PatternOneofCase.Patch) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Patch);
+      }
+      if (patternCase_ == PatternOneofCase.Custom) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Custom);
+      }
+      if (Body.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Body);
+      }
+      if (ResponseBody.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ResponseBody);
+      }
+      size += additionalBindings_.CalculateSize(_repeated_additionalBindings_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(HttpRule other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Selector.Length != 0) {
+        Selector = other.Selector;
+      }
+      if (other.Body.Length != 0) {
+        Body = other.Body;
+      }
+      if (other.ResponseBody.Length != 0) {
+        ResponseBody = other.ResponseBody;
+      }
+      additionalBindings_.Add(other.additionalBindings_);
+      switch (other.PatternCase) {
+        case PatternOneofCase.Get:
+          Get = other.Get;
+          break;
+        case PatternOneofCase.Put:
+          Put = other.Put;
+          break;
+        case PatternOneofCase.Post:
+          Post = other.Post;
+          break;
+        case PatternOneofCase.Delete:
+          Delete = other.Delete;
+          break;
+        case PatternOneofCase.Patch:
+          Patch = other.Patch;
+          break;
+        case PatternOneofCase.Custom:
+          if (Custom == null) {
+            Custom = new global::Google.Api.CustomHttpPattern();
+          }
+          Custom.MergeFrom(other.Custom);
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Selector = input.ReadString();
+            break;
+          }
+          case 18: {
+            Get = input.ReadString();
+            break;
+          }
+          case 26: {
+            Put = input.ReadString();
+            break;
+          }
+          case 34: {
+            Post = input.ReadString();
+            break;
+          }
+          case 42: {
+            Delete = input.ReadString();
+            break;
+          }
+          case 50: {
+            Patch = input.ReadString();
+            break;
+          }
+          case 58: {
+            Body = input.ReadString();
+            break;
+          }
+          case 66: {
+            global::Google.Api.CustomHttpPattern subBuilder = new global::Google.Api.CustomHttpPattern();
+            if (patternCase_ == PatternOneofCase.Custom) {
+              subBuilder.MergeFrom(Custom);
+            }
+            input.ReadMessage(subBuilder);
+            Custom = subBuilder;
+            break;
+          }
+          case 90: {
+            additionalBindings_.AddEntriesFrom(input, _repeated_additionalBindings_codec);
+            break;
+          }
+          case 98: {
+            ResponseBody = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Selector = input.ReadString();
+            break;
+          }
+          case 18: {
+            Get = input.ReadString();
+            break;
+          }
+          case 26: {
+            Put = input.ReadString();
+            break;
+          }
+          case 34: {
+            Post = input.ReadString();
+            break;
+          }
+          case 42: {
+            Delete = input.ReadString();
+            break;
+          }
+          case 50: {
+            Patch = input.ReadString();
+            break;
+          }
+          case 58: {
+            Body = input.ReadString();
+            break;
+          }
+          case 66: {
+            global::Google.Api.CustomHttpPattern subBuilder = new global::Google.Api.CustomHttpPattern();
+            if (patternCase_ == PatternOneofCase.Custom) {
+              subBuilder.MergeFrom(Custom);
+            }
+            input.ReadMessage(subBuilder);
+            Custom = subBuilder;
+            break;
+          }
+          case 90: {
+            additionalBindings_.AddEntriesFrom(ref input, _repeated_additionalBindings_codec);
+            break;
+          }
+          case 98: {
+            ResponseBody = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// A custom pattern is used for defining custom HTTP verb.
+  /// </summary>
+  public sealed partial class CustomHttpPattern : pb::IMessage<CustomHttpPattern>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<CustomHttpPattern> _parser = new pb::MessageParser<CustomHttpPattern>(() => new CustomHttpPattern());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<CustomHttpPattern> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Google.Api.HttpReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CustomHttpPattern() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CustomHttpPattern(CustomHttpPattern other) : this() {
+      kind_ = other.kind_;
+      path_ = other.path_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public CustomHttpPattern Clone() {
+      return new CustomHttpPattern(this);
+    }
+
+    /// <summary>Field number for the "kind" field.</summary>
+    public const int KindFieldNumber = 1;
+    private string kind_ = "";
+    /// <summary>
+    /// The name of this custom HTTP verb.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Kind {
+      get { return kind_; }
+      set {
+        kind_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "path" field.</summary>
+    public const int PathFieldNumber = 2;
+    private string path_ = "";
+    /// <summary>
+    /// The path matched by this custom verb.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Path {
+      get { return path_; }
+      set {
+        path_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as CustomHttpPattern);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(CustomHttpPattern other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Kind != other.Kind) return false;
+      if (Path != other.Path) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Kind.Length != 0) hash ^= Kind.GetHashCode();
+      if (Path.Length != 0) hash ^= Path.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Kind.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Kind);
+      }
+      if (Path.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Path);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Kind.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Kind);
+      }
+      if (Path.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Path);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Kind.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Kind);
+      }
+      if (Path.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Path);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(CustomHttpPattern other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Kind.Length != 0) {
+        Kind = other.Kind;
+      }
+      if (other.Path.Length != 0) {
+        Path = other.Path;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Kind = input.ReadString();
+            break;
+          }
+          case 18: {
+            Path = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Kind = input.ReadString();
+            break;
+          }
+          case 18: {
+            Path = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/gw/Gw.cs b/api/csharp/Chirpstack/gw/Gw.cs
new file mode 100644
index 00000000..ef08690c
--- /dev/null
+++ b/api/csharp/Chirpstack/gw/Gw.cs
@@ -0,0 +1,12325 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: gw/gw.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Gateway {
+
+  /// <summary>Holder for reflection information generated from gw/gw.proto</summary>
+  public static partial class GwReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for gw/gw.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static GwReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "Cgtndy9ndy5wcm90bxICZ3caE2NvbW1vbi9jb21tb24ucHJvdG8aH2dvb2ds",
+            "ZS9wcm90b2J1Zi90aW1lc3RhbXAucHJvdG8aHmdvb2dsZS9wcm90b2J1Zi9k",
+            "dXJhdGlvbi5wcm90bxocZ29vZ2xlL3Byb3RvYnVmL3N0cnVjdC5wcm90byKV",
+            "AQoKTW9kdWxhdGlvbhImCgRsb3JhGAMgASgLMhYuZ3cuTG9yYU1vZHVsYXRp",
+            "b25JbmZvSAASJAoDZnNrGAQgASgLMhUuZ3cuRnNrTW9kdWxhdGlvbkluZm9I",
+            "ABIrCgdscl9maHNzGAUgASgLMhguZ3cuTHJGaHNzTW9kdWxhdGlvbkluZm9I",
+            "AEIMCgpwYXJhbWV0ZXJzIo0CChJVcGxpbmtUeEluZm9MZWdhY3kSEQoJZnJl",
+            "cXVlbmN5GAEgASgNEiYKCm1vZHVsYXRpb24YAiABKA4yEi5jb21tb24uTW9k",
+            "dWxhdGlvbhI2ChRsb3JhX21vZHVsYXRpb25faW5mbxgDIAEoCzIWLmd3Lkxv",
+            "cmFNb2R1bGF0aW9uSW5mb0gAEjQKE2Zza19tb2R1bGF0aW9uX2luZm8YBCAB",
+            "KAsyFS5ndy5Gc2tNb2R1bGF0aW9uSW5mb0gAEjsKF2xyX2Zoc3NfbW9kdWxh",
+            "dGlvbl9pbmZvGAUgASgLMhguZ3cuTHJGaHNzTW9kdWxhdGlvbkluZm9IAEIR",
+            "Cg9tb2R1bGF0aW9uX2luZm8iRQoMVXBsaW5rVHhJbmZvEhEKCWZyZXF1ZW5j",
+            "eRgBIAEoDRIiCgptb2R1bGF0aW9uGAIgASgLMg4uZ3cuTW9kdWxhdGlvbiKc",
+            "AQoSTG9yYU1vZHVsYXRpb25JbmZvEhEKCWJhbmR3aWR0aBgBIAEoDRIYChBz",
+            "cHJlYWRpbmdfZmFjdG9yGAIgASgNEhgKEGNvZGVfcmF0ZV9sZWdhY3kYAyAB",
+            "KAkSHwoJY29kZV9yYXRlGAUgASgOMgwuZ3cuQ29kZVJhdGUSHgoWcG9sYXJp",
+            "emF0aW9uX2ludmVyc2lvbhgEIAEoCCJCChFGc2tNb2R1bGF0aW9uSW5mbxIb",
+            "ChNmcmVxdWVuY3lfZGV2aWF0aW9uGAEgASgNEhAKCGRhdGFyYXRlGAIgASgN",
+            "IoYBChRMckZoc3NNb2R1bGF0aW9uSW5mbxIfChdvcGVyYXRpbmdfY2hhbm5l",
+            "bF93aWR0aBgBIAEoDRIYChBjb2RlX3JhdGVfbGVnYWN5GAIgASgJEh8KCWNv",
+            "ZGVfcmF0ZRgEIAEoDjIMLmd3LkNvZGVSYXRlEhIKCmdyaWRfc3RlcHMYAyAB",
+            "KA0iVgoWRW5jcnlwdGVkRmluZVRpbWVzdGFtcBIVCg1hZXNfa2V5X2luZGV4",
+            "GAEgASgNEhQKDGVuY3J5cHRlZF9ucxgCIAEoDBIPCgdmcGdhX2lkGAMgASgM",
+            "Ij4KElBsYWluRmluZVRpbWVzdGFtcBIoCgR0aW1lGAEgASgLMhouZ29vZ2xl",
+            "LnByb3RvYnVmLlRpbWVzdGFtcCKQBwoMR2F0ZXdheVN0YXRzEhkKEWdhdGV3",
+            "YXlfaWRfbGVnYWN5GAEgASgMEhIKCmdhdGV3YXlfaWQYESABKAkSKAoEdGlt",
+            "ZRgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASIgoIbG9jYXRp",
+            "b24YAyABKAsyEC5jb21tb24uTG9jYXRpb24SFgoOY29uZmlnX3ZlcnNpb24Y",
+            "BCABKAkSGwoTcnhfcGFja2V0c19yZWNlaXZlZBgFIAEoDRIeChZyeF9wYWNr",
+            "ZXRzX3JlY2VpdmVkX29rGAYgASgNEhsKE3R4X3BhY2tldHNfcmVjZWl2ZWQY",
+            "ByABKA0SGgoSdHhfcGFja2V0c19lbWl0dGVkGAggASgNEjAKCG1ldGFkYXRh",
+            "GAogAygLMh4uZ3cuR2F0ZXdheVN0YXRzLk1ldGFkYXRhRW50cnkSTQoYdHhf",
+            "cGFja2V0c19wZXJfZnJlcXVlbmN5GAwgAygLMisuZ3cuR2F0ZXdheVN0YXRz",
+            "LlR4UGFja2V0c1BlckZyZXF1ZW5jeUVudHJ5Ek0KGHJ4X3BhY2tldHNfcGVy",
+            "X2ZyZXF1ZW5jeRgNIAMoCzIrLmd3LkdhdGV3YXlTdGF0cy5SeFBhY2tldHNQ",
+            "ZXJGcmVxdWVuY3lFbnRyeRI5Chl0eF9wYWNrZXRzX3Blcl9tb2R1bGF0aW9u",
+            "GA4gAygLMhYuZ3cuUGVyTW9kdWxhdGlvbkNvdW50EjkKGXJ4X3BhY2tldHNf",
+            "cGVyX21vZHVsYXRpb24YDyADKAsyFi5ndy5QZXJNb2R1bGF0aW9uQ291bnQS",
+            "RwoVdHhfcGFja2V0c19wZXJfc3RhdHVzGBAgAygLMiguZ3cuR2F0ZXdheVN0",
+            "YXRzLlR4UGFja2V0c1BlclN0YXR1c0VudHJ5Gi8KDU1ldGFkYXRhRW50cnkS",
+            "CwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ARo8ChpUeFBhY2tldHNQ",
+            "ZXJGcmVxdWVuY3lFbnRyeRILCgNrZXkYASABKA0SDQoFdmFsdWUYAiABKA06",
+            "AjgBGjwKGlJ4UGFja2V0c1BlckZyZXF1ZW5jeUVudHJ5EgsKA2tleRgBIAEo",
+            "DRINCgV2YWx1ZRgCIAEoDToCOAEaOQoXVHhQYWNrZXRzUGVyU3RhdHVzRW50",
+            "cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgNOgI4ASJHChJQZXJNb2R1",
+            "bGF0aW9uQ291bnQSIgoKbW9kdWxhdGlvbhgBIAEoCzIOLmd3Lk1vZHVsYXRp",
+            "b24SDQoFY291bnQYAiABKA0igAUKElVwbGlua1J4SW5mb0xlZ2FjeRISCgpn",
+            "YXRld2F5X2lkGAEgASgMEigKBHRpbWUYAiABKAsyGi5nb29nbGUucHJvdG9i",
+            "dWYuVGltZXN0YW1wEjcKFHRpbWVfc2luY2VfZ3BzX2Vwb2NoGAMgASgLMhku",
+            "Z29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uEgwKBHJzc2kYBSABKAUSEAoIbG9y",
+            "YV9zbnIYBiABKAESDwoHY2hhbm5lbBgHIAEoDRIQCghyZl9jaGFpbhgIIAEo",
+            "DRINCgVib2FyZBgJIAEoDRIPCgdhbnRlbm5hGAogASgNEiIKCGxvY2F0aW9u",
+            "GAsgASgLMhAuY29tbW9uLkxvY2F0aW9uEjIKE2ZpbmVfdGltZXN0YW1wX3R5",
+            "cGUYDCABKA4yFS5ndy5GaW5lVGltZXN0YW1wVHlwZRI+ChhlbmNyeXB0ZWRf",
+            "ZmluZV90aW1lc3RhbXAYDSABKAsyGi5ndy5FbmNyeXB0ZWRGaW5lVGltZXN0",
+            "YW1wSAASNgoUcGxhaW5fZmluZV90aW1lc3RhbXAYDiABKAsyFi5ndy5QbGFp",
+            "bkZpbmVUaW1lc3RhbXBIABIPCgdjb250ZXh0GA8gASgMEhEKCXVwbGlua19p",
+            "ZBgQIAEoDBIhCgpjcmNfc3RhdHVzGBEgASgOMg0uZ3cuQ1JDU3RhdHVzEjYK",
+            "CG1ldGFkYXRhGBIgAygLMiQuZ3cuVXBsaW5rUnhJbmZvTGVnYWN5Lk1ldGFk",
+            "YXRhRW50cnkaLwoNTWV0YWRhdGFFbnRyeRILCgNrZXkYASABKAkSDQoFdmFs",
+            "dWUYAiABKAk6AjgBQhAKDmZpbmVfdGltZXN0YW1wIu8DCgxVcGxpbmtSeElu",
+            "Zm8SEgoKZ2F0ZXdheV9pZBgBIAEoCRIRCgl1cGxpbmtfaWQYAiABKA0SKAoE",
+            "dGltZRgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASNwoUdGlt",
+            "ZV9zaW5jZV9ncHNfZXBvY2gYBCABKAsyGS5nb29nbGUucHJvdG9idWYuRHVy",
+            "YXRpb24SPAoZZmluZV90aW1lX3NpbmNlX2dwc19lcG9jaBgFIAEoCzIZLmdv",
+            "b2dsZS5wcm90b2J1Zi5EdXJhdGlvbhIMCgRyc3NpGAYgASgFEgsKA3NuchgH",
+            "IAEoAhIPCgdjaGFubmVsGAggASgNEhAKCHJmX2NoYWluGAkgASgNEg0KBWJv",
+            "YXJkGAogASgNEg8KB2FudGVubmEYCyABKA0SIgoIbG9jYXRpb24YDCABKAsy",
+            "EC5jb21tb24uTG9jYXRpb24SDwoHY29udGV4dBgNIAEoDBIwCghtZXRhZGF0",
+            "YRgPIAMoCzIeLmd3LlVwbGlua1J4SW5mby5NZXRhZGF0YUVudHJ5EiEKCmNy",
+            "Y19zdGF0dXMYECABKA4yDS5ndy5DUkNTdGF0dXMaLwoNTWV0YWRhdGFFbnRy",
+            "eRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBIoIEChREb3dubGlu",
+            "a1R4SW5mb0xlZ2FjeRISCgpnYXRld2F5X2lkGAEgASgMEhEKCWZyZXF1ZW5j",
+            "eRgFIAEoDRINCgVwb3dlchgGIAEoBRImCgptb2R1bGF0aW9uGAcgASgOMhIu",
+            "Y29tbW9uLk1vZHVsYXRpb24SNgoUbG9yYV9tb2R1bGF0aW9uX2luZm8YCCAB",
+            "KAsyFi5ndy5Mb3JhTW9kdWxhdGlvbkluZm9IABI0ChNmc2tfbW9kdWxhdGlv",
+            "bl9pbmZvGAkgASgLMhUuZ3cuRnNrTW9kdWxhdGlvbkluZm9IABINCgVib2Fy",
+            "ZBgKIAEoDRIPCgdhbnRlbm5hGAsgASgNEiIKBnRpbWluZxgMIAEoDjISLmd3",
+            "LkRvd25saW5rVGltaW5nEjwKF2ltbWVkaWF0ZWx5X3RpbWluZ19pbmZvGA0g",
+            "ASgLMhkuZ3cuSW1tZWRpYXRlbHlUaW1pbmdJbmZvSAESMAoRZGVsYXlfdGlt",
+            "aW5nX2luZm8YDiABKAsyEy5ndy5EZWxheVRpbWluZ0luZm9IARI3ChVncHNf",
+            "ZXBvY2hfdGltaW5nX2luZm8YDyABKAsyFi5ndy5HUFNFcG9jaFRpbWluZ0lu",
+            "Zm9IARIPCgdjb250ZXh0GBAgASgMQhEKD21vZHVsYXRpb25faW5mb0INCgt0",
+            "aW1pbmdfaW5mbyKjAQoORG93bmxpbmtUeEluZm8SEQoJZnJlcXVlbmN5GAEg",
+            "ASgNEg0KBXBvd2VyGAIgASgFEiIKCm1vZHVsYXRpb24YAyABKAsyDi5ndy5N",
+            "b2R1bGF0aW9uEg0KBWJvYXJkGAQgASgNEg8KB2FudGVubmEYBSABKA0SGgoG",
+            "dGltaW5nGAYgASgLMgouZ3cuVGltaW5nEg8KB2NvbnRleHQYByABKAwimwEK",
+            "BlRpbWluZxIwCgtpbW1lZGlhdGVseRgBIAEoCzIZLmd3LkltbWVkaWF0ZWx5",
+            "VGltaW5nSW5mb0gAEiQKBWRlbGF5GAIgASgLMhMuZ3cuRGVsYXlUaW1pbmdJ",
+            "bmZvSAASKwoJZ3BzX2Vwb2NoGAMgASgLMhYuZ3cuR1BTRXBvY2hUaW1pbmdJ",
+            "bmZvSABCDAoKcGFyYW1ldGVycyIXChVJbW1lZGlhdGVseVRpbWluZ0luZm8i",
+            "OwoPRGVsYXlUaW1pbmdJbmZvEigKBWRlbGF5GAEgASgLMhkuZ29vZ2xlLnBy",
+            "b3RvYnVmLkR1cmF0aW9uIk0KEkdQU0Vwb2NoVGltaW5nSW5mbxI3ChR0aW1l",
+            "X3NpbmNlX2dwc19lcG9jaBgBIAEoCzIZLmdvb2dsZS5wcm90b2J1Zi5EdXJh",
+            "dGlvbiLIAQoLVXBsaW5rRnJhbWUSEwoLcGh5X3BheWxvYWQYASABKAwSLgoO",
+            "dHhfaW5mb19sZWdhY3kYAiABKAsyFi5ndy5VcGxpbmtUeEluZm9MZWdhY3kS",
+            "LgoOcnhfaW5mb19sZWdhY3kYAyABKAsyFi5ndy5VcGxpbmtSeEluZm9MZWdh",
+            "Y3kSIQoHdHhfaW5mbxgEIAEoCzIQLmd3LlVwbGlua1R4SW5mbxIhCgdyeF9p",
+            "bmZvGAUgASgLMhAuZ3cuVXBsaW5rUnhJbmZvImsKDlVwbGlua0ZyYW1lU2V0",
+            "EhMKC3BoeV9wYXlsb2FkGAEgASgMEiEKB3R4X2luZm8YAiABKAsyEC5ndy5V",
+            "cGxpbmtUeEluZm8SIQoHcnhfaW5mbxgDIAMoCzIQLmd3LlVwbGlua1J4SW5m",
+            "byKVAQoNRG93bmxpbmtGcmFtZRITCgtkb3dubGlua19pZBgDIAEoDRIaChJk",
+            "b3dubGlua19pZF9sZWdhY3kYBCABKAwSJAoFaXRlbXMYBSADKAsyFS5ndy5E",
+            "b3dubGlua0ZyYW1lSXRlbRIZChFnYXRld2F5X2lkX2xlZ2FjeRgGIAEoDBIS",
+            "CgpnYXRld2F5X2lkGAcgASgJIn8KEURvd25saW5rRnJhbWVJdGVtEhMKC3Bo",
+            "eV9wYXlsb2FkGAEgASgMEjAKDnR4X2luZm9fbGVnYWN5GAIgASgLMhguZ3cu",
+            "RG93bmxpbmtUeEluZm9MZWdhY3kSIwoHdHhfaW5mbxgDIAEoCzISLmd3LkRv",
+            "d25saW5rVHhJbmZvIpUBCg1Eb3dubGlua1R4QWNrEhkKEWdhdGV3YXlfaWRf",
+            "bGVnYWN5GAEgASgMEhIKCmdhdGV3YXlfaWQYBiABKAkSEwoLZG93bmxpbmtf",
+            "aWQYAiABKA0SGgoSZG93bmxpbmtfaWRfbGVnYWN5GAQgASgMEiQKBWl0ZW1z",
+            "GAUgAygLMhUuZ3cuRG93bmxpbmtUeEFja0l0ZW0iNAoRRG93bmxpbmtUeEFj",
+            "a0l0ZW0SHwoGc3RhdHVzGAEgASgOMg8uZ3cuVHhBY2tTdGF0dXMitQEKFEdh",
+            "dGV3YXlDb25maWd1cmF0aW9uEhkKEWdhdGV3YXlfaWRfbGVnYWN5GAEgASgM",
+            "EhIKCmdhdGV3YXlfaWQYBSABKAkSDwoHdmVyc2lvbhgCIAEoCRIqCghjaGFu",
+            "bmVscxgDIAMoCzIYLmd3LkNoYW5uZWxDb25maWd1cmF0aW9uEjEKDnN0YXRz",
+            "X2ludGVydmFsGAQgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uIocC",
+            "ChRDaGFubmVsQ29uZmlndXJhdGlvbhIRCglmcmVxdWVuY3kYASABKA0SLQoR",
+            "bW9kdWxhdGlvbl9sZWdhY3kYAiABKA4yEi5jb21tb24uTW9kdWxhdGlvbhI6",
+            "ChZsb3JhX21vZHVsYXRpb25fY29uZmlnGAMgASgLMhguZ3cuTG9yYU1vZHVs",
+            "YXRpb25Db25maWdIABI4ChVmc2tfbW9kdWxhdGlvbl9jb25maWcYBCABKAsy",
+            "Fy5ndy5Gc2tNb2R1bGF0aW9uQ29uZmlnSAASDQoFYm9hcmQYBSABKA0SEwoL",
+            "ZGVtb2R1bGF0b3IYBiABKA1CEwoRbW9kdWxhdGlvbl9jb25maWciXgoUTG9y",
+            "YU1vZHVsYXRpb25Db25maWcSGAoQYmFuZHdpZHRoX2xlZ2FjeRgBIAEoDRIR",
+            "CgliYW5kd2lkdGgYAyABKA0SGQoRc3ByZWFkaW5nX2ZhY3RvcnMYAiADKA0i",
+            "UwoTRnNrTW9kdWxhdGlvbkNvbmZpZxIYChBiYW5kd2lkdGhfbGVnYWN5GAEg",
+            "ASgNEhEKCWJhbmR3aWR0aBgDIAEoDRIPCgdiaXRyYXRlGAIgASgNIvQBChlH",
+            "YXRld2F5Q29tbWFuZEV4ZWNSZXF1ZXN0EhkKEWdhdGV3YXlfaWRfbGVnYWN5",
+            "GAEgASgMEhIKCmdhdGV3YXlfaWQYBiABKAkSDwoHY29tbWFuZBgCIAEoCRIP",
+            "CgdleGVjX2lkGAcgASgNEg0KBXN0ZGluGAQgASgMEkMKC2Vudmlyb25tZW50",
+            "GAUgAygLMi4uZ3cuR2F0ZXdheUNvbW1hbmRFeGVjUmVxdWVzdC5FbnZpcm9u",
+            "bWVudEVudHJ5GjIKEEVudmlyb25tZW50RW50cnkSCwoDa2V5GAEgASgJEg0K",
+            "BXZhbHVlGAIgASgJOgI4ASKLAQoaR2F0ZXdheUNvbW1hbmRFeGVjUmVzcG9u",
+            "c2USGQoRZ2F0ZXdheV9pZF9sZWdhY3kYASABKAwSEgoKZ2F0ZXdheV9pZBgG",
+            "IAEoCRIPCgdleGVjX2lkGAcgASgNEg4KBnN0ZG91dBgDIAEoDBIOCgZzdGRl",
+            "cnIYBCABKAwSDQoFZXJyb3IYBSABKAkiWQoXUmF3UGFja2V0Rm9yd2FyZGVy",
+            "RXZlbnQSGQoRZ2F0ZXdheV9pZF9sZWdhY3kYASABKAwSEgoKZ2F0ZXdheV9p",
+            "ZBgEIAEoCRIPCgdwYXlsb2FkGAMgASgMIlsKGVJhd1BhY2tldEZvcndhcmRl",
+            "ckNvbW1hbmQSGQoRZ2F0ZXdheV9pZF9sZWdhY3kYASABKAwSEgoKZ2F0ZXdh",
+            "eV9pZBgEIAEoCRIPCgdwYXlsb2FkGAMgASgMIoABCglDb25uU3RhdGUSGQoR",
+            "Z2F0ZXdheV9pZF9sZWdhY3kYASABKAwSEgoKZ2F0ZXdheV9pZBgDIAEoCRIi",
+            "CgVzdGF0ZRgCIAEoDjITLmd3LkNvbm5TdGF0ZS5TdGF0ZSIgCgVTdGF0ZRIL",
+            "CgdPRkZMSU5FEAASCgoGT05MSU5FEAEqtQEKCENvZGVSYXRlEhAKDENSX1VO",
+            "REVGSU5FRBAAEgoKBkNSXzRfNRABEgoKBkNSXzRfNhACEgoKBkNSXzRfNxAD",
+            "EgoKBkNSXzRfOBAEEgoKBkNSXzNfOBAFEgoKBkNSXzJfNhAGEgoKBkNSXzFf",
+            "NBAHEgoKBkNSXzFfNhAIEgoKBkNSXzVfNhAJEg0KCUNSX0xJXzRfNRAKEg0K",
+            "CUNSX0xJXzRfNhALEg0KCUNSX0xJXzRfOBAMKjsKDkRvd25saW5rVGltaW5n",
+            "Eg8KC0lNTUVESUFURUxZEAASCQoFREVMQVkQARINCglHUFNfRVBPQ0gQAio3",
+            "ChFGaW5lVGltZXN0YW1wVHlwZRIICgROT05FEAASDQoJRU5DUllQVEVEEAES",
+            "CQoFUExBSU4QAiowCglDUkNTdGF0dXMSCgoGTk9fQ1JDEAASCwoHQkFEX0NS",
+            "QxABEgoKBkNSQ19PSxACKrwBCgtUeEFja1N0YXR1cxILCgdJR05PUkVEEAAS",
+            "BgoCT0sQARIMCghUT09fTEFURRACEg0KCVRPT19FQVJMWRADEhQKEENPTExJ",
+            "U0lPTl9QQUNLRVQQBBIUChBDT0xMSVNJT05fQkVBQ09OEAUSCwoHVFhfRlJF",
+            "URAGEgwKCFRYX1BPV0VSEAcSEAoMR1BTX1VOTE9DS0VEEAgSDgoKUVVFVUVf",
+            "RlVMTBAJEhIKDklOVEVSTkFMX0VSUk9SEApCagoUaW8uY2hpcnBzdGFjay5h",
+            "cGkuZ3dCDEdhdGV3YXlQcm90b1ABWi1naXRodWIuY29tL2NoaXJwc3RhY2sv",
+            "Y2hpcnBzdGFjay9hcGkvZ28vdjQvZ3eqAhJDaGlycHN0YWNrLkdhdGV3YXli",
+            "BnByb3RvMw=="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Chirpstack.Common.CommonReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.DurationReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Chirpstack.Gateway.CodeRate), typeof(global::Chirpstack.Gateway.DownlinkTiming), typeof(global::Chirpstack.Gateway.FineTimestampType), typeof(global::Chirpstack.Gateway.CRCStatus), typeof(global::Chirpstack.Gateway.TxAckStatus), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.Modulation), global::Chirpstack.Gateway.Modulation.Parser, new[]{ "Lora", "Fsk", "LrFhss" }, new[]{ "Parameters" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.UplinkTxInfoLegacy), global::Chirpstack.Gateway.UplinkTxInfoLegacy.Parser, new[]{ "Frequency", "Modulation", "LoraModulationInfo", "FskModulationInfo", "LrFhssModulationInfo" }, new[]{ "ModulationInfo" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.UplinkTxInfo), global::Chirpstack.Gateway.UplinkTxInfo.Parser, new[]{ "Frequency", "Modulation" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.LoraModulationInfo), global::Chirpstack.Gateway.LoraModulationInfo.Parser, new[]{ "Bandwidth", "SpreadingFactor", "CodeRateLegacy", "CodeRate", "PolarizationInversion" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.FskModulationInfo), global::Chirpstack.Gateway.FskModulationInfo.Parser, new[]{ "FrequencyDeviation", "Datarate" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.LrFhssModulationInfo), global::Chirpstack.Gateway.LrFhssModulationInfo.Parser, new[]{ "OperatingChannelWidth", "CodeRateLegacy", "CodeRate", "GridSteps" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.EncryptedFineTimestamp), global::Chirpstack.Gateway.EncryptedFineTimestamp.Parser, new[]{ "AesKeyIndex", "EncryptedNs", "FpgaId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.PlainFineTimestamp), global::Chirpstack.Gateway.PlainFineTimestamp.Parser, new[]{ "Time" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.GatewayStats), global::Chirpstack.Gateway.GatewayStats.Parser, new[]{ "GatewayIdLegacy", "GatewayId", "Time", "Location", "ConfigVersion", "RxPacketsReceived", "RxPacketsReceivedOk", "TxPacketsReceived", "TxPacketsEmitted", "Metadata", "TxPacketsPerFrequency", "RxPacketsPerFrequency", "TxPacketsPerModulation", "RxPacketsPerModulation", "TxPacketsPerStatus" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, null, null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.PerModulationCount), global::Chirpstack.Gateway.PerModulationCount.Parser, new[]{ "Modulation", "Count" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.UplinkRxInfoLegacy), global::Chirpstack.Gateway.UplinkRxInfoLegacy.Parser, new[]{ "GatewayId", "Time", "TimeSinceGpsEpoch", "Rssi", "LoraSnr", "Channel", "RfChain", "Board", "Antenna", "Location", "FineTimestampType", "EncryptedFineTimestamp", "PlainFineTimestamp", "Context", "UplinkId", "CrcStatus", "Metadata" }, new[]{ "FineTimestamp" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.UplinkRxInfo), global::Chirpstack.Gateway.UplinkRxInfo.Parser, new[]{ "GatewayId", "UplinkId", "Time", "TimeSinceGpsEpoch", "FineTimeSinceGpsEpoch", "Rssi", "Snr", "Channel", "RfChain", "Board", "Antenna", "Location", "Context", "Metadata", "CrcStatus" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.DownlinkTxInfoLegacy), global::Chirpstack.Gateway.DownlinkTxInfoLegacy.Parser, new[]{ "GatewayId", "Frequency", "Power", "Modulation", "LoraModulationInfo", "FskModulationInfo", "Board", "Antenna", "Timing", "ImmediatelyTimingInfo", "DelayTimingInfo", "GpsEpochTimingInfo", "Context" }, new[]{ "ModulationInfo", "TimingInfo" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.DownlinkTxInfo), global::Chirpstack.Gateway.DownlinkTxInfo.Parser, new[]{ "Frequency", "Power", "Modulation", "Board", "Antenna", "Timing", "Context" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.Timing), global::Chirpstack.Gateway.Timing.Parser, new[]{ "Immediately", "Delay", "GpsEpoch" }, new[]{ "Parameters" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.ImmediatelyTimingInfo), global::Chirpstack.Gateway.ImmediatelyTimingInfo.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.DelayTimingInfo), global::Chirpstack.Gateway.DelayTimingInfo.Parser, new[]{ "Delay" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.GPSEpochTimingInfo), global::Chirpstack.Gateway.GPSEpochTimingInfo.Parser, new[]{ "TimeSinceGpsEpoch" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.UplinkFrame), global::Chirpstack.Gateway.UplinkFrame.Parser, new[]{ "PhyPayload", "TxInfoLegacy", "RxInfoLegacy", "TxInfo", "RxInfo" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.UplinkFrameSet), global::Chirpstack.Gateway.UplinkFrameSet.Parser, new[]{ "PhyPayload", "TxInfo", "RxInfo" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.DownlinkFrame), global::Chirpstack.Gateway.DownlinkFrame.Parser, new[]{ "DownlinkId", "DownlinkIdLegacy", "Items", "GatewayIdLegacy", "GatewayId" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.DownlinkFrameItem), global::Chirpstack.Gateway.DownlinkFrameItem.Parser, new[]{ "PhyPayload", "TxInfoLegacy", "TxInfo" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.DownlinkTxAck), global::Chirpstack.Gateway.DownlinkTxAck.Parser, new[]{ "GatewayIdLegacy", "GatewayId", "DownlinkId", "DownlinkIdLegacy", "Items" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.DownlinkTxAckItem), global::Chirpstack.Gateway.DownlinkTxAckItem.Parser, new[]{ "Status" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.GatewayConfiguration), global::Chirpstack.Gateway.GatewayConfiguration.Parser, new[]{ "GatewayIdLegacy", "GatewayId", "Version", "Channels", "StatsInterval" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.ChannelConfiguration), global::Chirpstack.Gateway.ChannelConfiguration.Parser, new[]{ "Frequency", "ModulationLegacy", "LoraModulationConfig", "FskModulationConfig", "Board", "Demodulator" }, new[]{ "ModulationConfig" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.LoraModulationConfig), global::Chirpstack.Gateway.LoraModulationConfig.Parser, new[]{ "BandwidthLegacy", "Bandwidth", "SpreadingFactors" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.FskModulationConfig), global::Chirpstack.Gateway.FskModulationConfig.Parser, new[]{ "BandwidthLegacy", "Bandwidth", "Bitrate" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.GatewayCommandExecRequest), global::Chirpstack.Gateway.GatewayCommandExecRequest.Parser, new[]{ "GatewayIdLegacy", "GatewayId", "Command", "ExecId", "Stdin", "Environment" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.GatewayCommandExecResponse), global::Chirpstack.Gateway.GatewayCommandExecResponse.Parser, new[]{ "GatewayIdLegacy", "GatewayId", "ExecId", "Stdout", "Stderr", "Error" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.RawPacketForwarderEvent), global::Chirpstack.Gateway.RawPacketForwarderEvent.Parser, new[]{ "GatewayIdLegacy", "GatewayId", "Payload" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.RawPacketForwarderCommand), global::Chirpstack.Gateway.RawPacketForwarderCommand.Parser, new[]{ "GatewayIdLegacy", "GatewayId", "Payload" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Gateway.ConnState), global::Chirpstack.Gateway.ConnState.Parser, new[]{ "GatewayIdLegacy", "GatewayId", "State" }, null, new[]{ typeof(global::Chirpstack.Gateway.ConnState.Types.State) }, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Enums
+  public enum CodeRate {
+    [pbr::OriginalName("CR_UNDEFINED")] CrUndefined = 0,
+    /// <summary>
+    /// LoRa
+    /// </summary>
+    [pbr::OriginalName("CR_4_5")] Cr45 = 1,
+    [pbr::OriginalName("CR_4_6")] Cr46 = 2,
+    [pbr::OriginalName("CR_4_7")] Cr47 = 3,
+    [pbr::OriginalName("CR_4_8")] Cr48 = 4,
+    /// <summary>
+    /// LR-FHSS
+    /// </summary>
+    [pbr::OriginalName("CR_3_8")] Cr38 = 5,
+    [pbr::OriginalName("CR_2_6")] Cr26 = 6,
+    [pbr::OriginalName("CR_1_4")] Cr14 = 7,
+    [pbr::OriginalName("CR_1_6")] Cr16 = 8,
+    [pbr::OriginalName("CR_5_6")] Cr56 = 9,
+    /// <summary>
+    /// LoRa 2.4 gHz
+    /// </summary>
+    [pbr::OriginalName("CR_LI_4_5")] CrLi45 = 10,
+    [pbr::OriginalName("CR_LI_4_6")] CrLi46 = 11,
+    [pbr::OriginalName("CR_LI_4_8")] CrLi48 = 12,
+  }
+
+  public enum DownlinkTiming {
+    /// <summary>
+    /// Send the downlink immediately.
+    /// </summary>
+    [pbr::OriginalName("IMMEDIATELY")] Immediately = 0,
+    /// <summary>
+    /// Send downlink at the given delay (based on provided context).
+    /// </summary>
+    [pbr::OriginalName("DELAY")] Delay = 1,
+    /// <summary>
+    /// Send at given GPS epoch value.
+    /// </summary>
+    [pbr::OriginalName("GPS_EPOCH")] GpsEpoch = 2,
+  }
+
+  public enum FineTimestampType {
+    /// <summary>
+    /// No fine-timestamp available.
+    /// </summary>
+    [pbr::OriginalName("NONE")] None = 0,
+    /// <summary>
+    /// Encrypted fine-timestamp.
+    /// </summary>
+    [pbr::OriginalName("ENCRYPTED")] Encrypted = 1,
+    /// <summary>
+    /// Plain fine-timestamp.
+    /// </summary>
+    [pbr::OriginalName("PLAIN")] Plain = 2,
+  }
+
+  public enum CRCStatus {
+    /// <summary>
+    /// No CRC.
+    /// </summary>
+    [pbr::OriginalName("NO_CRC")] NoCrc = 0,
+    /// <summary>
+    /// Bad CRC.
+    /// </summary>
+    [pbr::OriginalName("BAD_CRC")] BadCrc = 1,
+    /// <summary>
+    /// CRC OK.
+    /// </summary>
+    [pbr::OriginalName("CRC_OK")] CrcOk = 2,
+  }
+
+  public enum TxAckStatus {
+    /// <summary>
+    /// Ignored (when a previous item was already emitted).
+    /// </summary>
+    [pbr::OriginalName("IGNORED")] Ignored = 0,
+    /// <summary>
+    /// Packet has been programmed for downlink.
+    /// </summary>
+    [pbr::OriginalName("OK")] Ok = 1,
+    /// <summary>
+    /// Rejected because it was already too late to program this packet for downlink.
+    /// </summary>
+    [pbr::OriginalName("TOO_LATE")] TooLate = 2,
+    /// <summary>
+    /// Rejected because downlink packet timestamp is too much in advance.
+    /// </summary>
+    [pbr::OriginalName("TOO_EARLY")] TooEarly = 3,
+    /// <summary>
+    /// Rejected because there was already a packet programmed in requested timeframe.
+    /// </summary>
+    [pbr::OriginalName("COLLISION_PACKET")] CollisionPacket = 4,
+    /// <summary>
+    /// Rejected because there was already a beacon planned in requested timeframe.
+    /// </summary>
+    [pbr::OriginalName("COLLISION_BEACON")] CollisionBeacon = 5,
+    /// <summary>
+    /// Rejected because requested frequency is not supported by TX RF chain.
+    /// </summary>
+    [pbr::OriginalName("TX_FREQ")] TxFreq = 6,
+    /// <summary>
+    /// Rejected because requested power is not supported by gateway.
+    /// </summary>
+    [pbr::OriginalName("TX_POWER")] TxPower = 7,
+    /// <summary>
+    /// Rejected because GPS is unlocked, so GPS timestamp cannot be used.
+    /// </summary>
+    [pbr::OriginalName("GPS_UNLOCKED")] GpsUnlocked = 8,
+    /// <summary>
+    /// Downlink queue is full.
+    /// </summary>
+    [pbr::OriginalName("QUEUE_FULL")] QueueFull = 9,
+    /// <summary>
+    /// Internal error.
+    /// </summary>
+    [pbr::OriginalName("INTERNAL_ERROR")] InternalError = 10,
+  }
+
+  #endregion
+
+  #region Messages
+  public sealed partial class Modulation : pb::IMessage<Modulation>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<Modulation> _parser = new pb::MessageParser<Modulation>(() => new Modulation());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<Modulation> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Modulation() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Modulation(Modulation other) : this() {
+      switch (other.ParametersCase) {
+        case ParametersOneofCase.Lora:
+          Lora = other.Lora.Clone();
+          break;
+        case ParametersOneofCase.Fsk:
+          Fsk = other.Fsk.Clone();
+          break;
+        case ParametersOneofCase.LrFhss:
+          LrFhss = other.LrFhss.Clone();
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Modulation Clone() {
+      return new Modulation(this);
+    }
+
+    /// <summary>Field number for the "lora" field.</summary>
+    public const int LoraFieldNumber = 3;
+    /// <summary>
+    /// LoRa modulation information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.LoraModulationInfo Lora {
+      get { return parametersCase_ == ParametersOneofCase.Lora ? (global::Chirpstack.Gateway.LoraModulationInfo) parameters_ : null; }
+      set {
+        parameters_ = value;
+        parametersCase_ = value == null ? ParametersOneofCase.None : ParametersOneofCase.Lora;
+      }
+    }
+
+    /// <summary>Field number for the "fsk" field.</summary>
+    public const int FskFieldNumber = 4;
+    /// <summary>
+    /// FSK modulation information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.FskModulationInfo Fsk {
+      get { return parametersCase_ == ParametersOneofCase.Fsk ? (global::Chirpstack.Gateway.FskModulationInfo) parameters_ : null; }
+      set {
+        parameters_ = value;
+        parametersCase_ = value == null ? ParametersOneofCase.None : ParametersOneofCase.Fsk;
+      }
+    }
+
+    /// <summary>Field number for the "lr_fhss" field.</summary>
+    public const int LrFhssFieldNumber = 5;
+    /// <summary>
+    /// LR-FHSS modulation information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.LrFhssModulationInfo LrFhss {
+      get { return parametersCase_ == ParametersOneofCase.LrFhss ? (global::Chirpstack.Gateway.LrFhssModulationInfo) parameters_ : null; }
+      set {
+        parameters_ = value;
+        parametersCase_ = value == null ? ParametersOneofCase.None : ParametersOneofCase.LrFhss;
+      }
+    }
+
+    private object parameters_;
+    /// <summary>Enum of possible cases for the "parameters" oneof.</summary>
+    public enum ParametersOneofCase {
+      None = 0,
+      Lora = 3,
+      Fsk = 4,
+      LrFhss = 5,
+    }
+    private ParametersOneofCase parametersCase_ = ParametersOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ParametersOneofCase ParametersCase {
+      get { return parametersCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearParameters() {
+      parametersCase_ = ParametersOneofCase.None;
+      parameters_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as Modulation);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(Modulation other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Lora, other.Lora)) return false;
+      if (!object.Equals(Fsk, other.Fsk)) return false;
+      if (!object.Equals(LrFhss, other.LrFhss)) return false;
+      if (ParametersCase != other.ParametersCase) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (parametersCase_ == ParametersOneofCase.Lora) hash ^= Lora.GetHashCode();
+      if (parametersCase_ == ParametersOneofCase.Fsk) hash ^= Fsk.GetHashCode();
+      if (parametersCase_ == ParametersOneofCase.LrFhss) hash ^= LrFhss.GetHashCode();
+      hash ^= (int) parametersCase_;
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (parametersCase_ == ParametersOneofCase.Lora) {
+        output.WriteRawTag(26);
+        output.WriteMessage(Lora);
+      }
+      if (parametersCase_ == ParametersOneofCase.Fsk) {
+        output.WriteRawTag(34);
+        output.WriteMessage(Fsk);
+      }
+      if (parametersCase_ == ParametersOneofCase.LrFhss) {
+        output.WriteRawTag(42);
+        output.WriteMessage(LrFhss);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (parametersCase_ == ParametersOneofCase.Lora) {
+        output.WriteRawTag(26);
+        output.WriteMessage(Lora);
+      }
+      if (parametersCase_ == ParametersOneofCase.Fsk) {
+        output.WriteRawTag(34);
+        output.WriteMessage(Fsk);
+      }
+      if (parametersCase_ == ParametersOneofCase.LrFhss) {
+        output.WriteRawTag(42);
+        output.WriteMessage(LrFhss);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (parametersCase_ == ParametersOneofCase.Lora) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Lora);
+      }
+      if (parametersCase_ == ParametersOneofCase.Fsk) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Fsk);
+      }
+      if (parametersCase_ == ParametersOneofCase.LrFhss) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LrFhss);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(Modulation other) {
+      if (other == null) {
+        return;
+      }
+      switch (other.ParametersCase) {
+        case ParametersOneofCase.Lora:
+          if (Lora == null) {
+            Lora = new global::Chirpstack.Gateway.LoraModulationInfo();
+          }
+          Lora.MergeFrom(other.Lora);
+          break;
+        case ParametersOneofCase.Fsk:
+          if (Fsk == null) {
+            Fsk = new global::Chirpstack.Gateway.FskModulationInfo();
+          }
+          Fsk.MergeFrom(other.Fsk);
+          break;
+        case ParametersOneofCase.LrFhss:
+          if (LrFhss == null) {
+            LrFhss = new global::Chirpstack.Gateway.LrFhssModulationInfo();
+          }
+          LrFhss.MergeFrom(other.LrFhss);
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 26: {
+            global::Chirpstack.Gateway.LoraModulationInfo subBuilder = new global::Chirpstack.Gateway.LoraModulationInfo();
+            if (parametersCase_ == ParametersOneofCase.Lora) {
+              subBuilder.MergeFrom(Lora);
+            }
+            input.ReadMessage(subBuilder);
+            Lora = subBuilder;
+            break;
+          }
+          case 34: {
+            global::Chirpstack.Gateway.FskModulationInfo subBuilder = new global::Chirpstack.Gateway.FskModulationInfo();
+            if (parametersCase_ == ParametersOneofCase.Fsk) {
+              subBuilder.MergeFrom(Fsk);
+            }
+            input.ReadMessage(subBuilder);
+            Fsk = subBuilder;
+            break;
+          }
+          case 42: {
+            global::Chirpstack.Gateway.LrFhssModulationInfo subBuilder = new global::Chirpstack.Gateway.LrFhssModulationInfo();
+            if (parametersCase_ == ParametersOneofCase.LrFhss) {
+              subBuilder.MergeFrom(LrFhss);
+            }
+            input.ReadMessage(subBuilder);
+            LrFhss = subBuilder;
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 26: {
+            global::Chirpstack.Gateway.LoraModulationInfo subBuilder = new global::Chirpstack.Gateway.LoraModulationInfo();
+            if (parametersCase_ == ParametersOneofCase.Lora) {
+              subBuilder.MergeFrom(Lora);
+            }
+            input.ReadMessage(subBuilder);
+            Lora = subBuilder;
+            break;
+          }
+          case 34: {
+            global::Chirpstack.Gateway.FskModulationInfo subBuilder = new global::Chirpstack.Gateway.FskModulationInfo();
+            if (parametersCase_ == ParametersOneofCase.Fsk) {
+              subBuilder.MergeFrom(Fsk);
+            }
+            input.ReadMessage(subBuilder);
+            Fsk = subBuilder;
+            break;
+          }
+          case 42: {
+            global::Chirpstack.Gateway.LrFhssModulationInfo subBuilder = new global::Chirpstack.Gateway.LrFhssModulationInfo();
+            if (parametersCase_ == ParametersOneofCase.LrFhss) {
+              subBuilder.MergeFrom(LrFhss);
+            }
+            input.ReadMessage(subBuilder);
+            LrFhss = subBuilder;
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UplinkTxInfoLegacy : pb::IMessage<UplinkTxInfoLegacy>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UplinkTxInfoLegacy> _parser = new pb::MessageParser<UplinkTxInfoLegacy>(() => new UplinkTxInfoLegacy());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UplinkTxInfoLegacy> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkTxInfoLegacy() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkTxInfoLegacy(UplinkTxInfoLegacy other) : this() {
+      frequency_ = other.frequency_;
+      modulation_ = other.modulation_;
+      switch (other.ModulationInfoCase) {
+        case ModulationInfoOneofCase.LoraModulationInfo:
+          LoraModulationInfo = other.LoraModulationInfo.Clone();
+          break;
+        case ModulationInfoOneofCase.FskModulationInfo:
+          FskModulationInfo = other.FskModulationInfo.Clone();
+          break;
+        case ModulationInfoOneofCase.LrFhssModulationInfo:
+          LrFhssModulationInfo = other.LrFhssModulationInfo.Clone();
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkTxInfoLegacy Clone() {
+      return new UplinkTxInfoLegacy(this);
+    }
+
+    /// <summary>Field number for the "frequency" field.</summary>
+    public const int FrequencyFieldNumber = 1;
+    private uint frequency_;
+    /// <summary>
+    /// Frequency (Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Frequency {
+      get { return frequency_; }
+      set {
+        frequency_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "modulation" field.</summary>
+    public const int ModulationFieldNumber = 2;
+    private global::Chirpstack.Common.Modulation modulation_ = global::Chirpstack.Common.Modulation.Lora;
+    /// <summary>
+    /// Modulation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Modulation Modulation {
+      get { return modulation_; }
+      set {
+        modulation_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "lora_modulation_info" field.</summary>
+    public const int LoraModulationInfoFieldNumber = 3;
+    /// <summary>
+    /// LoRa modulation information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.LoraModulationInfo LoraModulationInfo {
+      get { return modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo ? (global::Chirpstack.Gateway.LoraModulationInfo) modulationInfo_ : null; }
+      set {
+        modulationInfo_ = value;
+        modulationInfoCase_ = value == null ? ModulationInfoOneofCase.None : ModulationInfoOneofCase.LoraModulationInfo;
+      }
+    }
+
+    /// <summary>Field number for the "fsk_modulation_info" field.</summary>
+    public const int FskModulationInfoFieldNumber = 4;
+    /// <summary>
+    /// FSK modulation information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.FskModulationInfo FskModulationInfo {
+      get { return modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo ? (global::Chirpstack.Gateway.FskModulationInfo) modulationInfo_ : null; }
+      set {
+        modulationInfo_ = value;
+        modulationInfoCase_ = value == null ? ModulationInfoOneofCase.None : ModulationInfoOneofCase.FskModulationInfo;
+      }
+    }
+
+    /// <summary>Field number for the "lr_fhss_modulation_info" field.</summary>
+    public const int LrFhssModulationInfoFieldNumber = 5;
+    /// <summary>
+    /// LR-FHSS modulation information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.LrFhssModulationInfo LrFhssModulationInfo {
+      get { return modulationInfoCase_ == ModulationInfoOneofCase.LrFhssModulationInfo ? (global::Chirpstack.Gateway.LrFhssModulationInfo) modulationInfo_ : null; }
+      set {
+        modulationInfo_ = value;
+        modulationInfoCase_ = value == null ? ModulationInfoOneofCase.None : ModulationInfoOneofCase.LrFhssModulationInfo;
+      }
+    }
+
+    private object modulationInfo_;
+    /// <summary>Enum of possible cases for the "modulation_info" oneof.</summary>
+    public enum ModulationInfoOneofCase {
+      None = 0,
+      LoraModulationInfo = 3,
+      FskModulationInfo = 4,
+      LrFhssModulationInfo = 5,
+    }
+    private ModulationInfoOneofCase modulationInfoCase_ = ModulationInfoOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ModulationInfoOneofCase ModulationInfoCase {
+      get { return modulationInfoCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearModulationInfo() {
+      modulationInfoCase_ = ModulationInfoOneofCase.None;
+      modulationInfo_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UplinkTxInfoLegacy);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UplinkTxInfoLegacy other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Frequency != other.Frequency) return false;
+      if (Modulation != other.Modulation) return false;
+      if (!object.Equals(LoraModulationInfo, other.LoraModulationInfo)) return false;
+      if (!object.Equals(FskModulationInfo, other.FskModulationInfo)) return false;
+      if (!object.Equals(LrFhssModulationInfo, other.LrFhssModulationInfo)) return false;
+      if (ModulationInfoCase != other.ModulationInfoCase) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Frequency != 0) hash ^= Frequency.GetHashCode();
+      if (Modulation != global::Chirpstack.Common.Modulation.Lora) hash ^= Modulation.GetHashCode();
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) hash ^= LoraModulationInfo.GetHashCode();
+      if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) hash ^= FskModulationInfo.GetHashCode();
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LrFhssModulationInfo) hash ^= LrFhssModulationInfo.GetHashCode();
+      hash ^= (int) modulationInfoCase_;
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Frequency != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Frequency);
+      }
+      if (Modulation != global::Chirpstack.Common.Modulation.Lora) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Modulation);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) {
+        output.WriteRawTag(26);
+        output.WriteMessage(LoraModulationInfo);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) {
+        output.WriteRawTag(34);
+        output.WriteMessage(FskModulationInfo);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LrFhssModulationInfo) {
+        output.WriteRawTag(42);
+        output.WriteMessage(LrFhssModulationInfo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Frequency != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Frequency);
+      }
+      if (Modulation != global::Chirpstack.Common.Modulation.Lora) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) Modulation);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) {
+        output.WriteRawTag(26);
+        output.WriteMessage(LoraModulationInfo);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) {
+        output.WriteRawTag(34);
+        output.WriteMessage(FskModulationInfo);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LrFhssModulationInfo) {
+        output.WriteRawTag(42);
+        output.WriteMessage(LrFhssModulationInfo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Frequency != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Frequency);
+      }
+      if (Modulation != global::Chirpstack.Common.Modulation.Lora) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Modulation);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LoraModulationInfo);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(FskModulationInfo);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LrFhssModulationInfo) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LrFhssModulationInfo);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UplinkTxInfoLegacy other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Frequency != 0) {
+        Frequency = other.Frequency;
+      }
+      if (other.Modulation != global::Chirpstack.Common.Modulation.Lora) {
+        Modulation = other.Modulation;
+      }
+      switch (other.ModulationInfoCase) {
+        case ModulationInfoOneofCase.LoraModulationInfo:
+          if (LoraModulationInfo == null) {
+            LoraModulationInfo = new global::Chirpstack.Gateway.LoraModulationInfo();
+          }
+          LoraModulationInfo.MergeFrom(other.LoraModulationInfo);
+          break;
+        case ModulationInfoOneofCase.FskModulationInfo:
+          if (FskModulationInfo == null) {
+            FskModulationInfo = new global::Chirpstack.Gateway.FskModulationInfo();
+          }
+          FskModulationInfo.MergeFrom(other.FskModulationInfo);
+          break;
+        case ModulationInfoOneofCase.LrFhssModulationInfo:
+          if (LrFhssModulationInfo == null) {
+            LrFhssModulationInfo = new global::Chirpstack.Gateway.LrFhssModulationInfo();
+          }
+          LrFhssModulationInfo.MergeFrom(other.LrFhssModulationInfo);
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Modulation = (global::Chirpstack.Common.Modulation) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            global::Chirpstack.Gateway.LoraModulationInfo subBuilder = new global::Chirpstack.Gateway.LoraModulationInfo();
+            if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) {
+              subBuilder.MergeFrom(LoraModulationInfo);
+            }
+            input.ReadMessage(subBuilder);
+            LoraModulationInfo = subBuilder;
+            break;
+          }
+          case 34: {
+            global::Chirpstack.Gateway.FskModulationInfo subBuilder = new global::Chirpstack.Gateway.FskModulationInfo();
+            if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) {
+              subBuilder.MergeFrom(FskModulationInfo);
+            }
+            input.ReadMessage(subBuilder);
+            FskModulationInfo = subBuilder;
+            break;
+          }
+          case 42: {
+            global::Chirpstack.Gateway.LrFhssModulationInfo subBuilder = new global::Chirpstack.Gateway.LrFhssModulationInfo();
+            if (modulationInfoCase_ == ModulationInfoOneofCase.LrFhssModulationInfo) {
+              subBuilder.MergeFrom(LrFhssModulationInfo);
+            }
+            input.ReadMessage(subBuilder);
+            LrFhssModulationInfo = subBuilder;
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Modulation = (global::Chirpstack.Common.Modulation) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            global::Chirpstack.Gateway.LoraModulationInfo subBuilder = new global::Chirpstack.Gateway.LoraModulationInfo();
+            if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) {
+              subBuilder.MergeFrom(LoraModulationInfo);
+            }
+            input.ReadMessage(subBuilder);
+            LoraModulationInfo = subBuilder;
+            break;
+          }
+          case 34: {
+            global::Chirpstack.Gateway.FskModulationInfo subBuilder = new global::Chirpstack.Gateway.FskModulationInfo();
+            if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) {
+              subBuilder.MergeFrom(FskModulationInfo);
+            }
+            input.ReadMessage(subBuilder);
+            FskModulationInfo = subBuilder;
+            break;
+          }
+          case 42: {
+            global::Chirpstack.Gateway.LrFhssModulationInfo subBuilder = new global::Chirpstack.Gateway.LrFhssModulationInfo();
+            if (modulationInfoCase_ == ModulationInfoOneofCase.LrFhssModulationInfo) {
+              subBuilder.MergeFrom(LrFhssModulationInfo);
+            }
+            input.ReadMessage(subBuilder);
+            LrFhssModulationInfo = subBuilder;
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UplinkTxInfo : pb::IMessage<UplinkTxInfo>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UplinkTxInfo> _parser = new pb::MessageParser<UplinkTxInfo>(() => new UplinkTxInfo());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UplinkTxInfo> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkTxInfo() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkTxInfo(UplinkTxInfo other) : this() {
+      frequency_ = other.frequency_;
+      modulation_ = other.modulation_ != null ? other.modulation_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkTxInfo Clone() {
+      return new UplinkTxInfo(this);
+    }
+
+    /// <summary>Field number for the "frequency" field.</summary>
+    public const int FrequencyFieldNumber = 1;
+    private uint frequency_;
+    /// <summary>
+    /// Frequency (Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Frequency {
+      get { return frequency_; }
+      set {
+        frequency_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "modulation" field.</summary>
+    public const int ModulationFieldNumber = 2;
+    private global::Chirpstack.Gateway.Modulation modulation_;
+    /// <summary>
+    /// Modulation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.Modulation Modulation {
+      get { return modulation_; }
+      set {
+        modulation_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UplinkTxInfo);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UplinkTxInfo other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Frequency != other.Frequency) return false;
+      if (!object.Equals(Modulation, other.Modulation)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Frequency != 0) hash ^= Frequency.GetHashCode();
+      if (modulation_ != null) hash ^= Modulation.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Frequency != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Frequency);
+      }
+      if (modulation_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Modulation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Frequency != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Frequency);
+      }
+      if (modulation_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Modulation);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Frequency != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Frequency);
+      }
+      if (modulation_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Modulation);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UplinkTxInfo other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Frequency != 0) {
+        Frequency = other.Frequency;
+      }
+      if (other.modulation_ != null) {
+        if (modulation_ == null) {
+          Modulation = new global::Chirpstack.Gateway.Modulation();
+        }
+        Modulation.MergeFrom(other.Modulation);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            if (modulation_ == null) {
+              Modulation = new global::Chirpstack.Gateway.Modulation();
+            }
+            input.ReadMessage(Modulation);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            if (modulation_ == null) {
+              Modulation = new global::Chirpstack.Gateway.Modulation();
+            }
+            input.ReadMessage(Modulation);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class LoraModulationInfo : pb::IMessage<LoraModulationInfo>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<LoraModulationInfo> _parser = new pb::MessageParser<LoraModulationInfo>(() => new LoraModulationInfo());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<LoraModulationInfo> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraModulationInfo() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraModulationInfo(LoraModulationInfo other) : this() {
+      bandwidth_ = other.bandwidth_;
+      spreadingFactor_ = other.spreadingFactor_;
+      codeRateLegacy_ = other.codeRateLegacy_;
+      codeRate_ = other.codeRate_;
+      polarizationInversion_ = other.polarizationInversion_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraModulationInfo Clone() {
+      return new LoraModulationInfo(this);
+    }
+
+    /// <summary>Field number for the "bandwidth" field.</summary>
+    public const int BandwidthFieldNumber = 1;
+    private uint bandwidth_;
+    /// <summary>
+    /// Bandwidth.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Bandwidth {
+      get { return bandwidth_; }
+      set {
+        bandwidth_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "spreading_factor" field.</summary>
+    public const int SpreadingFactorFieldNumber = 2;
+    private uint spreadingFactor_;
+    /// <summary>
+    /// Speading-factor.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint SpreadingFactor {
+      get { return spreadingFactor_; }
+      set {
+        spreadingFactor_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "code_rate_legacy" field.</summary>
+    public const int CodeRateLegacyFieldNumber = 3;
+    private string codeRateLegacy_ = "";
+    /// <summary>
+    /// Code-rate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string CodeRateLegacy {
+      get { return codeRateLegacy_; }
+      set {
+        codeRateLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "code_rate" field.</summary>
+    public const int CodeRateFieldNumber = 5;
+    private global::Chirpstack.Gateway.CodeRate codeRate_ = global::Chirpstack.Gateway.CodeRate.CrUndefined;
+    /// <summary>
+    /// Code-rate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.CodeRate CodeRate {
+      get { return codeRate_; }
+      set {
+        codeRate_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "polarization_inversion" field.</summary>
+    public const int PolarizationInversionFieldNumber = 4;
+    private bool polarizationInversion_;
+    /// <summary>
+    /// Polarization inversion.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool PolarizationInversion {
+      get { return polarizationInversion_; }
+      set {
+        polarizationInversion_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as LoraModulationInfo);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(LoraModulationInfo other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Bandwidth != other.Bandwidth) return false;
+      if (SpreadingFactor != other.SpreadingFactor) return false;
+      if (CodeRateLegacy != other.CodeRateLegacy) return false;
+      if (CodeRate != other.CodeRate) return false;
+      if (PolarizationInversion != other.PolarizationInversion) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Bandwidth != 0) hash ^= Bandwidth.GetHashCode();
+      if (SpreadingFactor != 0) hash ^= SpreadingFactor.GetHashCode();
+      if (CodeRateLegacy.Length != 0) hash ^= CodeRateLegacy.GetHashCode();
+      if (CodeRate != global::Chirpstack.Gateway.CodeRate.CrUndefined) hash ^= CodeRate.GetHashCode();
+      if (PolarizationInversion != false) hash ^= PolarizationInversion.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Bandwidth != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Bandwidth);
+      }
+      if (SpreadingFactor != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(SpreadingFactor);
+      }
+      if (CodeRateLegacy.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(CodeRateLegacy);
+      }
+      if (PolarizationInversion != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(PolarizationInversion);
+      }
+      if (CodeRate != global::Chirpstack.Gateway.CodeRate.CrUndefined) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) CodeRate);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Bandwidth != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Bandwidth);
+      }
+      if (SpreadingFactor != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(SpreadingFactor);
+      }
+      if (CodeRateLegacy.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(CodeRateLegacy);
+      }
+      if (PolarizationInversion != false) {
+        output.WriteRawTag(32);
+        output.WriteBool(PolarizationInversion);
+      }
+      if (CodeRate != global::Chirpstack.Gateway.CodeRate.CrUndefined) {
+        output.WriteRawTag(40);
+        output.WriteEnum((int) CodeRate);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Bandwidth != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Bandwidth);
+      }
+      if (SpreadingFactor != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(SpreadingFactor);
+      }
+      if (CodeRateLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(CodeRateLegacy);
+      }
+      if (CodeRate != global::Chirpstack.Gateway.CodeRate.CrUndefined) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) CodeRate);
+      }
+      if (PolarizationInversion != false) {
+        size += 1 + 1;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(LoraModulationInfo other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Bandwidth != 0) {
+        Bandwidth = other.Bandwidth;
+      }
+      if (other.SpreadingFactor != 0) {
+        SpreadingFactor = other.SpreadingFactor;
+      }
+      if (other.CodeRateLegacy.Length != 0) {
+        CodeRateLegacy = other.CodeRateLegacy;
+      }
+      if (other.CodeRate != global::Chirpstack.Gateway.CodeRate.CrUndefined) {
+        CodeRate = other.CodeRate;
+      }
+      if (other.PolarizationInversion != false) {
+        PolarizationInversion = other.PolarizationInversion;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Bandwidth = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            SpreadingFactor = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            CodeRateLegacy = input.ReadString();
+            break;
+          }
+          case 32: {
+            PolarizationInversion = input.ReadBool();
+            break;
+          }
+          case 40: {
+            CodeRate = (global::Chirpstack.Gateway.CodeRate) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Bandwidth = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            SpreadingFactor = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            CodeRateLegacy = input.ReadString();
+            break;
+          }
+          case 32: {
+            PolarizationInversion = input.ReadBool();
+            break;
+          }
+          case 40: {
+            CodeRate = (global::Chirpstack.Gateway.CodeRate) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class FskModulationInfo : pb::IMessage<FskModulationInfo>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<FskModulationInfo> _parser = new pb::MessageParser<FskModulationInfo>(() => new FskModulationInfo());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<FskModulationInfo> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FskModulationInfo() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FskModulationInfo(FskModulationInfo other) : this() {
+      frequencyDeviation_ = other.frequencyDeviation_;
+      datarate_ = other.datarate_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FskModulationInfo Clone() {
+      return new FskModulationInfo(this);
+    }
+
+    /// <summary>Field number for the "frequency_deviation" field.</summary>
+    public const int FrequencyDeviationFieldNumber = 1;
+    private uint frequencyDeviation_;
+    /// <summary>
+    /// Frequency deviation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FrequencyDeviation {
+      get { return frequencyDeviation_; }
+      set {
+        frequencyDeviation_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "datarate" field.</summary>
+    public const int DatarateFieldNumber = 2;
+    private uint datarate_;
+    /// <summary>
+    /// FSK datarate (bits / sec).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Datarate {
+      get { return datarate_; }
+      set {
+        datarate_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as FskModulationInfo);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(FskModulationInfo other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (FrequencyDeviation != other.FrequencyDeviation) return false;
+      if (Datarate != other.Datarate) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (FrequencyDeviation != 0) hash ^= FrequencyDeviation.GetHashCode();
+      if (Datarate != 0) hash ^= Datarate.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (FrequencyDeviation != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(FrequencyDeviation);
+      }
+      if (Datarate != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Datarate);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (FrequencyDeviation != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(FrequencyDeviation);
+      }
+      if (Datarate != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Datarate);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (FrequencyDeviation != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FrequencyDeviation);
+      }
+      if (Datarate != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Datarate);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(FskModulationInfo other) {
+      if (other == null) {
+        return;
+      }
+      if (other.FrequencyDeviation != 0) {
+        FrequencyDeviation = other.FrequencyDeviation;
+      }
+      if (other.Datarate != 0) {
+        Datarate = other.Datarate;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            FrequencyDeviation = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Datarate = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            FrequencyDeviation = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Datarate = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class LrFhssModulationInfo : pb::IMessage<LrFhssModulationInfo>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<LrFhssModulationInfo> _parser = new pb::MessageParser<LrFhssModulationInfo>(() => new LrFhssModulationInfo());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<LrFhssModulationInfo> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LrFhssModulationInfo() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LrFhssModulationInfo(LrFhssModulationInfo other) : this() {
+      operatingChannelWidth_ = other.operatingChannelWidth_;
+      codeRateLegacy_ = other.codeRateLegacy_;
+      codeRate_ = other.codeRate_;
+      gridSteps_ = other.gridSteps_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LrFhssModulationInfo Clone() {
+      return new LrFhssModulationInfo(this);
+    }
+
+    /// <summary>Field number for the "operating_channel_width" field.</summary>
+    public const int OperatingChannelWidthFieldNumber = 1;
+    private uint operatingChannelWidth_;
+    /// <summary>
+    /// Operating channel width (OCW) in Hz.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint OperatingChannelWidth {
+      get { return operatingChannelWidth_; }
+      set {
+        operatingChannelWidth_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "code_rate_legacy" field.</summary>
+    public const int CodeRateLegacyFieldNumber = 2;
+    private string codeRateLegacy_ = "";
+    /// <summary>
+    /// Code-rate.
+    /// Deprecated: use code_rate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string CodeRateLegacy {
+      get { return codeRateLegacy_; }
+      set {
+        codeRateLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "code_rate" field.</summary>
+    public const int CodeRateFieldNumber = 4;
+    private global::Chirpstack.Gateway.CodeRate codeRate_ = global::Chirpstack.Gateway.CodeRate.CrUndefined;
+    /// <summary>
+    /// Code-rate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.CodeRate CodeRate {
+      get { return codeRate_; }
+      set {
+        codeRate_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "grid_steps" field.</summary>
+    public const int GridStepsFieldNumber = 3;
+    private uint gridSteps_;
+    /// <summary>
+    /// Hopping grid number of steps.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint GridSteps {
+      get { return gridSteps_; }
+      set {
+        gridSteps_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as LrFhssModulationInfo);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(LrFhssModulationInfo other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (OperatingChannelWidth != other.OperatingChannelWidth) return false;
+      if (CodeRateLegacy != other.CodeRateLegacy) return false;
+      if (CodeRate != other.CodeRate) return false;
+      if (GridSteps != other.GridSteps) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (OperatingChannelWidth != 0) hash ^= OperatingChannelWidth.GetHashCode();
+      if (CodeRateLegacy.Length != 0) hash ^= CodeRateLegacy.GetHashCode();
+      if (CodeRate != global::Chirpstack.Gateway.CodeRate.CrUndefined) hash ^= CodeRate.GetHashCode();
+      if (GridSteps != 0) hash ^= GridSteps.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (OperatingChannelWidth != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(OperatingChannelWidth);
+      }
+      if (CodeRateLegacy.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(CodeRateLegacy);
+      }
+      if (GridSteps != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(GridSteps);
+      }
+      if (CodeRate != global::Chirpstack.Gateway.CodeRate.CrUndefined) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) CodeRate);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (OperatingChannelWidth != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(OperatingChannelWidth);
+      }
+      if (CodeRateLegacy.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(CodeRateLegacy);
+      }
+      if (GridSteps != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(GridSteps);
+      }
+      if (CodeRate != global::Chirpstack.Gateway.CodeRate.CrUndefined) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) CodeRate);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (OperatingChannelWidth != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(OperatingChannelWidth);
+      }
+      if (CodeRateLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(CodeRateLegacy);
+      }
+      if (CodeRate != global::Chirpstack.Gateway.CodeRate.CrUndefined) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) CodeRate);
+      }
+      if (GridSteps != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(GridSteps);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(LrFhssModulationInfo other) {
+      if (other == null) {
+        return;
+      }
+      if (other.OperatingChannelWidth != 0) {
+        OperatingChannelWidth = other.OperatingChannelWidth;
+      }
+      if (other.CodeRateLegacy.Length != 0) {
+        CodeRateLegacy = other.CodeRateLegacy;
+      }
+      if (other.CodeRate != global::Chirpstack.Gateway.CodeRate.CrUndefined) {
+        CodeRate = other.CodeRate;
+      }
+      if (other.GridSteps != 0) {
+        GridSteps = other.GridSteps;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            OperatingChannelWidth = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            CodeRateLegacy = input.ReadString();
+            break;
+          }
+          case 24: {
+            GridSteps = input.ReadUInt32();
+            break;
+          }
+          case 32: {
+            CodeRate = (global::Chirpstack.Gateway.CodeRate) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            OperatingChannelWidth = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            CodeRateLegacy = input.ReadString();
+            break;
+          }
+          case 24: {
+            GridSteps = input.ReadUInt32();
+            break;
+          }
+          case 32: {
+            CodeRate = (global::Chirpstack.Gateway.CodeRate) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class EncryptedFineTimestamp : pb::IMessage<EncryptedFineTimestamp>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<EncryptedFineTimestamp> _parser = new pb::MessageParser<EncryptedFineTimestamp>(() => new EncryptedFineTimestamp());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<EncryptedFineTimestamp> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EncryptedFineTimestamp() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EncryptedFineTimestamp(EncryptedFineTimestamp other) : this() {
+      aesKeyIndex_ = other.aesKeyIndex_;
+      encryptedNs_ = other.encryptedNs_;
+      fpgaId_ = other.fpgaId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public EncryptedFineTimestamp Clone() {
+      return new EncryptedFineTimestamp(this);
+    }
+
+    /// <summary>Field number for the "aes_key_index" field.</summary>
+    public const int AesKeyIndexFieldNumber = 1;
+    private uint aesKeyIndex_;
+    /// <summary>
+    /// AES key index used for encrypting the fine timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint AesKeyIndex {
+      get { return aesKeyIndex_; }
+      set {
+        aesKeyIndex_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "encrypted_ns" field.</summary>
+    public const int EncryptedNsFieldNumber = 2;
+    private pb::ByteString encryptedNs_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Encrypted 'main' fine-timestamp (ns precision part of the timestamp).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString EncryptedNs {
+      get { return encryptedNs_; }
+      set {
+        encryptedNs_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "fpga_id" field.</summary>
+    public const int FpgaIdFieldNumber = 3;
+    private pb::ByteString fpgaId_ = pb::ByteString.Empty;
+    /// <summary>
+    /// FPGA ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString FpgaId {
+      get { return fpgaId_; }
+      set {
+        fpgaId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as EncryptedFineTimestamp);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(EncryptedFineTimestamp other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (AesKeyIndex != other.AesKeyIndex) return false;
+      if (EncryptedNs != other.EncryptedNs) return false;
+      if (FpgaId != other.FpgaId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (AesKeyIndex != 0) hash ^= AesKeyIndex.GetHashCode();
+      if (EncryptedNs.Length != 0) hash ^= EncryptedNs.GetHashCode();
+      if (FpgaId.Length != 0) hash ^= FpgaId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (AesKeyIndex != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(AesKeyIndex);
+      }
+      if (EncryptedNs.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteBytes(EncryptedNs);
+      }
+      if (FpgaId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteBytes(FpgaId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (AesKeyIndex != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(AesKeyIndex);
+      }
+      if (EncryptedNs.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteBytes(EncryptedNs);
+      }
+      if (FpgaId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteBytes(FpgaId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (AesKeyIndex != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(AesKeyIndex);
+      }
+      if (EncryptedNs.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(EncryptedNs);
+      }
+      if (FpgaId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(FpgaId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(EncryptedFineTimestamp other) {
+      if (other == null) {
+        return;
+      }
+      if (other.AesKeyIndex != 0) {
+        AesKeyIndex = other.AesKeyIndex;
+      }
+      if (other.EncryptedNs.Length != 0) {
+        EncryptedNs = other.EncryptedNs;
+      }
+      if (other.FpgaId.Length != 0) {
+        FpgaId = other.FpgaId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            AesKeyIndex = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            EncryptedNs = input.ReadBytes();
+            break;
+          }
+          case 26: {
+            FpgaId = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            AesKeyIndex = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            EncryptedNs = input.ReadBytes();
+            break;
+          }
+          case 26: {
+            FpgaId = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class PlainFineTimestamp : pb::IMessage<PlainFineTimestamp>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<PlainFineTimestamp> _parser = new pb::MessageParser<PlainFineTimestamp>(() => new PlainFineTimestamp());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<PlainFineTimestamp> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public PlainFineTimestamp() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public PlainFineTimestamp(PlainFineTimestamp other) : this() {
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public PlainFineTimestamp Clone() {
+      return new PlainFineTimestamp(this);
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 1;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Full timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as PlainFineTimestamp);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(PlainFineTimestamp other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Time, other.Time)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (time_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Time);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (time_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Time);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(PlainFineTimestamp other) {
+      if (other == null) {
+        return;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GatewayStats : pb::IMessage<GatewayStats>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GatewayStats> _parser = new pb::MessageParser<GatewayStats>(() => new GatewayStats());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GatewayStats> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayStats() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayStats(GatewayStats other) : this() {
+      gatewayIdLegacy_ = other.gatewayIdLegacy_;
+      gatewayId_ = other.gatewayId_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      location_ = other.location_ != null ? other.location_.Clone() : null;
+      configVersion_ = other.configVersion_;
+      rxPacketsReceived_ = other.rxPacketsReceived_;
+      rxPacketsReceivedOk_ = other.rxPacketsReceivedOk_;
+      txPacketsReceived_ = other.txPacketsReceived_;
+      txPacketsEmitted_ = other.txPacketsEmitted_;
+      metadata_ = other.metadata_.Clone();
+      txPacketsPerFrequency_ = other.txPacketsPerFrequency_.Clone();
+      rxPacketsPerFrequency_ = other.rxPacketsPerFrequency_.Clone();
+      txPacketsPerModulation_ = other.txPacketsPerModulation_.Clone();
+      rxPacketsPerModulation_ = other.rxPacketsPerModulation_.Clone();
+      txPacketsPerStatus_ = other.txPacketsPerStatus_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayStats Clone() {
+      return new GatewayStats(this);
+    }
+
+    /// <summary>Field number for the "gateway_id_legacy" field.</summary>
+    public const int GatewayIdLegacyFieldNumber = 1;
+    private pb::ByteString gatewayIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID.
+    /// Deprecated: use gateway_id.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayIdLegacy {
+      get { return gatewayIdLegacy_; }
+      set {
+        gatewayIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 17;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Gateway time.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "location" field.</summary>
+    public const int LocationFieldNumber = 3;
+    private global::Chirpstack.Common.Location location_;
+    /// <summary>
+    /// Gateway location.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Location Location {
+      get { return location_; }
+      set {
+        location_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "config_version" field.</summary>
+    public const int ConfigVersionFieldNumber = 4;
+    private string configVersion_ = "";
+    /// <summary>
+    /// Gateway configuration version (this maps to the config_version sent
+    /// by ChirpStack to the gateway).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ConfigVersion {
+      get { return configVersion_; }
+      set {
+        configVersion_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "rx_packets_received" field.</summary>
+    public const int RxPacketsReceivedFieldNumber = 5;
+    private uint rxPacketsReceived_;
+    /// <summary>
+    /// Number of radio packets received.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint RxPacketsReceived {
+      get { return rxPacketsReceived_; }
+      set {
+        rxPacketsReceived_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_packets_received_ok" field.</summary>
+    public const int RxPacketsReceivedOkFieldNumber = 6;
+    private uint rxPacketsReceivedOk_;
+    /// <summary>
+    /// Number of radio packets received with valid PHY CRC.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint RxPacketsReceivedOk {
+      get { return rxPacketsReceivedOk_; }
+      set {
+        rxPacketsReceivedOk_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tx_packets_received" field.</summary>
+    public const int TxPacketsReceivedFieldNumber = 7;
+    private uint txPacketsReceived_;
+    /// <summary>
+    /// Number of downlink packets received for transmission.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TxPacketsReceived {
+      get { return txPacketsReceived_; }
+      set {
+        txPacketsReceived_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tx_packets_emitted" field.</summary>
+    public const int TxPacketsEmittedFieldNumber = 8;
+    private uint txPacketsEmitted_;
+    /// <summary>
+    /// Number of downlink packets emitted.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint TxPacketsEmitted {
+      get { return txPacketsEmitted_; }
+      set {
+        txPacketsEmitted_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "metadata" field.</summary>
+    public const int MetadataFieldNumber = 10;
+    private static readonly pbc::MapField<string, string>.Codec _map_metadata_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 82);
+    private readonly pbc::MapField<string, string> metadata_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Additional gateway meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Metadata {
+      get { return metadata_; }
+    }
+
+    /// <summary>Field number for the "tx_packets_per_frequency" field.</summary>
+    public const int TxPacketsPerFrequencyFieldNumber = 12;
+    private static readonly pbc::MapField<uint, uint>.Codec _map_txPacketsPerFrequency_codec
+        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForUInt32(8, 0), pb::FieldCodec.ForUInt32(16, 0), 98);
+    private readonly pbc::MapField<uint, uint> txPacketsPerFrequency_ = new pbc::MapField<uint, uint>();
+    /// <summary>
+    /// Tx packets per frequency.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<uint, uint> TxPacketsPerFrequency {
+      get { return txPacketsPerFrequency_; }
+    }
+
+    /// <summary>Field number for the "rx_packets_per_frequency" field.</summary>
+    public const int RxPacketsPerFrequencyFieldNumber = 13;
+    private static readonly pbc::MapField<uint, uint>.Codec _map_rxPacketsPerFrequency_codec
+        = new pbc::MapField<uint, uint>.Codec(pb::FieldCodec.ForUInt32(8, 0), pb::FieldCodec.ForUInt32(16, 0), 106);
+    private readonly pbc::MapField<uint, uint> rxPacketsPerFrequency_ = new pbc::MapField<uint, uint>();
+    /// <summary>
+    /// Rx packets per frequency.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<uint, uint> RxPacketsPerFrequency {
+      get { return rxPacketsPerFrequency_; }
+    }
+
+    /// <summary>Field number for the "tx_packets_per_modulation" field.</summary>
+    public const int TxPacketsPerModulationFieldNumber = 14;
+    private static readonly pb::FieldCodec<global::Chirpstack.Gateway.PerModulationCount> _repeated_txPacketsPerModulation_codec
+        = pb::FieldCodec.ForMessage(114, global::Chirpstack.Gateway.PerModulationCount.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Gateway.PerModulationCount> txPacketsPerModulation_ = new pbc::RepeatedField<global::Chirpstack.Gateway.PerModulationCount>();
+    /// <summary>
+    /// Tx packets per modulation parameters.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Gateway.PerModulationCount> TxPacketsPerModulation {
+      get { return txPacketsPerModulation_; }
+    }
+
+    /// <summary>Field number for the "rx_packets_per_modulation" field.</summary>
+    public const int RxPacketsPerModulationFieldNumber = 15;
+    private static readonly pb::FieldCodec<global::Chirpstack.Gateway.PerModulationCount> _repeated_rxPacketsPerModulation_codec
+        = pb::FieldCodec.ForMessage(122, global::Chirpstack.Gateway.PerModulationCount.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Gateway.PerModulationCount> rxPacketsPerModulation_ = new pbc::RepeatedField<global::Chirpstack.Gateway.PerModulationCount>();
+    /// <summary>
+    /// Rx packets per modulation parameters.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Gateway.PerModulationCount> RxPacketsPerModulation {
+      get { return rxPacketsPerModulation_; }
+    }
+
+    /// <summary>Field number for the "tx_packets_per_status" field.</summary>
+    public const int TxPacketsPerStatusFieldNumber = 16;
+    private static readonly pbc::MapField<string, uint>.Codec _map_txPacketsPerStatus_codec
+        = new pbc::MapField<string, uint>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForUInt32(16, 0), 130);
+    private readonly pbc::MapField<string, uint> txPacketsPerStatus_ = new pbc::MapField<string, uint>();
+    /// <summary>
+    /// Tx packets per status.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, uint> TxPacketsPerStatus {
+      get { return txPacketsPerStatus_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GatewayStats);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GatewayStats other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayIdLegacy != other.GatewayIdLegacy) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(Location, other.Location)) return false;
+      if (ConfigVersion != other.ConfigVersion) return false;
+      if (RxPacketsReceived != other.RxPacketsReceived) return false;
+      if (RxPacketsReceivedOk != other.RxPacketsReceivedOk) return false;
+      if (TxPacketsReceived != other.TxPacketsReceived) return false;
+      if (TxPacketsEmitted != other.TxPacketsEmitted) return false;
+      if (!Metadata.Equals(other.Metadata)) return false;
+      if (!TxPacketsPerFrequency.Equals(other.TxPacketsPerFrequency)) return false;
+      if (!RxPacketsPerFrequency.Equals(other.RxPacketsPerFrequency)) return false;
+      if(!txPacketsPerModulation_.Equals(other.txPacketsPerModulation_)) return false;
+      if(!rxPacketsPerModulation_.Equals(other.rxPacketsPerModulation_)) return false;
+      if (!TxPacketsPerStatus.Equals(other.TxPacketsPerStatus)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayIdLegacy.Length != 0) hash ^= GatewayIdLegacy.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (location_ != null) hash ^= Location.GetHashCode();
+      if (ConfigVersion.Length != 0) hash ^= ConfigVersion.GetHashCode();
+      if (RxPacketsReceived != 0) hash ^= RxPacketsReceived.GetHashCode();
+      if (RxPacketsReceivedOk != 0) hash ^= RxPacketsReceivedOk.GetHashCode();
+      if (TxPacketsReceived != 0) hash ^= TxPacketsReceived.GetHashCode();
+      if (TxPacketsEmitted != 0) hash ^= TxPacketsEmitted.GetHashCode();
+      hash ^= Metadata.GetHashCode();
+      hash ^= TxPacketsPerFrequency.GetHashCode();
+      hash ^= RxPacketsPerFrequency.GetHashCode();
+      hash ^= txPacketsPerModulation_.GetHashCode();
+      hash ^= rxPacketsPerModulation_.GetHashCode();
+      hash ^= TxPacketsPerStatus.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(Location);
+      }
+      if (ConfigVersion.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ConfigVersion);
+      }
+      if (RxPacketsReceived != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(RxPacketsReceived);
+      }
+      if (RxPacketsReceivedOk != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(RxPacketsReceivedOk);
+      }
+      if (TxPacketsReceived != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(TxPacketsReceived);
+      }
+      if (TxPacketsEmitted != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(TxPacketsEmitted);
+      }
+      metadata_.WriteTo(output, _map_metadata_codec);
+      txPacketsPerFrequency_.WriteTo(output, _map_txPacketsPerFrequency_codec);
+      rxPacketsPerFrequency_.WriteTo(output, _map_rxPacketsPerFrequency_codec);
+      txPacketsPerModulation_.WriteTo(output, _repeated_txPacketsPerModulation_codec);
+      rxPacketsPerModulation_.WriteTo(output, _repeated_rxPacketsPerModulation_codec);
+      txPacketsPerStatus_.WriteTo(output, _map_txPacketsPerStatus_codec);
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(138, 1);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(Location);
+      }
+      if (ConfigVersion.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ConfigVersion);
+      }
+      if (RxPacketsReceived != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(RxPacketsReceived);
+      }
+      if (RxPacketsReceivedOk != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(RxPacketsReceivedOk);
+      }
+      if (TxPacketsReceived != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(TxPacketsReceived);
+      }
+      if (TxPacketsEmitted != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(TxPacketsEmitted);
+      }
+      metadata_.WriteTo(ref output, _map_metadata_codec);
+      txPacketsPerFrequency_.WriteTo(ref output, _map_txPacketsPerFrequency_codec);
+      rxPacketsPerFrequency_.WriteTo(ref output, _map_rxPacketsPerFrequency_codec);
+      txPacketsPerModulation_.WriteTo(ref output, _repeated_txPacketsPerModulation_codec);
+      rxPacketsPerModulation_.WriteTo(ref output, _repeated_rxPacketsPerModulation_codec);
+      txPacketsPerStatus_.WriteTo(ref output, _map_txPacketsPerStatus_codec);
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(138, 1);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (location_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Location);
+      }
+      if (ConfigVersion.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ConfigVersion);
+      }
+      if (RxPacketsReceived != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(RxPacketsReceived);
+      }
+      if (RxPacketsReceivedOk != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(RxPacketsReceivedOk);
+      }
+      if (TxPacketsReceived != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TxPacketsReceived);
+      }
+      if (TxPacketsEmitted != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(TxPacketsEmitted);
+      }
+      size += metadata_.CalculateSize(_map_metadata_codec);
+      size += txPacketsPerFrequency_.CalculateSize(_map_txPacketsPerFrequency_codec);
+      size += rxPacketsPerFrequency_.CalculateSize(_map_rxPacketsPerFrequency_codec);
+      size += txPacketsPerModulation_.CalculateSize(_repeated_txPacketsPerModulation_codec);
+      size += rxPacketsPerModulation_.CalculateSize(_repeated_rxPacketsPerModulation_codec);
+      size += txPacketsPerStatus_.CalculateSize(_map_txPacketsPerStatus_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GatewayStats other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayIdLegacy.Length != 0) {
+        GatewayIdLegacy = other.GatewayIdLegacy;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.location_ != null) {
+        if (location_ == null) {
+          Location = new global::Chirpstack.Common.Location();
+        }
+        Location.MergeFrom(other.Location);
+      }
+      if (other.ConfigVersion.Length != 0) {
+        ConfigVersion = other.ConfigVersion;
+      }
+      if (other.RxPacketsReceived != 0) {
+        RxPacketsReceived = other.RxPacketsReceived;
+      }
+      if (other.RxPacketsReceivedOk != 0) {
+        RxPacketsReceivedOk = other.RxPacketsReceivedOk;
+      }
+      if (other.TxPacketsReceived != 0) {
+        TxPacketsReceived = other.TxPacketsReceived;
+      }
+      if (other.TxPacketsEmitted != 0) {
+        TxPacketsEmitted = other.TxPacketsEmitted;
+      }
+      metadata_.Add(other.metadata_);
+      txPacketsPerFrequency_.Add(other.txPacketsPerFrequency_);
+      rxPacketsPerFrequency_.Add(other.rxPacketsPerFrequency_);
+      txPacketsPerModulation_.Add(other.txPacketsPerModulation_);
+      rxPacketsPerModulation_.Add(other.rxPacketsPerModulation_);
+      txPacketsPerStatus_.Add(other.txPacketsPerStatus_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+          case 34: {
+            ConfigVersion = input.ReadString();
+            break;
+          }
+          case 40: {
+            RxPacketsReceived = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            RxPacketsReceivedOk = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            TxPacketsReceived = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            TxPacketsEmitted = input.ReadUInt32();
+            break;
+          }
+          case 82: {
+            metadata_.AddEntriesFrom(input, _map_metadata_codec);
+            break;
+          }
+          case 98: {
+            txPacketsPerFrequency_.AddEntriesFrom(input, _map_txPacketsPerFrequency_codec);
+            break;
+          }
+          case 106: {
+            rxPacketsPerFrequency_.AddEntriesFrom(input, _map_rxPacketsPerFrequency_codec);
+            break;
+          }
+          case 114: {
+            txPacketsPerModulation_.AddEntriesFrom(input, _repeated_txPacketsPerModulation_codec);
+            break;
+          }
+          case 122: {
+            rxPacketsPerModulation_.AddEntriesFrom(input, _repeated_rxPacketsPerModulation_codec);
+            break;
+          }
+          case 130: {
+            txPacketsPerStatus_.AddEntriesFrom(input, _map_txPacketsPerStatus_codec);
+            break;
+          }
+          case 138: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+          case 34: {
+            ConfigVersion = input.ReadString();
+            break;
+          }
+          case 40: {
+            RxPacketsReceived = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            RxPacketsReceivedOk = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            TxPacketsReceived = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            TxPacketsEmitted = input.ReadUInt32();
+            break;
+          }
+          case 82: {
+            metadata_.AddEntriesFrom(ref input, _map_metadata_codec);
+            break;
+          }
+          case 98: {
+            txPacketsPerFrequency_.AddEntriesFrom(ref input, _map_txPacketsPerFrequency_codec);
+            break;
+          }
+          case 106: {
+            rxPacketsPerFrequency_.AddEntriesFrom(ref input, _map_rxPacketsPerFrequency_codec);
+            break;
+          }
+          case 114: {
+            txPacketsPerModulation_.AddEntriesFrom(ref input, _repeated_txPacketsPerModulation_codec);
+            break;
+          }
+          case 122: {
+            rxPacketsPerModulation_.AddEntriesFrom(ref input, _repeated_rxPacketsPerModulation_codec);
+            break;
+          }
+          case 130: {
+            txPacketsPerStatus_.AddEntriesFrom(ref input, _map_txPacketsPerStatus_codec);
+            break;
+          }
+          case 138: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class PerModulationCount : pb::IMessage<PerModulationCount>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<PerModulationCount> _parser = new pb::MessageParser<PerModulationCount>(() => new PerModulationCount());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<PerModulationCount> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public PerModulationCount() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public PerModulationCount(PerModulationCount other) : this() {
+      modulation_ = other.modulation_ != null ? other.modulation_.Clone() : null;
+      count_ = other.count_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public PerModulationCount Clone() {
+      return new PerModulationCount(this);
+    }
+
+    /// <summary>Field number for the "modulation" field.</summary>
+    public const int ModulationFieldNumber = 1;
+    private global::Chirpstack.Gateway.Modulation modulation_;
+    /// <summary>
+    /// Modulation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.Modulation Modulation {
+      get { return modulation_; }
+      set {
+        modulation_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "count" field.</summary>
+    public const int CountFieldNumber = 2;
+    private uint count_;
+    /// <summary>
+    /// Count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Count {
+      get { return count_; }
+      set {
+        count_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as PerModulationCount);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(PerModulationCount other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Modulation, other.Modulation)) return false;
+      if (Count != other.Count) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (modulation_ != null) hash ^= Modulation.GetHashCode();
+      if (Count != 0) hash ^= Count.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (modulation_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Modulation);
+      }
+      if (Count != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Count);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (modulation_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Modulation);
+      }
+      if (Count != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Count);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (modulation_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Modulation);
+      }
+      if (Count != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Count);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(PerModulationCount other) {
+      if (other == null) {
+        return;
+      }
+      if (other.modulation_ != null) {
+        if (modulation_ == null) {
+          Modulation = new global::Chirpstack.Gateway.Modulation();
+        }
+        Modulation.MergeFrom(other.Modulation);
+      }
+      if (other.Count != 0) {
+        Count = other.Count;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (modulation_ == null) {
+              Modulation = new global::Chirpstack.Gateway.Modulation();
+            }
+            input.ReadMessage(Modulation);
+            break;
+          }
+          case 16: {
+            Count = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (modulation_ == null) {
+              Modulation = new global::Chirpstack.Gateway.Modulation();
+            }
+            input.ReadMessage(Modulation);
+            break;
+          }
+          case 16: {
+            Count = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UplinkRxInfoLegacy : pb::IMessage<UplinkRxInfoLegacy>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UplinkRxInfoLegacy> _parser = new pb::MessageParser<UplinkRxInfoLegacy>(() => new UplinkRxInfoLegacy());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UplinkRxInfoLegacy> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[10]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkRxInfoLegacy() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkRxInfoLegacy(UplinkRxInfoLegacy other) : this() {
+      gatewayId_ = other.gatewayId_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      timeSinceGpsEpoch_ = other.timeSinceGpsEpoch_ != null ? other.timeSinceGpsEpoch_.Clone() : null;
+      rssi_ = other.rssi_;
+      loraSnr_ = other.loraSnr_;
+      channel_ = other.channel_;
+      rfChain_ = other.rfChain_;
+      board_ = other.board_;
+      antenna_ = other.antenna_;
+      location_ = other.location_ != null ? other.location_.Clone() : null;
+      fineTimestampType_ = other.fineTimestampType_;
+      context_ = other.context_;
+      uplinkId_ = other.uplinkId_;
+      crcStatus_ = other.crcStatus_;
+      metadata_ = other.metadata_.Clone();
+      switch (other.FineTimestampCase) {
+        case FineTimestampOneofCase.EncryptedFineTimestamp:
+          EncryptedFineTimestamp = other.EncryptedFineTimestamp.Clone();
+          break;
+        case FineTimestampOneofCase.PlainFineTimestamp:
+          PlainFineTimestamp = other.PlainFineTimestamp.Clone();
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkRxInfoLegacy Clone() {
+      return new UplinkRxInfoLegacy(this);
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 1;
+    private pb::ByteString gatewayId_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// RX time (only set when the gateway has a GPS module).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "time_since_gps_epoch" field.</summary>
+    public const int TimeSinceGpsEpochFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Duration timeSinceGpsEpoch_;
+    /// <summary>
+    /// RX time since GPS epoch (only set when the gateway has a GPS module).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Duration TimeSinceGpsEpoch {
+      get { return timeSinceGpsEpoch_; }
+      set {
+        timeSinceGpsEpoch_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rssi" field.</summary>
+    public const int RssiFieldNumber = 5;
+    private int rssi_;
+    /// <summary>
+    /// RSSI.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int Rssi {
+      get { return rssi_; }
+      set {
+        rssi_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "lora_snr" field.</summary>
+    public const int LoraSnrFieldNumber = 6;
+    private double loraSnr_;
+    /// <summary>
+    /// LoRa SNR.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public double LoraSnr {
+      get { return loraSnr_; }
+      set {
+        loraSnr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "channel" field.</summary>
+    public const int ChannelFieldNumber = 7;
+    private uint channel_;
+    /// <summary>
+    /// Channel.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Channel {
+      get { return channel_; }
+      set {
+        channel_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rf_chain" field.</summary>
+    public const int RfChainFieldNumber = 8;
+    private uint rfChain_;
+    /// <summary>
+    /// RF Chain.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint RfChain {
+      get { return rfChain_; }
+      set {
+        rfChain_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "board" field.</summary>
+    public const int BoardFieldNumber = 9;
+    private uint board_;
+    /// <summary>
+    /// Board.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Board {
+      get { return board_; }
+      set {
+        board_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "antenna" field.</summary>
+    public const int AntennaFieldNumber = 10;
+    private uint antenna_;
+    /// <summary>
+    /// Antenna.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Antenna {
+      get { return antenna_; }
+      set {
+        antenna_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "location" field.</summary>
+    public const int LocationFieldNumber = 11;
+    private global::Chirpstack.Common.Location location_;
+    /// <summary>
+    /// Location.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Location Location {
+      get { return location_; }
+      set {
+        location_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "fine_timestamp_type" field.</summary>
+    public const int FineTimestampTypeFieldNumber = 12;
+    private global::Chirpstack.Gateway.FineTimestampType fineTimestampType_ = global::Chirpstack.Gateway.FineTimestampType.None;
+    /// <summary>
+    /// Fine-timestamp type.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.FineTimestampType FineTimestampType {
+      get { return fineTimestampType_; }
+      set {
+        fineTimestampType_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "encrypted_fine_timestamp" field.</summary>
+    public const int EncryptedFineTimestampFieldNumber = 13;
+    /// <summary>
+    /// Encrypted fine-timestamp data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.EncryptedFineTimestamp EncryptedFineTimestamp {
+      get { return fineTimestampCase_ == FineTimestampOneofCase.EncryptedFineTimestamp ? (global::Chirpstack.Gateway.EncryptedFineTimestamp) fineTimestamp_ : null; }
+      set {
+        fineTimestamp_ = value;
+        fineTimestampCase_ = value == null ? FineTimestampOneofCase.None : FineTimestampOneofCase.EncryptedFineTimestamp;
+      }
+    }
+
+    /// <summary>Field number for the "plain_fine_timestamp" field.</summary>
+    public const int PlainFineTimestampFieldNumber = 14;
+    /// <summary>
+    /// Plain fine-timestamp data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.PlainFineTimestamp PlainFineTimestamp {
+      get { return fineTimestampCase_ == FineTimestampOneofCase.PlainFineTimestamp ? (global::Chirpstack.Gateway.PlainFineTimestamp) fineTimestamp_ : null; }
+      set {
+        fineTimestamp_ = value;
+        fineTimestampCase_ = value == null ? FineTimestampOneofCase.None : FineTimestampOneofCase.PlainFineTimestamp;
+      }
+    }
+
+    /// <summary>Field number for the "context" field.</summary>
+    public const int ContextFieldNumber = 15;
+    private pb::ByteString context_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway specific context.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Context {
+      get { return context_; }
+      set {
+        context_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "uplink_id" field.</summary>
+    public const int UplinkIdFieldNumber = 16;
+    private pb::ByteString uplinkId_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Uplink ID (UUID bytes).
+    /// Unique and random ID which can be used to correlate the uplink across multiple logs.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString UplinkId {
+      get { return uplinkId_; }
+      set {
+        uplinkId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "crc_status" field.</summary>
+    public const int CrcStatusFieldNumber = 17;
+    private global::Chirpstack.Gateway.CRCStatus crcStatus_ = global::Chirpstack.Gateway.CRCStatus.NoCrc;
+    /// <summary>
+    /// CRC status.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.CRCStatus CrcStatus {
+      get { return crcStatus_; }
+      set {
+        crcStatus_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "metadata" field.</summary>
+    public const int MetadataFieldNumber = 18;
+    private static readonly pbc::MapField<string, string>.Codec _map_metadata_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 146);
+    private readonly pbc::MapField<string, string> metadata_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Optional meta-data map.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Metadata {
+      get { return metadata_; }
+    }
+
+    private object fineTimestamp_;
+    /// <summary>Enum of possible cases for the "fine_timestamp" oneof.</summary>
+    public enum FineTimestampOneofCase {
+      None = 0,
+      EncryptedFineTimestamp = 13,
+      PlainFineTimestamp = 14,
+    }
+    private FineTimestampOneofCase fineTimestampCase_ = FineTimestampOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FineTimestampOneofCase FineTimestampCase {
+      get { return fineTimestampCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearFineTimestamp() {
+      fineTimestampCase_ = FineTimestampOneofCase.None;
+      fineTimestamp_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UplinkRxInfoLegacy);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UplinkRxInfoLegacy other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayId != other.GatewayId) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(TimeSinceGpsEpoch, other.TimeSinceGpsEpoch)) return false;
+      if (Rssi != other.Rssi) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(LoraSnr, other.LoraSnr)) return false;
+      if (Channel != other.Channel) return false;
+      if (RfChain != other.RfChain) return false;
+      if (Board != other.Board) return false;
+      if (Antenna != other.Antenna) return false;
+      if (!object.Equals(Location, other.Location)) return false;
+      if (FineTimestampType != other.FineTimestampType) return false;
+      if (!object.Equals(EncryptedFineTimestamp, other.EncryptedFineTimestamp)) return false;
+      if (!object.Equals(PlainFineTimestamp, other.PlainFineTimestamp)) return false;
+      if (Context != other.Context) return false;
+      if (UplinkId != other.UplinkId) return false;
+      if (CrcStatus != other.CrcStatus) return false;
+      if (!Metadata.Equals(other.Metadata)) return false;
+      if (FineTimestampCase != other.FineTimestampCase) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (timeSinceGpsEpoch_ != null) hash ^= TimeSinceGpsEpoch.GetHashCode();
+      if (Rssi != 0) hash ^= Rssi.GetHashCode();
+      if (LoraSnr != 0D) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(LoraSnr);
+      if (Channel != 0) hash ^= Channel.GetHashCode();
+      if (RfChain != 0) hash ^= RfChain.GetHashCode();
+      if (Board != 0) hash ^= Board.GetHashCode();
+      if (Antenna != 0) hash ^= Antenna.GetHashCode();
+      if (location_ != null) hash ^= Location.GetHashCode();
+      if (FineTimestampType != global::Chirpstack.Gateway.FineTimestampType.None) hash ^= FineTimestampType.GetHashCode();
+      if (fineTimestampCase_ == FineTimestampOneofCase.EncryptedFineTimestamp) hash ^= EncryptedFineTimestamp.GetHashCode();
+      if (fineTimestampCase_ == FineTimestampOneofCase.PlainFineTimestamp) hash ^= PlainFineTimestamp.GetHashCode();
+      if (Context.Length != 0) hash ^= Context.GetHashCode();
+      if (UplinkId.Length != 0) hash ^= UplinkId.GetHashCode();
+      if (CrcStatus != global::Chirpstack.Gateway.CRCStatus.NoCrc) hash ^= CrcStatus.GetHashCode();
+      hash ^= Metadata.GetHashCode();
+      hash ^= (int) fineTimestampCase_;
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (timeSinceGpsEpoch_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(TimeSinceGpsEpoch);
+      }
+      if (Rssi != 0) {
+        output.WriteRawTag(40);
+        output.WriteInt32(Rssi);
+      }
+      if (LoraSnr != 0D) {
+        output.WriteRawTag(49);
+        output.WriteDouble(LoraSnr);
+      }
+      if (Channel != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(Channel);
+      }
+      if (RfChain != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(RfChain);
+      }
+      if (Board != 0) {
+        output.WriteRawTag(72);
+        output.WriteUInt32(Board);
+      }
+      if (Antenna != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(Antenna);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(90);
+        output.WriteMessage(Location);
+      }
+      if (FineTimestampType != global::Chirpstack.Gateway.FineTimestampType.None) {
+        output.WriteRawTag(96);
+        output.WriteEnum((int) FineTimestampType);
+      }
+      if (fineTimestampCase_ == FineTimestampOneofCase.EncryptedFineTimestamp) {
+        output.WriteRawTag(106);
+        output.WriteMessage(EncryptedFineTimestamp);
+      }
+      if (fineTimestampCase_ == FineTimestampOneofCase.PlainFineTimestamp) {
+        output.WriteRawTag(114);
+        output.WriteMessage(PlainFineTimestamp);
+      }
+      if (Context.Length != 0) {
+        output.WriteRawTag(122);
+        output.WriteBytes(Context);
+      }
+      if (UplinkId.Length != 0) {
+        output.WriteRawTag(130, 1);
+        output.WriteBytes(UplinkId);
+      }
+      if (CrcStatus != global::Chirpstack.Gateway.CRCStatus.NoCrc) {
+        output.WriteRawTag(136, 1);
+        output.WriteEnum((int) CrcStatus);
+      }
+      metadata_.WriteTo(output, _map_metadata_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (timeSinceGpsEpoch_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(TimeSinceGpsEpoch);
+      }
+      if (Rssi != 0) {
+        output.WriteRawTag(40);
+        output.WriteInt32(Rssi);
+      }
+      if (LoraSnr != 0D) {
+        output.WriteRawTag(49);
+        output.WriteDouble(LoraSnr);
+      }
+      if (Channel != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(Channel);
+      }
+      if (RfChain != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(RfChain);
+      }
+      if (Board != 0) {
+        output.WriteRawTag(72);
+        output.WriteUInt32(Board);
+      }
+      if (Antenna != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(Antenna);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(90);
+        output.WriteMessage(Location);
+      }
+      if (FineTimestampType != global::Chirpstack.Gateway.FineTimestampType.None) {
+        output.WriteRawTag(96);
+        output.WriteEnum((int) FineTimestampType);
+      }
+      if (fineTimestampCase_ == FineTimestampOneofCase.EncryptedFineTimestamp) {
+        output.WriteRawTag(106);
+        output.WriteMessage(EncryptedFineTimestamp);
+      }
+      if (fineTimestampCase_ == FineTimestampOneofCase.PlainFineTimestamp) {
+        output.WriteRawTag(114);
+        output.WriteMessage(PlainFineTimestamp);
+      }
+      if (Context.Length != 0) {
+        output.WriteRawTag(122);
+        output.WriteBytes(Context);
+      }
+      if (UplinkId.Length != 0) {
+        output.WriteRawTag(130, 1);
+        output.WriteBytes(UplinkId);
+      }
+      if (CrcStatus != global::Chirpstack.Gateway.CRCStatus.NoCrc) {
+        output.WriteRawTag(136, 1);
+        output.WriteEnum((int) CrcStatus);
+      }
+      metadata_.WriteTo(ref output, _map_metadata_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayId);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (timeSinceGpsEpoch_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TimeSinceGpsEpoch);
+      }
+      if (Rssi != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Rssi);
+      }
+      if (LoraSnr != 0D) {
+        size += 1 + 8;
+      }
+      if (Channel != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Channel);
+      }
+      if (RfChain != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(RfChain);
+      }
+      if (Board != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Board);
+      }
+      if (Antenna != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Antenna);
+      }
+      if (location_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Location);
+      }
+      if (FineTimestampType != global::Chirpstack.Gateway.FineTimestampType.None) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) FineTimestampType);
+      }
+      if (fineTimestampCase_ == FineTimestampOneofCase.EncryptedFineTimestamp) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(EncryptedFineTimestamp);
+      }
+      if (fineTimestampCase_ == FineTimestampOneofCase.PlainFineTimestamp) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(PlainFineTimestamp);
+      }
+      if (Context.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Context);
+      }
+      if (UplinkId.Length != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeBytesSize(UplinkId);
+      }
+      if (CrcStatus != global::Chirpstack.Gateway.CRCStatus.NoCrc) {
+        size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) CrcStatus);
+      }
+      size += metadata_.CalculateSize(_map_metadata_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UplinkRxInfoLegacy other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.timeSinceGpsEpoch_ != null) {
+        if (timeSinceGpsEpoch_ == null) {
+          TimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+        }
+        TimeSinceGpsEpoch.MergeFrom(other.TimeSinceGpsEpoch);
+      }
+      if (other.Rssi != 0) {
+        Rssi = other.Rssi;
+      }
+      if (other.LoraSnr != 0D) {
+        LoraSnr = other.LoraSnr;
+      }
+      if (other.Channel != 0) {
+        Channel = other.Channel;
+      }
+      if (other.RfChain != 0) {
+        RfChain = other.RfChain;
+      }
+      if (other.Board != 0) {
+        Board = other.Board;
+      }
+      if (other.Antenna != 0) {
+        Antenna = other.Antenna;
+      }
+      if (other.location_ != null) {
+        if (location_ == null) {
+          Location = new global::Chirpstack.Common.Location();
+        }
+        Location.MergeFrom(other.Location);
+      }
+      if (other.FineTimestampType != global::Chirpstack.Gateway.FineTimestampType.None) {
+        FineTimestampType = other.FineTimestampType;
+      }
+      if (other.Context.Length != 0) {
+        Context = other.Context;
+      }
+      if (other.UplinkId.Length != 0) {
+        UplinkId = other.UplinkId;
+      }
+      if (other.CrcStatus != global::Chirpstack.Gateway.CRCStatus.NoCrc) {
+        CrcStatus = other.CrcStatus;
+      }
+      metadata_.Add(other.metadata_);
+      switch (other.FineTimestampCase) {
+        case FineTimestampOneofCase.EncryptedFineTimestamp:
+          if (EncryptedFineTimestamp == null) {
+            EncryptedFineTimestamp = new global::Chirpstack.Gateway.EncryptedFineTimestamp();
+          }
+          EncryptedFineTimestamp.MergeFrom(other.EncryptedFineTimestamp);
+          break;
+        case FineTimestampOneofCase.PlainFineTimestamp:
+          if (PlainFineTimestamp == null) {
+            PlainFineTimestamp = new global::Chirpstack.Gateway.PlainFineTimestamp();
+          }
+          PlainFineTimestamp.MergeFrom(other.PlainFineTimestamp);
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayId = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (timeSinceGpsEpoch_ == null) {
+              TimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(TimeSinceGpsEpoch);
+            break;
+          }
+          case 40: {
+            Rssi = input.ReadInt32();
+            break;
+          }
+          case 49: {
+            LoraSnr = input.ReadDouble();
+            break;
+          }
+          case 56: {
+            Channel = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            RfChain = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            Board = input.ReadUInt32();
+            break;
+          }
+          case 80: {
+            Antenna = input.ReadUInt32();
+            break;
+          }
+          case 90: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+          case 96: {
+            FineTimestampType = (global::Chirpstack.Gateway.FineTimestampType) input.ReadEnum();
+            break;
+          }
+          case 106: {
+            global::Chirpstack.Gateway.EncryptedFineTimestamp subBuilder = new global::Chirpstack.Gateway.EncryptedFineTimestamp();
+            if (fineTimestampCase_ == FineTimestampOneofCase.EncryptedFineTimestamp) {
+              subBuilder.MergeFrom(EncryptedFineTimestamp);
+            }
+            input.ReadMessage(subBuilder);
+            EncryptedFineTimestamp = subBuilder;
+            break;
+          }
+          case 114: {
+            global::Chirpstack.Gateway.PlainFineTimestamp subBuilder = new global::Chirpstack.Gateway.PlainFineTimestamp();
+            if (fineTimestampCase_ == FineTimestampOneofCase.PlainFineTimestamp) {
+              subBuilder.MergeFrom(PlainFineTimestamp);
+            }
+            input.ReadMessage(subBuilder);
+            PlainFineTimestamp = subBuilder;
+            break;
+          }
+          case 122: {
+            Context = input.ReadBytes();
+            break;
+          }
+          case 130: {
+            UplinkId = input.ReadBytes();
+            break;
+          }
+          case 136: {
+            CrcStatus = (global::Chirpstack.Gateway.CRCStatus) input.ReadEnum();
+            break;
+          }
+          case 146: {
+            metadata_.AddEntriesFrom(input, _map_metadata_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayId = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (timeSinceGpsEpoch_ == null) {
+              TimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(TimeSinceGpsEpoch);
+            break;
+          }
+          case 40: {
+            Rssi = input.ReadInt32();
+            break;
+          }
+          case 49: {
+            LoraSnr = input.ReadDouble();
+            break;
+          }
+          case 56: {
+            Channel = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            RfChain = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            Board = input.ReadUInt32();
+            break;
+          }
+          case 80: {
+            Antenna = input.ReadUInt32();
+            break;
+          }
+          case 90: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+          case 96: {
+            FineTimestampType = (global::Chirpstack.Gateway.FineTimestampType) input.ReadEnum();
+            break;
+          }
+          case 106: {
+            global::Chirpstack.Gateway.EncryptedFineTimestamp subBuilder = new global::Chirpstack.Gateway.EncryptedFineTimestamp();
+            if (fineTimestampCase_ == FineTimestampOneofCase.EncryptedFineTimestamp) {
+              subBuilder.MergeFrom(EncryptedFineTimestamp);
+            }
+            input.ReadMessage(subBuilder);
+            EncryptedFineTimestamp = subBuilder;
+            break;
+          }
+          case 114: {
+            global::Chirpstack.Gateway.PlainFineTimestamp subBuilder = new global::Chirpstack.Gateway.PlainFineTimestamp();
+            if (fineTimestampCase_ == FineTimestampOneofCase.PlainFineTimestamp) {
+              subBuilder.MergeFrom(PlainFineTimestamp);
+            }
+            input.ReadMessage(subBuilder);
+            PlainFineTimestamp = subBuilder;
+            break;
+          }
+          case 122: {
+            Context = input.ReadBytes();
+            break;
+          }
+          case 130: {
+            UplinkId = input.ReadBytes();
+            break;
+          }
+          case 136: {
+            CrcStatus = (global::Chirpstack.Gateway.CRCStatus) input.ReadEnum();
+            break;
+          }
+          case 146: {
+            metadata_.AddEntriesFrom(ref input, _map_metadata_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UplinkRxInfo : pb::IMessage<UplinkRxInfo>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UplinkRxInfo> _parser = new pb::MessageParser<UplinkRxInfo>(() => new UplinkRxInfo());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UplinkRxInfo> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[11]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkRxInfo() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkRxInfo(UplinkRxInfo other) : this() {
+      gatewayId_ = other.gatewayId_;
+      uplinkId_ = other.uplinkId_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      timeSinceGpsEpoch_ = other.timeSinceGpsEpoch_ != null ? other.timeSinceGpsEpoch_.Clone() : null;
+      fineTimeSinceGpsEpoch_ = other.fineTimeSinceGpsEpoch_ != null ? other.fineTimeSinceGpsEpoch_.Clone() : null;
+      rssi_ = other.rssi_;
+      snr_ = other.snr_;
+      channel_ = other.channel_;
+      rfChain_ = other.rfChain_;
+      board_ = other.board_;
+      antenna_ = other.antenna_;
+      location_ = other.location_ != null ? other.location_.Clone() : null;
+      context_ = other.context_;
+      metadata_ = other.metadata_.Clone();
+      crcStatus_ = other.crcStatus_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkRxInfo Clone() {
+      return new UplinkRxInfo(this);
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 1;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "uplink_id" field.</summary>
+    public const int UplinkIdFieldNumber = 2;
+    private uint uplinkId_;
+    /// <summary>
+    /// Uplink ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint UplinkId {
+      get { return uplinkId_; }
+      set {
+        uplinkId_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 3;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// RX time (only set when the gateway has a GPS module).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "time_since_gps_epoch" field.</summary>
+    public const int TimeSinceGpsEpochFieldNumber = 4;
+    private global::Google.Protobuf.WellKnownTypes.Duration timeSinceGpsEpoch_;
+    /// <summary>
+    /// RX time since GPS epoch (only set when the gateway has a GPS module).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Duration TimeSinceGpsEpoch {
+      get { return timeSinceGpsEpoch_; }
+      set {
+        timeSinceGpsEpoch_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "fine_time_since_gps_epoch" field.</summary>
+    public const int FineTimeSinceGpsEpochFieldNumber = 5;
+    private global::Google.Protobuf.WellKnownTypes.Duration fineTimeSinceGpsEpoch_;
+    /// <summary>
+    /// Fine-timestamp.
+    /// This timestamp can be used for TDOA based geolocation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Duration FineTimeSinceGpsEpoch {
+      get { return fineTimeSinceGpsEpoch_; }
+      set {
+        fineTimeSinceGpsEpoch_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rssi" field.</summary>
+    public const int RssiFieldNumber = 6;
+    private int rssi_;
+    /// <summary>
+    /// RSSI.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int Rssi {
+      get { return rssi_; }
+      set {
+        rssi_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "snr" field.</summary>
+    public const int SnrFieldNumber = 7;
+    private float snr_;
+    /// <summary>
+    /// SNR.
+    /// Note: only available for LoRa modulation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public float Snr {
+      get { return snr_; }
+      set {
+        snr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "channel" field.</summary>
+    public const int ChannelFieldNumber = 8;
+    private uint channel_;
+    /// <summary>
+    /// Channel.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Channel {
+      get { return channel_; }
+      set {
+        channel_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rf_chain" field.</summary>
+    public const int RfChainFieldNumber = 9;
+    private uint rfChain_;
+    /// <summary>
+    /// RF chain.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint RfChain {
+      get { return rfChain_; }
+      set {
+        rfChain_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "board" field.</summary>
+    public const int BoardFieldNumber = 10;
+    private uint board_;
+    /// <summary>
+    /// Board.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Board {
+      get { return board_; }
+      set {
+        board_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "antenna" field.</summary>
+    public const int AntennaFieldNumber = 11;
+    private uint antenna_;
+    /// <summary>
+    /// Antenna.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Antenna {
+      get { return antenna_; }
+      set {
+        antenna_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "location" field.</summary>
+    public const int LocationFieldNumber = 12;
+    private global::Chirpstack.Common.Location location_;
+    /// <summary>
+    /// Location.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Location Location {
+      get { return location_; }
+      set {
+        location_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "context" field.</summary>
+    public const int ContextFieldNumber = 13;
+    private pb::ByteString context_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway specific context.
+    /// This value must be returned to the gateway on (Class-A) downlink.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Context {
+      get { return context_; }
+      set {
+        context_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "metadata" field.</summary>
+    public const int MetadataFieldNumber = 15;
+    private static readonly pbc::MapField<string, string>.Codec _map_metadata_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 122);
+    private readonly pbc::MapField<string, string> metadata_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Additional gateway meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Metadata {
+      get { return metadata_; }
+    }
+
+    /// <summary>Field number for the "crc_status" field.</summary>
+    public const int CrcStatusFieldNumber = 16;
+    private global::Chirpstack.Gateway.CRCStatus crcStatus_ = global::Chirpstack.Gateway.CRCStatus.NoCrc;
+    /// <summary>
+    /// CRC status.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.CRCStatus CrcStatus {
+      get { return crcStatus_; }
+      set {
+        crcStatus_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UplinkRxInfo);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UplinkRxInfo other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayId != other.GatewayId) return false;
+      if (UplinkId != other.UplinkId) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(TimeSinceGpsEpoch, other.TimeSinceGpsEpoch)) return false;
+      if (!object.Equals(FineTimeSinceGpsEpoch, other.FineTimeSinceGpsEpoch)) return false;
+      if (Rssi != other.Rssi) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(Snr, other.Snr)) return false;
+      if (Channel != other.Channel) return false;
+      if (RfChain != other.RfChain) return false;
+      if (Board != other.Board) return false;
+      if (Antenna != other.Antenna) return false;
+      if (!object.Equals(Location, other.Location)) return false;
+      if (Context != other.Context) return false;
+      if (!Metadata.Equals(other.Metadata)) return false;
+      if (CrcStatus != other.CrcStatus) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (UplinkId != 0) hash ^= UplinkId.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (timeSinceGpsEpoch_ != null) hash ^= TimeSinceGpsEpoch.GetHashCode();
+      if (fineTimeSinceGpsEpoch_ != null) hash ^= FineTimeSinceGpsEpoch.GetHashCode();
+      if (Rssi != 0) hash ^= Rssi.GetHashCode();
+      if (Snr != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(Snr);
+      if (Channel != 0) hash ^= Channel.GetHashCode();
+      if (RfChain != 0) hash ^= RfChain.GetHashCode();
+      if (Board != 0) hash ^= Board.GetHashCode();
+      if (Antenna != 0) hash ^= Antenna.GetHashCode();
+      if (location_ != null) hash ^= Location.GetHashCode();
+      if (Context.Length != 0) hash ^= Context.GetHashCode();
+      hash ^= Metadata.GetHashCode();
+      if (CrcStatus != global::Chirpstack.Gateway.CRCStatus.NoCrc) hash ^= CrcStatus.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (UplinkId != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(UplinkId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(Time);
+      }
+      if (timeSinceGpsEpoch_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(TimeSinceGpsEpoch);
+      }
+      if (fineTimeSinceGpsEpoch_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(FineTimeSinceGpsEpoch);
+      }
+      if (Rssi != 0) {
+        output.WriteRawTag(48);
+        output.WriteInt32(Rssi);
+      }
+      if (Snr != 0F) {
+        output.WriteRawTag(61);
+        output.WriteFloat(Snr);
+      }
+      if (Channel != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(Channel);
+      }
+      if (RfChain != 0) {
+        output.WriteRawTag(72);
+        output.WriteUInt32(RfChain);
+      }
+      if (Board != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(Board);
+      }
+      if (Antenna != 0) {
+        output.WriteRawTag(88);
+        output.WriteUInt32(Antenna);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(98);
+        output.WriteMessage(Location);
+      }
+      if (Context.Length != 0) {
+        output.WriteRawTag(106);
+        output.WriteBytes(Context);
+      }
+      metadata_.WriteTo(output, _map_metadata_codec);
+      if (CrcStatus != global::Chirpstack.Gateway.CRCStatus.NoCrc) {
+        output.WriteRawTag(128, 1);
+        output.WriteEnum((int) CrcStatus);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(GatewayId);
+      }
+      if (UplinkId != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(UplinkId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(Time);
+      }
+      if (timeSinceGpsEpoch_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(TimeSinceGpsEpoch);
+      }
+      if (fineTimeSinceGpsEpoch_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(FineTimeSinceGpsEpoch);
+      }
+      if (Rssi != 0) {
+        output.WriteRawTag(48);
+        output.WriteInt32(Rssi);
+      }
+      if (Snr != 0F) {
+        output.WriteRawTag(61);
+        output.WriteFloat(Snr);
+      }
+      if (Channel != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(Channel);
+      }
+      if (RfChain != 0) {
+        output.WriteRawTag(72);
+        output.WriteUInt32(RfChain);
+      }
+      if (Board != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(Board);
+      }
+      if (Antenna != 0) {
+        output.WriteRawTag(88);
+        output.WriteUInt32(Antenna);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(98);
+        output.WriteMessage(Location);
+      }
+      if (Context.Length != 0) {
+        output.WriteRawTag(106);
+        output.WriteBytes(Context);
+      }
+      metadata_.WriteTo(ref output, _map_metadata_codec);
+      if (CrcStatus != global::Chirpstack.Gateway.CRCStatus.NoCrc) {
+        output.WriteRawTag(128, 1);
+        output.WriteEnum((int) CrcStatus);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (UplinkId != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(UplinkId);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (timeSinceGpsEpoch_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TimeSinceGpsEpoch);
+      }
+      if (fineTimeSinceGpsEpoch_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(FineTimeSinceGpsEpoch);
+      }
+      if (Rssi != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Rssi);
+      }
+      if (Snr != 0F) {
+        size += 1 + 4;
+      }
+      if (Channel != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Channel);
+      }
+      if (RfChain != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(RfChain);
+      }
+      if (Board != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Board);
+      }
+      if (Antenna != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Antenna);
+      }
+      if (location_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Location);
+      }
+      if (Context.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Context);
+      }
+      size += metadata_.CalculateSize(_map_metadata_codec);
+      if (CrcStatus != global::Chirpstack.Gateway.CRCStatus.NoCrc) {
+        size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) CrcStatus);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UplinkRxInfo other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.UplinkId != 0) {
+        UplinkId = other.UplinkId;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.timeSinceGpsEpoch_ != null) {
+        if (timeSinceGpsEpoch_ == null) {
+          TimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+        }
+        TimeSinceGpsEpoch.MergeFrom(other.TimeSinceGpsEpoch);
+      }
+      if (other.fineTimeSinceGpsEpoch_ != null) {
+        if (fineTimeSinceGpsEpoch_ == null) {
+          FineTimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+        }
+        FineTimeSinceGpsEpoch.MergeFrom(other.FineTimeSinceGpsEpoch);
+      }
+      if (other.Rssi != 0) {
+        Rssi = other.Rssi;
+      }
+      if (other.Snr != 0F) {
+        Snr = other.Snr;
+      }
+      if (other.Channel != 0) {
+        Channel = other.Channel;
+      }
+      if (other.RfChain != 0) {
+        RfChain = other.RfChain;
+      }
+      if (other.Board != 0) {
+        Board = other.Board;
+      }
+      if (other.Antenna != 0) {
+        Antenna = other.Antenna;
+      }
+      if (other.location_ != null) {
+        if (location_ == null) {
+          Location = new global::Chirpstack.Common.Location();
+        }
+        Location.MergeFrom(other.Location);
+      }
+      if (other.Context.Length != 0) {
+        Context = other.Context;
+      }
+      metadata_.Add(other.metadata_);
+      if (other.CrcStatus != global::Chirpstack.Gateway.CRCStatus.NoCrc) {
+        CrcStatus = other.CrcStatus;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 16: {
+            UplinkId = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 34: {
+            if (timeSinceGpsEpoch_ == null) {
+              TimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(TimeSinceGpsEpoch);
+            break;
+          }
+          case 42: {
+            if (fineTimeSinceGpsEpoch_ == null) {
+              FineTimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(FineTimeSinceGpsEpoch);
+            break;
+          }
+          case 48: {
+            Rssi = input.ReadInt32();
+            break;
+          }
+          case 61: {
+            Snr = input.ReadFloat();
+            break;
+          }
+          case 64: {
+            Channel = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            RfChain = input.ReadUInt32();
+            break;
+          }
+          case 80: {
+            Board = input.ReadUInt32();
+            break;
+          }
+          case 88: {
+            Antenna = input.ReadUInt32();
+            break;
+          }
+          case 98: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+          case 106: {
+            Context = input.ReadBytes();
+            break;
+          }
+          case 122: {
+            metadata_.AddEntriesFrom(input, _map_metadata_codec);
+            break;
+          }
+          case 128: {
+            CrcStatus = (global::Chirpstack.Gateway.CRCStatus) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 16: {
+            UplinkId = input.ReadUInt32();
+            break;
+          }
+          case 26: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 34: {
+            if (timeSinceGpsEpoch_ == null) {
+              TimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(TimeSinceGpsEpoch);
+            break;
+          }
+          case 42: {
+            if (fineTimeSinceGpsEpoch_ == null) {
+              FineTimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(FineTimeSinceGpsEpoch);
+            break;
+          }
+          case 48: {
+            Rssi = input.ReadInt32();
+            break;
+          }
+          case 61: {
+            Snr = input.ReadFloat();
+            break;
+          }
+          case 64: {
+            Channel = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            RfChain = input.ReadUInt32();
+            break;
+          }
+          case 80: {
+            Board = input.ReadUInt32();
+            break;
+          }
+          case 88: {
+            Antenna = input.ReadUInt32();
+            break;
+          }
+          case 98: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+          case 106: {
+            Context = input.ReadBytes();
+            break;
+          }
+          case 122: {
+            metadata_.AddEntriesFrom(ref input, _map_metadata_codec);
+            break;
+          }
+          case 128: {
+            CrcStatus = (global::Chirpstack.Gateway.CRCStatus) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DownlinkTxInfoLegacy : pb::IMessage<DownlinkTxInfoLegacy>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DownlinkTxInfoLegacy> _parser = new pb::MessageParser<DownlinkTxInfoLegacy>(() => new DownlinkTxInfoLegacy());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DownlinkTxInfoLegacy> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[12]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxInfoLegacy() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxInfoLegacy(DownlinkTxInfoLegacy other) : this() {
+      gatewayId_ = other.gatewayId_;
+      frequency_ = other.frequency_;
+      power_ = other.power_;
+      modulation_ = other.modulation_;
+      board_ = other.board_;
+      antenna_ = other.antenna_;
+      timing_ = other.timing_;
+      context_ = other.context_;
+      switch (other.ModulationInfoCase) {
+        case ModulationInfoOneofCase.LoraModulationInfo:
+          LoraModulationInfo = other.LoraModulationInfo.Clone();
+          break;
+        case ModulationInfoOneofCase.FskModulationInfo:
+          FskModulationInfo = other.FskModulationInfo.Clone();
+          break;
+      }
+
+      switch (other.TimingInfoCase) {
+        case TimingInfoOneofCase.ImmediatelyTimingInfo:
+          ImmediatelyTimingInfo = other.ImmediatelyTimingInfo.Clone();
+          break;
+        case TimingInfoOneofCase.DelayTimingInfo:
+          DelayTimingInfo = other.DelayTimingInfo.Clone();
+          break;
+        case TimingInfoOneofCase.GpsEpochTimingInfo:
+          GpsEpochTimingInfo = other.GpsEpochTimingInfo.Clone();
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxInfoLegacy Clone() {
+      return new DownlinkTxInfoLegacy(this);
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 1;
+    private pb::ByteString gatewayId_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID.
+    /// Deprecated: replaced by gateway_id in DownlinkFrame.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "frequency" field.</summary>
+    public const int FrequencyFieldNumber = 5;
+    private uint frequency_;
+    /// <summary>
+    /// TX frequency (in Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Frequency {
+      get { return frequency_; }
+      set {
+        frequency_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "power" field.</summary>
+    public const int PowerFieldNumber = 6;
+    private int power_;
+    /// <summary>
+    /// TX power (in dBm).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int Power {
+      get { return power_; }
+      set {
+        power_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "modulation" field.</summary>
+    public const int ModulationFieldNumber = 7;
+    private global::Chirpstack.Common.Modulation modulation_ = global::Chirpstack.Common.Modulation.Lora;
+    /// <summary>
+    /// Modulation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Modulation Modulation {
+      get { return modulation_; }
+      set {
+        modulation_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "lora_modulation_info" field.</summary>
+    public const int LoraModulationInfoFieldNumber = 8;
+    /// <summary>
+    /// LoRa modulation information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.LoraModulationInfo LoraModulationInfo {
+      get { return modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo ? (global::Chirpstack.Gateway.LoraModulationInfo) modulationInfo_ : null; }
+      set {
+        modulationInfo_ = value;
+        modulationInfoCase_ = value == null ? ModulationInfoOneofCase.None : ModulationInfoOneofCase.LoraModulationInfo;
+      }
+    }
+
+    /// <summary>Field number for the "fsk_modulation_info" field.</summary>
+    public const int FskModulationInfoFieldNumber = 9;
+    /// <summary>
+    /// FSK modulation information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.FskModulationInfo FskModulationInfo {
+      get { return modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo ? (global::Chirpstack.Gateway.FskModulationInfo) modulationInfo_ : null; }
+      set {
+        modulationInfo_ = value;
+        modulationInfoCase_ = value == null ? ModulationInfoOneofCase.None : ModulationInfoOneofCase.FskModulationInfo;
+      }
+    }
+
+    /// <summary>Field number for the "board" field.</summary>
+    public const int BoardFieldNumber = 10;
+    private uint board_;
+    /// <summary>
+    /// The board identifier for emitting the frame.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Board {
+      get { return board_; }
+      set {
+        board_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "antenna" field.</summary>
+    public const int AntennaFieldNumber = 11;
+    private uint antenna_;
+    /// <summary>
+    /// The antenna identifier for emitting the frame.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Antenna {
+      get { return antenna_; }
+      set {
+        antenna_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "timing" field.</summary>
+    public const int TimingFieldNumber = 12;
+    private global::Chirpstack.Gateway.DownlinkTiming timing_ = global::Chirpstack.Gateway.DownlinkTiming.Immediately;
+    /// <summary>
+    /// Timing defines the downlink timing to use.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.DownlinkTiming Timing {
+      get { return timing_; }
+      set {
+        timing_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "immediately_timing_info" field.</summary>
+    public const int ImmediatelyTimingInfoFieldNumber = 13;
+    /// <summary>
+    /// Immediately timing information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.ImmediatelyTimingInfo ImmediatelyTimingInfo {
+      get { return timingInfoCase_ == TimingInfoOneofCase.ImmediatelyTimingInfo ? (global::Chirpstack.Gateway.ImmediatelyTimingInfo) timingInfo_ : null; }
+      set {
+        timingInfo_ = value;
+        timingInfoCase_ = value == null ? TimingInfoOneofCase.None : TimingInfoOneofCase.ImmediatelyTimingInfo;
+      }
+    }
+
+    /// <summary>Field number for the "delay_timing_info" field.</summary>
+    public const int DelayTimingInfoFieldNumber = 14;
+    /// <summary>
+    /// Context based delay timing information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.DelayTimingInfo DelayTimingInfo {
+      get { return timingInfoCase_ == TimingInfoOneofCase.DelayTimingInfo ? (global::Chirpstack.Gateway.DelayTimingInfo) timingInfo_ : null; }
+      set {
+        timingInfo_ = value;
+        timingInfoCase_ = value == null ? TimingInfoOneofCase.None : TimingInfoOneofCase.DelayTimingInfo;
+      }
+    }
+
+    /// <summary>Field number for the "gps_epoch_timing_info" field.</summary>
+    public const int GpsEpochTimingInfoFieldNumber = 15;
+    /// <summary>
+    /// GPS Epoch timing information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.GPSEpochTimingInfo GpsEpochTimingInfo {
+      get { return timingInfoCase_ == TimingInfoOneofCase.GpsEpochTimingInfo ? (global::Chirpstack.Gateway.GPSEpochTimingInfo) timingInfo_ : null; }
+      set {
+        timingInfo_ = value;
+        timingInfoCase_ = value == null ? TimingInfoOneofCase.None : TimingInfoOneofCase.GpsEpochTimingInfo;
+      }
+    }
+
+    /// <summary>Field number for the "context" field.</summary>
+    public const int ContextFieldNumber = 16;
+    private pb::ByteString context_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway specific context.
+    /// In case of a Class-A downlink, this contains a copy of the uplink context.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Context {
+      get { return context_; }
+      set {
+        context_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    private object modulationInfo_;
+    /// <summary>Enum of possible cases for the "modulation_info" oneof.</summary>
+    public enum ModulationInfoOneofCase {
+      None = 0,
+      LoraModulationInfo = 8,
+      FskModulationInfo = 9,
+    }
+    private ModulationInfoOneofCase modulationInfoCase_ = ModulationInfoOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ModulationInfoOneofCase ModulationInfoCase {
+      get { return modulationInfoCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearModulationInfo() {
+      modulationInfoCase_ = ModulationInfoOneofCase.None;
+      modulationInfo_ = null;
+    }
+
+    private object timingInfo_;
+    /// <summary>Enum of possible cases for the "timing_info" oneof.</summary>
+    public enum TimingInfoOneofCase {
+      None = 0,
+      ImmediatelyTimingInfo = 13,
+      DelayTimingInfo = 14,
+      GpsEpochTimingInfo = 15,
+    }
+    private TimingInfoOneofCase timingInfoCase_ = TimingInfoOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TimingInfoOneofCase TimingInfoCase {
+      get { return timingInfoCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearTimingInfo() {
+      timingInfoCase_ = TimingInfoOneofCase.None;
+      timingInfo_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DownlinkTxInfoLegacy);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DownlinkTxInfoLegacy other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayId != other.GatewayId) return false;
+      if (Frequency != other.Frequency) return false;
+      if (Power != other.Power) return false;
+      if (Modulation != other.Modulation) return false;
+      if (!object.Equals(LoraModulationInfo, other.LoraModulationInfo)) return false;
+      if (!object.Equals(FskModulationInfo, other.FskModulationInfo)) return false;
+      if (Board != other.Board) return false;
+      if (Antenna != other.Antenna) return false;
+      if (Timing != other.Timing) return false;
+      if (!object.Equals(ImmediatelyTimingInfo, other.ImmediatelyTimingInfo)) return false;
+      if (!object.Equals(DelayTimingInfo, other.DelayTimingInfo)) return false;
+      if (!object.Equals(GpsEpochTimingInfo, other.GpsEpochTimingInfo)) return false;
+      if (Context != other.Context) return false;
+      if (ModulationInfoCase != other.ModulationInfoCase) return false;
+      if (TimingInfoCase != other.TimingInfoCase) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (Frequency != 0) hash ^= Frequency.GetHashCode();
+      if (Power != 0) hash ^= Power.GetHashCode();
+      if (Modulation != global::Chirpstack.Common.Modulation.Lora) hash ^= Modulation.GetHashCode();
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) hash ^= LoraModulationInfo.GetHashCode();
+      if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) hash ^= FskModulationInfo.GetHashCode();
+      if (Board != 0) hash ^= Board.GetHashCode();
+      if (Antenna != 0) hash ^= Antenna.GetHashCode();
+      if (Timing != global::Chirpstack.Gateway.DownlinkTiming.Immediately) hash ^= Timing.GetHashCode();
+      if (timingInfoCase_ == TimingInfoOneofCase.ImmediatelyTimingInfo) hash ^= ImmediatelyTimingInfo.GetHashCode();
+      if (timingInfoCase_ == TimingInfoOneofCase.DelayTimingInfo) hash ^= DelayTimingInfo.GetHashCode();
+      if (timingInfoCase_ == TimingInfoOneofCase.GpsEpochTimingInfo) hash ^= GpsEpochTimingInfo.GetHashCode();
+      if (Context.Length != 0) hash ^= Context.GetHashCode();
+      hash ^= (int) modulationInfoCase_;
+      hash ^= (int) timingInfoCase_;
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayId);
+      }
+      if (Frequency != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(Frequency);
+      }
+      if (Power != 0) {
+        output.WriteRawTag(48);
+        output.WriteInt32(Power);
+      }
+      if (Modulation != global::Chirpstack.Common.Modulation.Lora) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) Modulation);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) {
+        output.WriteRawTag(66);
+        output.WriteMessage(LoraModulationInfo);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) {
+        output.WriteRawTag(74);
+        output.WriteMessage(FskModulationInfo);
+      }
+      if (Board != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(Board);
+      }
+      if (Antenna != 0) {
+        output.WriteRawTag(88);
+        output.WriteUInt32(Antenna);
+      }
+      if (Timing != global::Chirpstack.Gateway.DownlinkTiming.Immediately) {
+        output.WriteRawTag(96);
+        output.WriteEnum((int) Timing);
+      }
+      if (timingInfoCase_ == TimingInfoOneofCase.ImmediatelyTimingInfo) {
+        output.WriteRawTag(106);
+        output.WriteMessage(ImmediatelyTimingInfo);
+      }
+      if (timingInfoCase_ == TimingInfoOneofCase.DelayTimingInfo) {
+        output.WriteRawTag(114);
+        output.WriteMessage(DelayTimingInfo);
+      }
+      if (timingInfoCase_ == TimingInfoOneofCase.GpsEpochTimingInfo) {
+        output.WriteRawTag(122);
+        output.WriteMessage(GpsEpochTimingInfo);
+      }
+      if (Context.Length != 0) {
+        output.WriteRawTag(130, 1);
+        output.WriteBytes(Context);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayId);
+      }
+      if (Frequency != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(Frequency);
+      }
+      if (Power != 0) {
+        output.WriteRawTag(48);
+        output.WriteInt32(Power);
+      }
+      if (Modulation != global::Chirpstack.Common.Modulation.Lora) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) Modulation);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) {
+        output.WriteRawTag(66);
+        output.WriteMessage(LoraModulationInfo);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) {
+        output.WriteRawTag(74);
+        output.WriteMessage(FskModulationInfo);
+      }
+      if (Board != 0) {
+        output.WriteRawTag(80);
+        output.WriteUInt32(Board);
+      }
+      if (Antenna != 0) {
+        output.WriteRawTag(88);
+        output.WriteUInt32(Antenna);
+      }
+      if (Timing != global::Chirpstack.Gateway.DownlinkTiming.Immediately) {
+        output.WriteRawTag(96);
+        output.WriteEnum((int) Timing);
+      }
+      if (timingInfoCase_ == TimingInfoOneofCase.ImmediatelyTimingInfo) {
+        output.WriteRawTag(106);
+        output.WriteMessage(ImmediatelyTimingInfo);
+      }
+      if (timingInfoCase_ == TimingInfoOneofCase.DelayTimingInfo) {
+        output.WriteRawTag(114);
+        output.WriteMessage(DelayTimingInfo);
+      }
+      if (timingInfoCase_ == TimingInfoOneofCase.GpsEpochTimingInfo) {
+        output.WriteRawTag(122);
+        output.WriteMessage(GpsEpochTimingInfo);
+      }
+      if (Context.Length != 0) {
+        output.WriteRawTag(130, 1);
+        output.WriteBytes(Context);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayId);
+      }
+      if (Frequency != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Frequency);
+      }
+      if (Power != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Power);
+      }
+      if (Modulation != global::Chirpstack.Common.Modulation.Lora) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Modulation);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LoraModulationInfo);
+      }
+      if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(FskModulationInfo);
+      }
+      if (Board != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Board);
+      }
+      if (Antenna != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Antenna);
+      }
+      if (Timing != global::Chirpstack.Gateway.DownlinkTiming.Immediately) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Timing);
+      }
+      if (timingInfoCase_ == TimingInfoOneofCase.ImmediatelyTimingInfo) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ImmediatelyTimingInfo);
+      }
+      if (timingInfoCase_ == TimingInfoOneofCase.DelayTimingInfo) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DelayTimingInfo);
+      }
+      if (timingInfoCase_ == TimingInfoOneofCase.GpsEpochTimingInfo) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(GpsEpochTimingInfo);
+      }
+      if (Context.Length != 0) {
+        size += 2 + pb::CodedOutputStream.ComputeBytesSize(Context);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DownlinkTxInfoLegacy other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.Frequency != 0) {
+        Frequency = other.Frequency;
+      }
+      if (other.Power != 0) {
+        Power = other.Power;
+      }
+      if (other.Modulation != global::Chirpstack.Common.Modulation.Lora) {
+        Modulation = other.Modulation;
+      }
+      if (other.Board != 0) {
+        Board = other.Board;
+      }
+      if (other.Antenna != 0) {
+        Antenna = other.Antenna;
+      }
+      if (other.Timing != global::Chirpstack.Gateway.DownlinkTiming.Immediately) {
+        Timing = other.Timing;
+      }
+      if (other.Context.Length != 0) {
+        Context = other.Context;
+      }
+      switch (other.ModulationInfoCase) {
+        case ModulationInfoOneofCase.LoraModulationInfo:
+          if (LoraModulationInfo == null) {
+            LoraModulationInfo = new global::Chirpstack.Gateway.LoraModulationInfo();
+          }
+          LoraModulationInfo.MergeFrom(other.LoraModulationInfo);
+          break;
+        case ModulationInfoOneofCase.FskModulationInfo:
+          if (FskModulationInfo == null) {
+            FskModulationInfo = new global::Chirpstack.Gateway.FskModulationInfo();
+          }
+          FskModulationInfo.MergeFrom(other.FskModulationInfo);
+          break;
+      }
+
+      switch (other.TimingInfoCase) {
+        case TimingInfoOneofCase.ImmediatelyTimingInfo:
+          if (ImmediatelyTimingInfo == null) {
+            ImmediatelyTimingInfo = new global::Chirpstack.Gateway.ImmediatelyTimingInfo();
+          }
+          ImmediatelyTimingInfo.MergeFrom(other.ImmediatelyTimingInfo);
+          break;
+        case TimingInfoOneofCase.DelayTimingInfo:
+          if (DelayTimingInfo == null) {
+            DelayTimingInfo = new global::Chirpstack.Gateway.DelayTimingInfo();
+          }
+          DelayTimingInfo.MergeFrom(other.DelayTimingInfo);
+          break;
+        case TimingInfoOneofCase.GpsEpochTimingInfo:
+          if (GpsEpochTimingInfo == null) {
+            GpsEpochTimingInfo = new global::Chirpstack.Gateway.GPSEpochTimingInfo();
+          }
+          GpsEpochTimingInfo.MergeFrom(other.GpsEpochTimingInfo);
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayId = input.ReadBytes();
+            break;
+          }
+          case 40: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            Power = input.ReadInt32();
+            break;
+          }
+          case 56: {
+            Modulation = (global::Chirpstack.Common.Modulation) input.ReadEnum();
+            break;
+          }
+          case 66: {
+            global::Chirpstack.Gateway.LoraModulationInfo subBuilder = new global::Chirpstack.Gateway.LoraModulationInfo();
+            if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) {
+              subBuilder.MergeFrom(LoraModulationInfo);
+            }
+            input.ReadMessage(subBuilder);
+            LoraModulationInfo = subBuilder;
+            break;
+          }
+          case 74: {
+            global::Chirpstack.Gateway.FskModulationInfo subBuilder = new global::Chirpstack.Gateway.FskModulationInfo();
+            if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) {
+              subBuilder.MergeFrom(FskModulationInfo);
+            }
+            input.ReadMessage(subBuilder);
+            FskModulationInfo = subBuilder;
+            break;
+          }
+          case 80: {
+            Board = input.ReadUInt32();
+            break;
+          }
+          case 88: {
+            Antenna = input.ReadUInt32();
+            break;
+          }
+          case 96: {
+            Timing = (global::Chirpstack.Gateway.DownlinkTiming) input.ReadEnum();
+            break;
+          }
+          case 106: {
+            global::Chirpstack.Gateway.ImmediatelyTimingInfo subBuilder = new global::Chirpstack.Gateway.ImmediatelyTimingInfo();
+            if (timingInfoCase_ == TimingInfoOneofCase.ImmediatelyTimingInfo) {
+              subBuilder.MergeFrom(ImmediatelyTimingInfo);
+            }
+            input.ReadMessage(subBuilder);
+            ImmediatelyTimingInfo = subBuilder;
+            break;
+          }
+          case 114: {
+            global::Chirpstack.Gateway.DelayTimingInfo subBuilder = new global::Chirpstack.Gateway.DelayTimingInfo();
+            if (timingInfoCase_ == TimingInfoOneofCase.DelayTimingInfo) {
+              subBuilder.MergeFrom(DelayTimingInfo);
+            }
+            input.ReadMessage(subBuilder);
+            DelayTimingInfo = subBuilder;
+            break;
+          }
+          case 122: {
+            global::Chirpstack.Gateway.GPSEpochTimingInfo subBuilder = new global::Chirpstack.Gateway.GPSEpochTimingInfo();
+            if (timingInfoCase_ == TimingInfoOneofCase.GpsEpochTimingInfo) {
+              subBuilder.MergeFrom(GpsEpochTimingInfo);
+            }
+            input.ReadMessage(subBuilder);
+            GpsEpochTimingInfo = subBuilder;
+            break;
+          }
+          case 130: {
+            Context = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayId = input.ReadBytes();
+            break;
+          }
+          case 40: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            Power = input.ReadInt32();
+            break;
+          }
+          case 56: {
+            Modulation = (global::Chirpstack.Common.Modulation) input.ReadEnum();
+            break;
+          }
+          case 66: {
+            global::Chirpstack.Gateway.LoraModulationInfo subBuilder = new global::Chirpstack.Gateway.LoraModulationInfo();
+            if (modulationInfoCase_ == ModulationInfoOneofCase.LoraModulationInfo) {
+              subBuilder.MergeFrom(LoraModulationInfo);
+            }
+            input.ReadMessage(subBuilder);
+            LoraModulationInfo = subBuilder;
+            break;
+          }
+          case 74: {
+            global::Chirpstack.Gateway.FskModulationInfo subBuilder = new global::Chirpstack.Gateway.FskModulationInfo();
+            if (modulationInfoCase_ == ModulationInfoOneofCase.FskModulationInfo) {
+              subBuilder.MergeFrom(FskModulationInfo);
+            }
+            input.ReadMessage(subBuilder);
+            FskModulationInfo = subBuilder;
+            break;
+          }
+          case 80: {
+            Board = input.ReadUInt32();
+            break;
+          }
+          case 88: {
+            Antenna = input.ReadUInt32();
+            break;
+          }
+          case 96: {
+            Timing = (global::Chirpstack.Gateway.DownlinkTiming) input.ReadEnum();
+            break;
+          }
+          case 106: {
+            global::Chirpstack.Gateway.ImmediatelyTimingInfo subBuilder = new global::Chirpstack.Gateway.ImmediatelyTimingInfo();
+            if (timingInfoCase_ == TimingInfoOneofCase.ImmediatelyTimingInfo) {
+              subBuilder.MergeFrom(ImmediatelyTimingInfo);
+            }
+            input.ReadMessage(subBuilder);
+            ImmediatelyTimingInfo = subBuilder;
+            break;
+          }
+          case 114: {
+            global::Chirpstack.Gateway.DelayTimingInfo subBuilder = new global::Chirpstack.Gateway.DelayTimingInfo();
+            if (timingInfoCase_ == TimingInfoOneofCase.DelayTimingInfo) {
+              subBuilder.MergeFrom(DelayTimingInfo);
+            }
+            input.ReadMessage(subBuilder);
+            DelayTimingInfo = subBuilder;
+            break;
+          }
+          case 122: {
+            global::Chirpstack.Gateway.GPSEpochTimingInfo subBuilder = new global::Chirpstack.Gateway.GPSEpochTimingInfo();
+            if (timingInfoCase_ == TimingInfoOneofCase.GpsEpochTimingInfo) {
+              subBuilder.MergeFrom(GpsEpochTimingInfo);
+            }
+            input.ReadMessage(subBuilder);
+            GpsEpochTimingInfo = subBuilder;
+            break;
+          }
+          case 130: {
+            Context = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DownlinkTxInfo : pb::IMessage<DownlinkTxInfo>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DownlinkTxInfo> _parser = new pb::MessageParser<DownlinkTxInfo>(() => new DownlinkTxInfo());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DownlinkTxInfo> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[13]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxInfo() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxInfo(DownlinkTxInfo other) : this() {
+      frequency_ = other.frequency_;
+      power_ = other.power_;
+      modulation_ = other.modulation_ != null ? other.modulation_.Clone() : null;
+      board_ = other.board_;
+      antenna_ = other.antenna_;
+      timing_ = other.timing_ != null ? other.timing_.Clone() : null;
+      context_ = other.context_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxInfo Clone() {
+      return new DownlinkTxInfo(this);
+    }
+
+    /// <summary>Field number for the "frequency" field.</summary>
+    public const int FrequencyFieldNumber = 1;
+    private uint frequency_;
+    /// <summary>
+    /// TX frequency (in Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Frequency {
+      get { return frequency_; }
+      set {
+        frequency_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "power" field.</summary>
+    public const int PowerFieldNumber = 2;
+    private int power_;
+    /// <summary>
+    /// TX power (in dBm).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int Power {
+      get { return power_; }
+      set {
+        power_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "modulation" field.</summary>
+    public const int ModulationFieldNumber = 3;
+    private global::Chirpstack.Gateway.Modulation modulation_;
+    /// <summary>
+    /// Modulation.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.Modulation Modulation {
+      get { return modulation_; }
+      set {
+        modulation_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "board" field.</summary>
+    public const int BoardFieldNumber = 4;
+    private uint board_;
+    /// <summary>
+    /// The board identifier for emitting the frame.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Board {
+      get { return board_; }
+      set {
+        board_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "antenna" field.</summary>
+    public const int AntennaFieldNumber = 5;
+    private uint antenna_;
+    /// <summary>
+    /// The antenna identifier for emitting the frame.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Antenna {
+      get { return antenna_; }
+      set {
+        antenna_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "timing" field.</summary>
+    public const int TimingFieldNumber = 6;
+    private global::Chirpstack.Gateway.Timing timing_;
+    /// <summary>
+    /// Timing.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.Timing Timing {
+      get { return timing_; }
+      set {
+        timing_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "context" field.</summary>
+    public const int ContextFieldNumber = 7;
+    private pb::ByteString context_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway specific context.
+    /// In case of a Class-A downlink, this contains a copy of the uplink context.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Context {
+      get { return context_; }
+      set {
+        context_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DownlinkTxInfo);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DownlinkTxInfo other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Frequency != other.Frequency) return false;
+      if (Power != other.Power) return false;
+      if (!object.Equals(Modulation, other.Modulation)) return false;
+      if (Board != other.Board) return false;
+      if (Antenna != other.Antenna) return false;
+      if (!object.Equals(Timing, other.Timing)) return false;
+      if (Context != other.Context) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Frequency != 0) hash ^= Frequency.GetHashCode();
+      if (Power != 0) hash ^= Power.GetHashCode();
+      if (modulation_ != null) hash ^= Modulation.GetHashCode();
+      if (Board != 0) hash ^= Board.GetHashCode();
+      if (Antenna != 0) hash ^= Antenna.GetHashCode();
+      if (timing_ != null) hash ^= Timing.GetHashCode();
+      if (Context.Length != 0) hash ^= Context.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Frequency != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Frequency);
+      }
+      if (Power != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Power);
+      }
+      if (modulation_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(Modulation);
+      }
+      if (Board != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(Board);
+      }
+      if (Antenna != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(Antenna);
+      }
+      if (timing_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Timing);
+      }
+      if (Context.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteBytes(Context);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Frequency != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Frequency);
+      }
+      if (Power != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(Power);
+      }
+      if (modulation_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(Modulation);
+      }
+      if (Board != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(Board);
+      }
+      if (Antenna != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(Antenna);
+      }
+      if (timing_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Timing);
+      }
+      if (Context.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteBytes(Context);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Frequency != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Frequency);
+      }
+      if (Power != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Power);
+      }
+      if (modulation_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Modulation);
+      }
+      if (Board != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Board);
+      }
+      if (Antenna != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Antenna);
+      }
+      if (timing_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Timing);
+      }
+      if (Context.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Context);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DownlinkTxInfo other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Frequency != 0) {
+        Frequency = other.Frequency;
+      }
+      if (other.Power != 0) {
+        Power = other.Power;
+      }
+      if (other.modulation_ != null) {
+        if (modulation_ == null) {
+          Modulation = new global::Chirpstack.Gateway.Modulation();
+        }
+        Modulation.MergeFrom(other.Modulation);
+      }
+      if (other.Board != 0) {
+        Board = other.Board;
+      }
+      if (other.Antenna != 0) {
+        Antenna = other.Antenna;
+      }
+      if (other.timing_ != null) {
+        if (timing_ == null) {
+          Timing = new global::Chirpstack.Gateway.Timing();
+        }
+        Timing.MergeFrom(other.Timing);
+      }
+      if (other.Context.Length != 0) {
+        Context = other.Context;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Power = input.ReadInt32();
+            break;
+          }
+          case 26: {
+            if (modulation_ == null) {
+              Modulation = new global::Chirpstack.Gateway.Modulation();
+            }
+            input.ReadMessage(Modulation);
+            break;
+          }
+          case 32: {
+            Board = input.ReadUInt32();
+            break;
+          }
+          case 40: {
+            Antenna = input.ReadUInt32();
+            break;
+          }
+          case 50: {
+            if (timing_ == null) {
+              Timing = new global::Chirpstack.Gateway.Timing();
+            }
+            input.ReadMessage(Timing);
+            break;
+          }
+          case 58: {
+            Context = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Power = input.ReadInt32();
+            break;
+          }
+          case 26: {
+            if (modulation_ == null) {
+              Modulation = new global::Chirpstack.Gateway.Modulation();
+            }
+            input.ReadMessage(Modulation);
+            break;
+          }
+          case 32: {
+            Board = input.ReadUInt32();
+            break;
+          }
+          case 40: {
+            Antenna = input.ReadUInt32();
+            break;
+          }
+          case 50: {
+            if (timing_ == null) {
+              Timing = new global::Chirpstack.Gateway.Timing();
+            }
+            input.ReadMessage(Timing);
+            break;
+          }
+          case 58: {
+            Context = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class Timing : pb::IMessage<Timing>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<Timing> _parser = new pb::MessageParser<Timing>(() => new Timing());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<Timing> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[14]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Timing() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Timing(Timing other) : this() {
+      switch (other.ParametersCase) {
+        case ParametersOneofCase.Immediately:
+          Immediately = other.Immediately.Clone();
+          break;
+        case ParametersOneofCase.Delay:
+          Delay = other.Delay.Clone();
+          break;
+        case ParametersOneofCase.GpsEpoch:
+          GpsEpoch = other.GpsEpoch.Clone();
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public Timing Clone() {
+      return new Timing(this);
+    }
+
+    /// <summary>Field number for the "immediately" field.</summary>
+    public const int ImmediatelyFieldNumber = 1;
+    /// <summary>
+    /// Immediately timing information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.ImmediatelyTimingInfo Immediately {
+      get { return parametersCase_ == ParametersOneofCase.Immediately ? (global::Chirpstack.Gateway.ImmediatelyTimingInfo) parameters_ : null; }
+      set {
+        parameters_ = value;
+        parametersCase_ = value == null ? ParametersOneofCase.None : ParametersOneofCase.Immediately;
+      }
+    }
+
+    /// <summary>Field number for the "delay" field.</summary>
+    public const int DelayFieldNumber = 2;
+    /// <summary>
+    /// Context based delay timing information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.DelayTimingInfo Delay {
+      get { return parametersCase_ == ParametersOneofCase.Delay ? (global::Chirpstack.Gateway.DelayTimingInfo) parameters_ : null; }
+      set {
+        parameters_ = value;
+        parametersCase_ = value == null ? ParametersOneofCase.None : ParametersOneofCase.Delay;
+      }
+    }
+
+    /// <summary>Field number for the "gps_epoch" field.</summary>
+    public const int GpsEpochFieldNumber = 3;
+    /// <summary>
+    /// GPS Epoch timing information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.GPSEpochTimingInfo GpsEpoch {
+      get { return parametersCase_ == ParametersOneofCase.GpsEpoch ? (global::Chirpstack.Gateway.GPSEpochTimingInfo) parameters_ : null; }
+      set {
+        parameters_ = value;
+        parametersCase_ = value == null ? ParametersOneofCase.None : ParametersOneofCase.GpsEpoch;
+      }
+    }
+
+    private object parameters_;
+    /// <summary>Enum of possible cases for the "parameters" oneof.</summary>
+    public enum ParametersOneofCase {
+      None = 0,
+      Immediately = 1,
+      Delay = 2,
+      GpsEpoch = 3,
+    }
+    private ParametersOneofCase parametersCase_ = ParametersOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ParametersOneofCase ParametersCase {
+      get { return parametersCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearParameters() {
+      parametersCase_ = ParametersOneofCase.None;
+      parameters_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as Timing);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(Timing other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Immediately, other.Immediately)) return false;
+      if (!object.Equals(Delay, other.Delay)) return false;
+      if (!object.Equals(GpsEpoch, other.GpsEpoch)) return false;
+      if (ParametersCase != other.ParametersCase) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (parametersCase_ == ParametersOneofCase.Immediately) hash ^= Immediately.GetHashCode();
+      if (parametersCase_ == ParametersOneofCase.Delay) hash ^= Delay.GetHashCode();
+      if (parametersCase_ == ParametersOneofCase.GpsEpoch) hash ^= GpsEpoch.GetHashCode();
+      hash ^= (int) parametersCase_;
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (parametersCase_ == ParametersOneofCase.Immediately) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Immediately);
+      }
+      if (parametersCase_ == ParametersOneofCase.Delay) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Delay);
+      }
+      if (parametersCase_ == ParametersOneofCase.GpsEpoch) {
+        output.WriteRawTag(26);
+        output.WriteMessage(GpsEpoch);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (parametersCase_ == ParametersOneofCase.Immediately) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Immediately);
+      }
+      if (parametersCase_ == ParametersOneofCase.Delay) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Delay);
+      }
+      if (parametersCase_ == ParametersOneofCase.GpsEpoch) {
+        output.WriteRawTag(26);
+        output.WriteMessage(GpsEpoch);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (parametersCase_ == ParametersOneofCase.Immediately) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Immediately);
+      }
+      if (parametersCase_ == ParametersOneofCase.Delay) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Delay);
+      }
+      if (parametersCase_ == ParametersOneofCase.GpsEpoch) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(GpsEpoch);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(Timing other) {
+      if (other == null) {
+        return;
+      }
+      switch (other.ParametersCase) {
+        case ParametersOneofCase.Immediately:
+          if (Immediately == null) {
+            Immediately = new global::Chirpstack.Gateway.ImmediatelyTimingInfo();
+          }
+          Immediately.MergeFrom(other.Immediately);
+          break;
+        case ParametersOneofCase.Delay:
+          if (Delay == null) {
+            Delay = new global::Chirpstack.Gateway.DelayTimingInfo();
+          }
+          Delay.MergeFrom(other.Delay);
+          break;
+        case ParametersOneofCase.GpsEpoch:
+          if (GpsEpoch == null) {
+            GpsEpoch = new global::Chirpstack.Gateway.GPSEpochTimingInfo();
+          }
+          GpsEpoch.MergeFrom(other.GpsEpoch);
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            global::Chirpstack.Gateway.ImmediatelyTimingInfo subBuilder = new global::Chirpstack.Gateway.ImmediatelyTimingInfo();
+            if (parametersCase_ == ParametersOneofCase.Immediately) {
+              subBuilder.MergeFrom(Immediately);
+            }
+            input.ReadMessage(subBuilder);
+            Immediately = subBuilder;
+            break;
+          }
+          case 18: {
+            global::Chirpstack.Gateway.DelayTimingInfo subBuilder = new global::Chirpstack.Gateway.DelayTimingInfo();
+            if (parametersCase_ == ParametersOneofCase.Delay) {
+              subBuilder.MergeFrom(Delay);
+            }
+            input.ReadMessage(subBuilder);
+            Delay = subBuilder;
+            break;
+          }
+          case 26: {
+            global::Chirpstack.Gateway.GPSEpochTimingInfo subBuilder = new global::Chirpstack.Gateway.GPSEpochTimingInfo();
+            if (parametersCase_ == ParametersOneofCase.GpsEpoch) {
+              subBuilder.MergeFrom(GpsEpoch);
+            }
+            input.ReadMessage(subBuilder);
+            GpsEpoch = subBuilder;
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            global::Chirpstack.Gateway.ImmediatelyTimingInfo subBuilder = new global::Chirpstack.Gateway.ImmediatelyTimingInfo();
+            if (parametersCase_ == ParametersOneofCase.Immediately) {
+              subBuilder.MergeFrom(Immediately);
+            }
+            input.ReadMessage(subBuilder);
+            Immediately = subBuilder;
+            break;
+          }
+          case 18: {
+            global::Chirpstack.Gateway.DelayTimingInfo subBuilder = new global::Chirpstack.Gateway.DelayTimingInfo();
+            if (parametersCase_ == ParametersOneofCase.Delay) {
+              subBuilder.MergeFrom(Delay);
+            }
+            input.ReadMessage(subBuilder);
+            Delay = subBuilder;
+            break;
+          }
+          case 26: {
+            global::Chirpstack.Gateway.GPSEpochTimingInfo subBuilder = new global::Chirpstack.Gateway.GPSEpochTimingInfo();
+            if (parametersCase_ == ParametersOneofCase.GpsEpoch) {
+              subBuilder.MergeFrom(GpsEpoch);
+            }
+            input.ReadMessage(subBuilder);
+            GpsEpoch = subBuilder;
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// Not implemented yet.
+  /// </summary>
+  public sealed partial class ImmediatelyTimingInfo : pb::IMessage<ImmediatelyTimingInfo>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ImmediatelyTimingInfo> _parser = new pb::MessageParser<ImmediatelyTimingInfo>(() => new ImmediatelyTimingInfo());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ImmediatelyTimingInfo> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[15]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ImmediatelyTimingInfo() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ImmediatelyTimingInfo(ImmediatelyTimingInfo other) : this() {
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ImmediatelyTimingInfo Clone() {
+      return new ImmediatelyTimingInfo(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ImmediatelyTimingInfo);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ImmediatelyTimingInfo other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ImmediatelyTimingInfo other) {
+      if (other == null) {
+        return;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DelayTimingInfo : pb::IMessage<DelayTimingInfo>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DelayTimingInfo> _parser = new pb::MessageParser<DelayTimingInfo>(() => new DelayTimingInfo());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DelayTimingInfo> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[16]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DelayTimingInfo() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DelayTimingInfo(DelayTimingInfo other) : this() {
+      delay_ = other.delay_ != null ? other.delay_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DelayTimingInfo Clone() {
+      return new DelayTimingInfo(this);
+    }
+
+    /// <summary>Field number for the "delay" field.</summary>
+    public const int DelayFieldNumber = 1;
+    private global::Google.Protobuf.WellKnownTypes.Duration delay_;
+    /// <summary>
+    /// Delay (duration).
+    /// The delay will be added to the gateway internal timing, provided by the context object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Duration Delay {
+      get { return delay_; }
+      set {
+        delay_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DelayTimingInfo);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DelayTimingInfo other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Delay, other.Delay)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (delay_ != null) hash ^= Delay.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (delay_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Delay);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (delay_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Delay);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (delay_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Delay);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DelayTimingInfo other) {
+      if (other == null) {
+        return;
+      }
+      if (other.delay_ != null) {
+        if (delay_ == null) {
+          Delay = new global::Google.Protobuf.WellKnownTypes.Duration();
+        }
+        Delay.MergeFrom(other.Delay);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (delay_ == null) {
+              Delay = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(Delay);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (delay_ == null) {
+              Delay = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(Delay);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GPSEpochTimingInfo : pb::IMessage<GPSEpochTimingInfo>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GPSEpochTimingInfo> _parser = new pb::MessageParser<GPSEpochTimingInfo>(() => new GPSEpochTimingInfo());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GPSEpochTimingInfo> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[17]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GPSEpochTimingInfo() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GPSEpochTimingInfo(GPSEpochTimingInfo other) : this() {
+      timeSinceGpsEpoch_ = other.timeSinceGpsEpoch_ != null ? other.timeSinceGpsEpoch_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GPSEpochTimingInfo Clone() {
+      return new GPSEpochTimingInfo(this);
+    }
+
+    /// <summary>Field number for the "time_since_gps_epoch" field.</summary>
+    public const int TimeSinceGpsEpochFieldNumber = 1;
+    private global::Google.Protobuf.WellKnownTypes.Duration timeSinceGpsEpoch_;
+    /// <summary>
+    /// Duration since GPS Epoch.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Duration TimeSinceGpsEpoch {
+      get { return timeSinceGpsEpoch_; }
+      set {
+        timeSinceGpsEpoch_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GPSEpochTimingInfo);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GPSEpochTimingInfo other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(TimeSinceGpsEpoch, other.TimeSinceGpsEpoch)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (timeSinceGpsEpoch_ != null) hash ^= TimeSinceGpsEpoch.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (timeSinceGpsEpoch_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(TimeSinceGpsEpoch);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (timeSinceGpsEpoch_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(TimeSinceGpsEpoch);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (timeSinceGpsEpoch_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TimeSinceGpsEpoch);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GPSEpochTimingInfo other) {
+      if (other == null) {
+        return;
+      }
+      if (other.timeSinceGpsEpoch_ != null) {
+        if (timeSinceGpsEpoch_ == null) {
+          TimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+        }
+        TimeSinceGpsEpoch.MergeFrom(other.TimeSinceGpsEpoch);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (timeSinceGpsEpoch_ == null) {
+              TimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(TimeSinceGpsEpoch);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (timeSinceGpsEpoch_ == null) {
+              TimeSinceGpsEpoch = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(TimeSinceGpsEpoch);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UplinkFrame : pb::IMessage<UplinkFrame>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UplinkFrame> _parser = new pb::MessageParser<UplinkFrame>(() => new UplinkFrame());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UplinkFrame> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[18]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkFrame() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkFrame(UplinkFrame other) : this() {
+      phyPayload_ = other.phyPayload_;
+      txInfoLegacy_ = other.txInfoLegacy_ != null ? other.txInfoLegacy_.Clone() : null;
+      rxInfoLegacy_ = other.rxInfoLegacy_ != null ? other.rxInfoLegacy_.Clone() : null;
+      txInfo_ = other.txInfo_ != null ? other.txInfo_.Clone() : null;
+      rxInfo_ = other.rxInfo_ != null ? other.rxInfo_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkFrame Clone() {
+      return new UplinkFrame(this);
+    }
+
+    /// <summary>Field number for the "phy_payload" field.</summary>
+    public const int PhyPayloadFieldNumber = 1;
+    private pb::ByteString phyPayload_ = pb::ByteString.Empty;
+    /// <summary>
+    /// PHYPayload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString PhyPayload {
+      get { return phyPayload_; }
+      set {
+        phyPayload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tx_info_legacy" field.</summary>
+    public const int TxInfoLegacyFieldNumber = 2;
+    private global::Chirpstack.Gateway.UplinkTxInfoLegacy txInfoLegacy_;
+    /// <summary>
+    /// TX meta-data (deprecated).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.UplinkTxInfoLegacy TxInfoLegacy {
+      get { return txInfoLegacy_; }
+      set {
+        txInfoLegacy_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_info_legacy" field.</summary>
+    public const int RxInfoLegacyFieldNumber = 3;
+    private global::Chirpstack.Gateway.UplinkRxInfoLegacy rxInfoLegacy_;
+    /// <summary>
+    /// RX meta-data (deprecated).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.UplinkRxInfoLegacy RxInfoLegacy {
+      get { return rxInfoLegacy_; }
+      set {
+        rxInfoLegacy_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tx_info" field.</summary>
+    public const int TxInfoFieldNumber = 4;
+    private global::Chirpstack.Gateway.UplinkTxInfo txInfo_;
+    /// <summary>
+    /// Tx meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.UplinkTxInfo TxInfo {
+      get { return txInfo_; }
+      set {
+        txInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_info" field.</summary>
+    public const int RxInfoFieldNumber = 5;
+    private global::Chirpstack.Gateway.UplinkRxInfo rxInfo_;
+    /// <summary>
+    /// Rx meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.UplinkRxInfo RxInfo {
+      get { return rxInfo_; }
+      set {
+        rxInfo_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UplinkFrame);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UplinkFrame other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (PhyPayload != other.PhyPayload) return false;
+      if (!object.Equals(TxInfoLegacy, other.TxInfoLegacy)) return false;
+      if (!object.Equals(RxInfoLegacy, other.RxInfoLegacy)) return false;
+      if (!object.Equals(TxInfo, other.TxInfo)) return false;
+      if (!object.Equals(RxInfo, other.RxInfo)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (PhyPayload.Length != 0) hash ^= PhyPayload.GetHashCode();
+      if (txInfoLegacy_ != null) hash ^= TxInfoLegacy.GetHashCode();
+      if (rxInfoLegacy_ != null) hash ^= RxInfoLegacy.GetHashCode();
+      if (txInfo_ != null) hash ^= TxInfo.GetHashCode();
+      if (rxInfo_ != null) hash ^= RxInfo.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (PhyPayload.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(PhyPayload);
+      }
+      if (txInfoLegacy_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxInfoLegacy);
+      }
+      if (rxInfoLegacy_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(RxInfoLegacy);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(TxInfo);
+      }
+      if (rxInfo_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(RxInfo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (PhyPayload.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(PhyPayload);
+      }
+      if (txInfoLegacy_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxInfoLegacy);
+      }
+      if (rxInfoLegacy_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(RxInfoLegacy);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(TxInfo);
+      }
+      if (rxInfo_ != null) {
+        output.WriteRawTag(42);
+        output.WriteMessage(RxInfo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (PhyPayload.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(PhyPayload);
+      }
+      if (txInfoLegacy_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfoLegacy);
+      }
+      if (rxInfoLegacy_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(RxInfoLegacy);
+      }
+      if (txInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfo);
+      }
+      if (rxInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(RxInfo);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UplinkFrame other) {
+      if (other == null) {
+        return;
+      }
+      if (other.PhyPayload.Length != 0) {
+        PhyPayload = other.PhyPayload;
+      }
+      if (other.txInfoLegacy_ != null) {
+        if (txInfoLegacy_ == null) {
+          TxInfoLegacy = new global::Chirpstack.Gateway.UplinkTxInfoLegacy();
+        }
+        TxInfoLegacy.MergeFrom(other.TxInfoLegacy);
+      }
+      if (other.rxInfoLegacy_ != null) {
+        if (rxInfoLegacy_ == null) {
+          RxInfoLegacy = new global::Chirpstack.Gateway.UplinkRxInfoLegacy();
+        }
+        RxInfoLegacy.MergeFrom(other.RxInfoLegacy);
+      }
+      if (other.txInfo_ != null) {
+        if (txInfo_ == null) {
+          TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+        }
+        TxInfo.MergeFrom(other.TxInfo);
+      }
+      if (other.rxInfo_ != null) {
+        if (rxInfo_ == null) {
+          RxInfo = new global::Chirpstack.Gateway.UplinkRxInfo();
+        }
+        RxInfo.MergeFrom(other.RxInfo);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            PhyPayload = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (txInfoLegacy_ == null) {
+              TxInfoLegacy = new global::Chirpstack.Gateway.UplinkTxInfoLegacy();
+            }
+            input.ReadMessage(TxInfoLegacy);
+            break;
+          }
+          case 26: {
+            if (rxInfoLegacy_ == null) {
+              RxInfoLegacy = new global::Chirpstack.Gateway.UplinkRxInfoLegacy();
+            }
+            input.ReadMessage(RxInfoLegacy);
+            break;
+          }
+          case 34: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 42: {
+            if (rxInfo_ == null) {
+              RxInfo = new global::Chirpstack.Gateway.UplinkRxInfo();
+            }
+            input.ReadMessage(RxInfo);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            PhyPayload = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (txInfoLegacy_ == null) {
+              TxInfoLegacy = new global::Chirpstack.Gateway.UplinkTxInfoLegacy();
+            }
+            input.ReadMessage(TxInfoLegacy);
+            break;
+          }
+          case 26: {
+            if (rxInfoLegacy_ == null) {
+              RxInfoLegacy = new global::Chirpstack.Gateway.UplinkRxInfoLegacy();
+            }
+            input.ReadMessage(RxInfoLegacy);
+            break;
+          }
+          case 34: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 42: {
+            if (rxInfo_ == null) {
+              RxInfo = new global::Chirpstack.Gateway.UplinkRxInfo();
+            }
+            input.ReadMessage(RxInfo);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class UplinkFrameSet : pb::IMessage<UplinkFrameSet>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UplinkFrameSet> _parser = new pb::MessageParser<UplinkFrameSet>(() => new UplinkFrameSet());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UplinkFrameSet> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[19]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkFrameSet() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkFrameSet(UplinkFrameSet other) : this() {
+      phyPayload_ = other.phyPayload_;
+      txInfo_ = other.txInfo_ != null ? other.txInfo_.Clone() : null;
+      rxInfo_ = other.rxInfo_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkFrameSet Clone() {
+      return new UplinkFrameSet(this);
+    }
+
+    /// <summary>Field number for the "phy_payload" field.</summary>
+    public const int PhyPayloadFieldNumber = 1;
+    private pb::ByteString phyPayload_ = pb::ByteString.Empty;
+    /// <summary>
+    /// PHYPayload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString PhyPayload {
+      get { return phyPayload_; }
+      set {
+        phyPayload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tx_info" field.</summary>
+    public const int TxInfoFieldNumber = 2;
+    private global::Chirpstack.Gateway.UplinkTxInfo txInfo_;
+    /// <summary>
+    /// TX meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.UplinkTxInfo TxInfo {
+      get { return txInfo_; }
+      set {
+        txInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_info" field.</summary>
+    public const int RxInfoFieldNumber = 3;
+    private static readonly pb::FieldCodec<global::Chirpstack.Gateway.UplinkRxInfo> _repeated_rxInfo_codec
+        = pb::FieldCodec.ForMessage(26, global::Chirpstack.Gateway.UplinkRxInfo.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo> rxInfo_ = new pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo>();
+    /// <summary>
+    /// RX meta-data set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo> RxInfo {
+      get { return rxInfo_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UplinkFrameSet);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UplinkFrameSet other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (PhyPayload != other.PhyPayload) return false;
+      if (!object.Equals(TxInfo, other.TxInfo)) return false;
+      if(!rxInfo_.Equals(other.rxInfo_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (PhyPayload.Length != 0) hash ^= PhyPayload.GetHashCode();
+      if (txInfo_ != null) hash ^= TxInfo.GetHashCode();
+      hash ^= rxInfo_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (PhyPayload.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(PhyPayload);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxInfo);
+      }
+      rxInfo_.WriteTo(output, _repeated_rxInfo_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (PhyPayload.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(PhyPayload);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxInfo);
+      }
+      rxInfo_.WriteTo(ref output, _repeated_rxInfo_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (PhyPayload.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(PhyPayload);
+      }
+      if (txInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfo);
+      }
+      size += rxInfo_.CalculateSize(_repeated_rxInfo_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UplinkFrameSet other) {
+      if (other == null) {
+        return;
+      }
+      if (other.PhyPayload.Length != 0) {
+        PhyPayload = other.PhyPayload;
+      }
+      if (other.txInfo_ != null) {
+        if (txInfo_ == null) {
+          TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+        }
+        TxInfo.MergeFrom(other.TxInfo);
+      }
+      rxInfo_.Add(other.rxInfo_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            PhyPayload = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 26: {
+            rxInfo_.AddEntriesFrom(input, _repeated_rxInfo_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            PhyPayload = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 26: {
+            rxInfo_.AddEntriesFrom(ref input, _repeated_rxInfo_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DownlinkFrame : pb::IMessage<DownlinkFrame>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DownlinkFrame> _parser = new pb::MessageParser<DownlinkFrame>(() => new DownlinkFrame());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DownlinkFrame> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[20]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkFrame() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkFrame(DownlinkFrame other) : this() {
+      downlinkId_ = other.downlinkId_;
+      downlinkIdLegacy_ = other.downlinkIdLegacy_;
+      items_ = other.items_.Clone();
+      gatewayIdLegacy_ = other.gatewayIdLegacy_;
+      gatewayId_ = other.gatewayId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkFrame Clone() {
+      return new DownlinkFrame(this);
+    }
+
+    /// <summary>Field number for the "downlink_id" field.</summary>
+    public const int DownlinkIdFieldNumber = 3;
+    private uint downlinkId_;
+    /// <summary>
+    /// Downlink ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint DownlinkId {
+      get { return downlinkId_; }
+      set {
+        downlinkId_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "downlink_id_legacy" field.</summary>
+    public const int DownlinkIdLegacyFieldNumber = 4;
+    private pb::ByteString downlinkIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Downlink ID (UUID).
+    /// Deprecated: use downlink_id.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString DownlinkIdLegacy {
+      get { return downlinkIdLegacy_; }
+      set {
+        downlinkIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "items" field.</summary>
+    public const int ItemsFieldNumber = 5;
+    private static readonly pb::FieldCodec<global::Chirpstack.Gateway.DownlinkFrameItem> _repeated_items_codec
+        = pb::FieldCodec.ForMessage(42, global::Chirpstack.Gateway.DownlinkFrameItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Gateway.DownlinkFrameItem> items_ = new pbc::RepeatedField<global::Chirpstack.Gateway.DownlinkFrameItem>();
+    /// <summary>
+    /// Downlink frame items.
+    /// This makes it possible to send multiple downlink opportunities to the
+    /// gateway at once (e.g. RX1 and RX2 in LoRaWAN). The first item has the
+    /// highest priority, the last the lowest. The gateway will emit at most
+    /// one item.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Gateway.DownlinkFrameItem> Items {
+      get { return items_; }
+    }
+
+    /// <summary>Field number for the "gateway_id_legacy" field.</summary>
+    public const int GatewayIdLegacyFieldNumber = 6;
+    private pb::ByteString gatewayIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID.
+    /// Deprecated: use gateway_id
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayIdLegacy {
+      get { return gatewayIdLegacy_; }
+      set {
+        gatewayIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 7;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DownlinkFrame);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DownlinkFrame other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DownlinkId != other.DownlinkId) return false;
+      if (DownlinkIdLegacy != other.DownlinkIdLegacy) return false;
+      if(!items_.Equals(other.items_)) return false;
+      if (GatewayIdLegacy != other.GatewayIdLegacy) return false;
+      if (GatewayId != other.GatewayId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DownlinkId != 0) hash ^= DownlinkId.GetHashCode();
+      if (DownlinkIdLegacy.Length != 0) hash ^= DownlinkIdLegacy.GetHashCode();
+      hash ^= items_.GetHashCode();
+      if (GatewayIdLegacy.Length != 0) hash ^= GatewayIdLegacy.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DownlinkId != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(DownlinkId);
+      }
+      if (DownlinkIdLegacy.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteBytes(DownlinkIdLegacy);
+      }
+      items_.WriteTo(output, _repeated_items_codec);
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DownlinkId != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(DownlinkId);
+      }
+      if (DownlinkIdLegacy.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteBytes(DownlinkIdLegacy);
+      }
+      items_.WriteTo(ref output, _repeated_items_codec);
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DownlinkId != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(DownlinkId);
+      }
+      if (DownlinkIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(DownlinkIdLegacy);
+      }
+      size += items_.CalculateSize(_repeated_items_codec);
+      if (GatewayIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DownlinkFrame other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DownlinkId != 0) {
+        DownlinkId = other.DownlinkId;
+      }
+      if (other.DownlinkIdLegacy.Length != 0) {
+        DownlinkIdLegacy = other.DownlinkIdLegacy;
+      }
+      items_.Add(other.items_);
+      if (other.GatewayIdLegacy.Length != 0) {
+        GatewayIdLegacy = other.GatewayIdLegacy;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 24: {
+            DownlinkId = input.ReadUInt32();
+            break;
+          }
+          case 34: {
+            DownlinkIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 42: {
+            items_.AddEntriesFrom(input, _repeated_items_codec);
+            break;
+          }
+          case 50: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 58: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 24: {
+            DownlinkId = input.ReadUInt32();
+            break;
+          }
+          case 34: {
+            DownlinkIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 42: {
+            items_.AddEntriesFrom(ref input, _repeated_items_codec);
+            break;
+          }
+          case 50: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 58: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DownlinkFrameItem : pb::IMessage<DownlinkFrameItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DownlinkFrameItem> _parser = new pb::MessageParser<DownlinkFrameItem>(() => new DownlinkFrameItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DownlinkFrameItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[21]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkFrameItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkFrameItem(DownlinkFrameItem other) : this() {
+      phyPayload_ = other.phyPayload_;
+      txInfoLegacy_ = other.txInfoLegacy_ != null ? other.txInfoLegacy_.Clone() : null;
+      txInfo_ = other.txInfo_ != null ? other.txInfo_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkFrameItem Clone() {
+      return new DownlinkFrameItem(this);
+    }
+
+    /// <summary>Field number for the "phy_payload" field.</summary>
+    public const int PhyPayloadFieldNumber = 1;
+    private pb::ByteString phyPayload_ = pb::ByteString.Empty;
+    /// <summary>
+    /// PHYPayload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString PhyPayload {
+      get { return phyPayload_; }
+      set {
+        phyPayload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tx_info_legacy" field.</summary>
+    public const int TxInfoLegacyFieldNumber = 2;
+    private global::Chirpstack.Gateway.DownlinkTxInfoLegacy txInfoLegacy_;
+    /// <summary>
+    /// TX meta-data (deprecated).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.DownlinkTxInfoLegacy TxInfoLegacy {
+      get { return txInfoLegacy_; }
+      set {
+        txInfoLegacy_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "tx_info" field.</summary>
+    public const int TxInfoFieldNumber = 3;
+    private global::Chirpstack.Gateway.DownlinkTxInfo txInfo_;
+    /// <summary>
+    /// Tx meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.DownlinkTxInfo TxInfo {
+      get { return txInfo_; }
+      set {
+        txInfo_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DownlinkFrameItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DownlinkFrameItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (PhyPayload != other.PhyPayload) return false;
+      if (!object.Equals(TxInfoLegacy, other.TxInfoLegacy)) return false;
+      if (!object.Equals(TxInfo, other.TxInfo)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (PhyPayload.Length != 0) hash ^= PhyPayload.GetHashCode();
+      if (txInfoLegacy_ != null) hash ^= TxInfoLegacy.GetHashCode();
+      if (txInfo_ != null) hash ^= TxInfo.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (PhyPayload.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(PhyPayload);
+      }
+      if (txInfoLegacy_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxInfoLegacy);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(TxInfo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (PhyPayload.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(PhyPayload);
+      }
+      if (txInfoLegacy_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxInfoLegacy);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(TxInfo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (PhyPayload.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(PhyPayload);
+      }
+      if (txInfoLegacy_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfoLegacy);
+      }
+      if (txInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfo);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DownlinkFrameItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.PhyPayload.Length != 0) {
+        PhyPayload = other.PhyPayload;
+      }
+      if (other.txInfoLegacy_ != null) {
+        if (txInfoLegacy_ == null) {
+          TxInfoLegacy = new global::Chirpstack.Gateway.DownlinkTxInfoLegacy();
+        }
+        TxInfoLegacy.MergeFrom(other.TxInfoLegacy);
+      }
+      if (other.txInfo_ != null) {
+        if (txInfo_ == null) {
+          TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+        }
+        TxInfo.MergeFrom(other.TxInfo);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            PhyPayload = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (txInfoLegacy_ == null) {
+              TxInfoLegacy = new global::Chirpstack.Gateway.DownlinkTxInfoLegacy();
+            }
+            input.ReadMessage(TxInfoLegacy);
+            break;
+          }
+          case 26: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            PhyPayload = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            if (txInfoLegacy_ == null) {
+              TxInfoLegacy = new global::Chirpstack.Gateway.DownlinkTxInfoLegacy();
+            }
+            input.ReadMessage(TxInfoLegacy);
+            break;
+          }
+          case 26: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DownlinkTxAck : pb::IMessage<DownlinkTxAck>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DownlinkTxAck> _parser = new pb::MessageParser<DownlinkTxAck>(() => new DownlinkTxAck());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DownlinkTxAck> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[22]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxAck() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxAck(DownlinkTxAck other) : this() {
+      gatewayIdLegacy_ = other.gatewayIdLegacy_;
+      gatewayId_ = other.gatewayId_;
+      downlinkId_ = other.downlinkId_;
+      downlinkIdLegacy_ = other.downlinkIdLegacy_;
+      items_ = other.items_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxAck Clone() {
+      return new DownlinkTxAck(this);
+    }
+
+    /// <summary>Field number for the "gateway_id_legacy" field.</summary>
+    public const int GatewayIdLegacyFieldNumber = 1;
+    private pb::ByteString gatewayIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID (deprecated).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayIdLegacy {
+      get { return gatewayIdLegacy_; }
+      set {
+        gatewayIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 6;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "downlink_id" field.</summary>
+    public const int DownlinkIdFieldNumber = 2;
+    private uint downlinkId_;
+    /// <summary>
+    /// Downlink ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint DownlinkId {
+      get { return downlinkId_; }
+      set {
+        downlinkId_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "downlink_id_legacy" field.</summary>
+    public const int DownlinkIdLegacyFieldNumber = 4;
+    private pb::ByteString downlinkIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Downlink ID (deprecated).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString DownlinkIdLegacy {
+      get { return downlinkIdLegacy_; }
+      set {
+        downlinkIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "items" field.</summary>
+    public const int ItemsFieldNumber = 5;
+    private static readonly pb::FieldCodec<global::Chirpstack.Gateway.DownlinkTxAckItem> _repeated_items_codec
+        = pb::FieldCodec.ForMessage(42, global::Chirpstack.Gateway.DownlinkTxAckItem.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Gateway.DownlinkTxAckItem> items_ = new pbc::RepeatedField<global::Chirpstack.Gateway.DownlinkTxAckItem>();
+    /// <summary>
+    /// Downlink frame items.
+    /// This list has the same length as the request and indicates which
+    /// downlink frame has been emitted of the requested list (or why it failed).
+    /// Note that at most one item has a positive acknowledgement.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Gateway.DownlinkTxAckItem> Items {
+      get { return items_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DownlinkTxAck);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DownlinkTxAck other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayIdLegacy != other.GatewayIdLegacy) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (DownlinkId != other.DownlinkId) return false;
+      if (DownlinkIdLegacy != other.DownlinkIdLegacy) return false;
+      if(!items_.Equals(other.items_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayIdLegacy.Length != 0) hash ^= GatewayIdLegacy.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (DownlinkId != 0) hash ^= DownlinkId.GetHashCode();
+      if (DownlinkIdLegacy.Length != 0) hash ^= DownlinkIdLegacy.GetHashCode();
+      hash ^= items_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (DownlinkId != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(DownlinkId);
+      }
+      if (DownlinkIdLegacy.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteBytes(DownlinkIdLegacy);
+      }
+      items_.WriteTo(output, _repeated_items_codec);
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (DownlinkId != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(DownlinkId);
+      }
+      if (DownlinkIdLegacy.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteBytes(DownlinkIdLegacy);
+      }
+      items_.WriteTo(ref output, _repeated_items_codec);
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (DownlinkId != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(DownlinkId);
+      }
+      if (DownlinkIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(DownlinkIdLegacy);
+      }
+      size += items_.CalculateSize(_repeated_items_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DownlinkTxAck other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayIdLegacy.Length != 0) {
+        GatewayIdLegacy = other.GatewayIdLegacy;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.DownlinkId != 0) {
+        DownlinkId = other.DownlinkId;
+      }
+      if (other.DownlinkIdLegacy.Length != 0) {
+        DownlinkIdLegacy = other.DownlinkIdLegacy;
+      }
+      items_.Add(other.items_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 16: {
+            DownlinkId = input.ReadUInt32();
+            break;
+          }
+          case 34: {
+            DownlinkIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 42: {
+            items_.AddEntriesFrom(input, _repeated_items_codec);
+            break;
+          }
+          case 50: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 16: {
+            DownlinkId = input.ReadUInt32();
+            break;
+          }
+          case 34: {
+            DownlinkIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 42: {
+            items_.AddEntriesFrom(ref input, _repeated_items_codec);
+            break;
+          }
+          case 50: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DownlinkTxAckItem : pb::IMessage<DownlinkTxAckItem>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DownlinkTxAckItem> _parser = new pb::MessageParser<DownlinkTxAckItem>(() => new DownlinkTxAckItem());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DownlinkTxAckItem> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[23]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxAckItem() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxAckItem(DownlinkTxAckItem other) : this() {
+      status_ = other.status_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkTxAckItem Clone() {
+      return new DownlinkTxAckItem(this);
+    }
+
+    /// <summary>Field number for the "status" field.</summary>
+    public const int StatusFieldNumber = 1;
+    private global::Chirpstack.Gateway.TxAckStatus status_ = global::Chirpstack.Gateway.TxAckStatus.Ignored;
+    /// <summary>
+    /// The Ack status of this item.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.TxAckStatus Status {
+      get { return status_; }
+      set {
+        status_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DownlinkTxAckItem);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DownlinkTxAckItem other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Status != other.Status) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Status != global::Chirpstack.Gateway.TxAckStatus.Ignored) hash ^= Status.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Status != global::Chirpstack.Gateway.TxAckStatus.Ignored) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) Status);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Status != global::Chirpstack.Gateway.TxAckStatus.Ignored) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) Status);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Status != global::Chirpstack.Gateway.TxAckStatus.Ignored) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Status);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DownlinkTxAckItem other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Status != global::Chirpstack.Gateway.TxAckStatus.Ignored) {
+        Status = other.Status;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Status = (global::Chirpstack.Gateway.TxAckStatus) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Status = (global::Chirpstack.Gateway.TxAckStatus) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GatewayConfiguration : pb::IMessage<GatewayConfiguration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GatewayConfiguration> _parser = new pb::MessageParser<GatewayConfiguration>(() => new GatewayConfiguration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GatewayConfiguration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[24]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayConfiguration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayConfiguration(GatewayConfiguration other) : this() {
+      gatewayIdLegacy_ = other.gatewayIdLegacy_;
+      gatewayId_ = other.gatewayId_;
+      version_ = other.version_;
+      channels_ = other.channels_.Clone();
+      statsInterval_ = other.statsInterval_ != null ? other.statsInterval_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayConfiguration Clone() {
+      return new GatewayConfiguration(this);
+    }
+
+    /// <summary>Field number for the "gateway_id_legacy" field.</summary>
+    public const int GatewayIdLegacyFieldNumber = 1;
+    private pb::ByteString gatewayIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID.
+    /// Deprecated: use gateway_id.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayIdLegacy {
+      get { return gatewayIdLegacy_; }
+      set {
+        gatewayIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 5;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "version" field.</summary>
+    public const int VersionFieldNumber = 2;
+    private string version_ = "";
+    /// <summary>
+    /// Configuration version.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Version {
+      get { return version_; }
+      set {
+        version_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "channels" field.</summary>
+    public const int ChannelsFieldNumber = 3;
+    private static readonly pb::FieldCodec<global::Chirpstack.Gateway.ChannelConfiguration> _repeated_channels_codec
+        = pb::FieldCodec.ForMessage(26, global::Chirpstack.Gateway.ChannelConfiguration.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Gateway.ChannelConfiguration> channels_ = new pbc::RepeatedField<global::Chirpstack.Gateway.ChannelConfiguration>();
+    /// <summary>
+    /// Channels.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Gateway.ChannelConfiguration> Channels {
+      get { return channels_; }
+    }
+
+    /// <summary>Field number for the "stats_interval" field.</summary>
+    public const int StatsIntervalFieldNumber = 4;
+    private global::Google.Protobuf.WellKnownTypes.Duration statsInterval_;
+    /// <summary>
+    /// Stats interval.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Duration StatsInterval {
+      get { return statsInterval_; }
+      set {
+        statsInterval_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GatewayConfiguration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GatewayConfiguration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayIdLegacy != other.GatewayIdLegacy) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (Version != other.Version) return false;
+      if(!channels_.Equals(other.channels_)) return false;
+      if (!object.Equals(StatsInterval, other.StatsInterval)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayIdLegacy.Length != 0) hash ^= GatewayIdLegacy.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (Version.Length != 0) hash ^= Version.GetHashCode();
+      hash ^= channels_.GetHashCode();
+      if (statsInterval_ != null) hash ^= StatsInterval.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (Version.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Version);
+      }
+      channels_.WriteTo(output, _repeated_channels_codec);
+      if (statsInterval_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(StatsInterval);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (Version.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Version);
+      }
+      channels_.WriteTo(ref output, _repeated_channels_codec);
+      if (statsInterval_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(StatsInterval);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (Version.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Version);
+      }
+      size += channels_.CalculateSize(_repeated_channels_codec);
+      if (statsInterval_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(StatsInterval);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GatewayConfiguration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayIdLegacy.Length != 0) {
+        GatewayIdLegacy = other.GatewayIdLegacy;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.Version.Length != 0) {
+        Version = other.Version;
+      }
+      channels_.Add(other.channels_);
+      if (other.statsInterval_ != null) {
+        if (statsInterval_ == null) {
+          StatsInterval = new global::Google.Protobuf.WellKnownTypes.Duration();
+        }
+        StatsInterval.MergeFrom(other.StatsInterval);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            Version = input.ReadString();
+            break;
+          }
+          case 26: {
+            channels_.AddEntriesFrom(input, _repeated_channels_codec);
+            break;
+          }
+          case 34: {
+            if (statsInterval_ == null) {
+              StatsInterval = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(StatsInterval);
+            break;
+          }
+          case 42: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            Version = input.ReadString();
+            break;
+          }
+          case 26: {
+            channels_.AddEntriesFrom(ref input, _repeated_channels_codec);
+            break;
+          }
+          case 34: {
+            if (statsInterval_ == null) {
+              StatsInterval = new global::Google.Protobuf.WellKnownTypes.Duration();
+            }
+            input.ReadMessage(StatsInterval);
+            break;
+          }
+          case 42: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class ChannelConfiguration : pb::IMessage<ChannelConfiguration>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ChannelConfiguration> _parser = new pb::MessageParser<ChannelConfiguration>(() => new ChannelConfiguration());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ChannelConfiguration> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[25]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ChannelConfiguration() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ChannelConfiguration(ChannelConfiguration other) : this() {
+      frequency_ = other.frequency_;
+      modulationLegacy_ = other.modulationLegacy_;
+      board_ = other.board_;
+      demodulator_ = other.demodulator_;
+      switch (other.ModulationConfigCase) {
+        case ModulationConfigOneofCase.LoraModulationConfig:
+          LoraModulationConfig = other.LoraModulationConfig.Clone();
+          break;
+        case ModulationConfigOneofCase.FskModulationConfig:
+          FskModulationConfig = other.FskModulationConfig.Clone();
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ChannelConfiguration Clone() {
+      return new ChannelConfiguration(this);
+    }
+
+    /// <summary>Field number for the "frequency" field.</summary>
+    public const int FrequencyFieldNumber = 1;
+    private uint frequency_;
+    /// <summary>
+    /// Frequency (Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Frequency {
+      get { return frequency_; }
+      set {
+        frequency_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "modulation_legacy" field.</summary>
+    public const int ModulationLegacyFieldNumber = 2;
+    private global::Chirpstack.Common.Modulation modulationLegacy_ = global::Chirpstack.Common.Modulation.Lora;
+    /// <summary>
+    /// Modulation (deprecated).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Modulation ModulationLegacy {
+      get { return modulationLegacy_; }
+      set {
+        modulationLegacy_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "lora_modulation_config" field.</summary>
+    public const int LoraModulationConfigFieldNumber = 3;
+    /// <summary>
+    /// LoRa modulation config.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.LoraModulationConfig LoraModulationConfig {
+      get { return modulationConfigCase_ == ModulationConfigOneofCase.LoraModulationConfig ? (global::Chirpstack.Gateway.LoraModulationConfig) modulationConfig_ : null; }
+      set {
+        modulationConfig_ = value;
+        modulationConfigCase_ = value == null ? ModulationConfigOneofCase.None : ModulationConfigOneofCase.LoraModulationConfig;
+      }
+    }
+
+    /// <summary>Field number for the "fsk_modulation_config" field.</summary>
+    public const int FskModulationConfigFieldNumber = 4;
+    /// <summary>
+    /// FSK modulation config.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.FskModulationConfig FskModulationConfig {
+      get { return modulationConfigCase_ == ModulationConfigOneofCase.FskModulationConfig ? (global::Chirpstack.Gateway.FskModulationConfig) modulationConfig_ : null; }
+      set {
+        modulationConfig_ = value;
+        modulationConfigCase_ = value == null ? ModulationConfigOneofCase.None : ModulationConfigOneofCase.FskModulationConfig;
+      }
+    }
+
+    /// <summary>Field number for the "board" field.</summary>
+    public const int BoardFieldNumber = 5;
+    private uint board_;
+    /// <summary>
+    /// Board index.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Board {
+      get { return board_; }
+      set {
+        board_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "demodulator" field.</summary>
+    public const int DemodulatorFieldNumber = 6;
+    private uint demodulator_;
+    /// <summary>
+    /// Demodulator index (of the given board).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Demodulator {
+      get { return demodulator_; }
+      set {
+        demodulator_ = value;
+      }
+    }
+
+    private object modulationConfig_;
+    /// <summary>Enum of possible cases for the "modulation_config" oneof.</summary>
+    public enum ModulationConfigOneofCase {
+      None = 0,
+      LoraModulationConfig = 3,
+      FskModulationConfig = 4,
+    }
+    private ModulationConfigOneofCase modulationConfigCase_ = ModulationConfigOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ModulationConfigOneofCase ModulationConfigCase {
+      get { return modulationConfigCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearModulationConfig() {
+      modulationConfigCase_ = ModulationConfigOneofCase.None;
+      modulationConfig_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ChannelConfiguration);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ChannelConfiguration other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Frequency != other.Frequency) return false;
+      if (ModulationLegacy != other.ModulationLegacy) return false;
+      if (!object.Equals(LoraModulationConfig, other.LoraModulationConfig)) return false;
+      if (!object.Equals(FskModulationConfig, other.FskModulationConfig)) return false;
+      if (Board != other.Board) return false;
+      if (Demodulator != other.Demodulator) return false;
+      if (ModulationConfigCase != other.ModulationConfigCase) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Frequency != 0) hash ^= Frequency.GetHashCode();
+      if (ModulationLegacy != global::Chirpstack.Common.Modulation.Lora) hash ^= ModulationLegacy.GetHashCode();
+      if (modulationConfigCase_ == ModulationConfigOneofCase.LoraModulationConfig) hash ^= LoraModulationConfig.GetHashCode();
+      if (modulationConfigCase_ == ModulationConfigOneofCase.FskModulationConfig) hash ^= FskModulationConfig.GetHashCode();
+      if (Board != 0) hash ^= Board.GetHashCode();
+      if (Demodulator != 0) hash ^= Demodulator.GetHashCode();
+      hash ^= (int) modulationConfigCase_;
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Frequency != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Frequency);
+      }
+      if (ModulationLegacy != global::Chirpstack.Common.Modulation.Lora) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) ModulationLegacy);
+      }
+      if (modulationConfigCase_ == ModulationConfigOneofCase.LoraModulationConfig) {
+        output.WriteRawTag(26);
+        output.WriteMessage(LoraModulationConfig);
+      }
+      if (modulationConfigCase_ == ModulationConfigOneofCase.FskModulationConfig) {
+        output.WriteRawTag(34);
+        output.WriteMessage(FskModulationConfig);
+      }
+      if (Board != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(Board);
+      }
+      if (Demodulator != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(Demodulator);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Frequency != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(Frequency);
+      }
+      if (ModulationLegacy != global::Chirpstack.Common.Modulation.Lora) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) ModulationLegacy);
+      }
+      if (modulationConfigCase_ == ModulationConfigOneofCase.LoraModulationConfig) {
+        output.WriteRawTag(26);
+        output.WriteMessage(LoraModulationConfig);
+      }
+      if (modulationConfigCase_ == ModulationConfigOneofCase.FskModulationConfig) {
+        output.WriteRawTag(34);
+        output.WriteMessage(FskModulationConfig);
+      }
+      if (Board != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(Board);
+      }
+      if (Demodulator != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(Demodulator);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Frequency != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Frequency);
+      }
+      if (ModulationLegacy != global::Chirpstack.Common.Modulation.Lora) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ModulationLegacy);
+      }
+      if (modulationConfigCase_ == ModulationConfigOneofCase.LoraModulationConfig) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(LoraModulationConfig);
+      }
+      if (modulationConfigCase_ == ModulationConfigOneofCase.FskModulationConfig) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(FskModulationConfig);
+      }
+      if (Board != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Board);
+      }
+      if (Demodulator != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Demodulator);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ChannelConfiguration other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Frequency != 0) {
+        Frequency = other.Frequency;
+      }
+      if (other.ModulationLegacy != global::Chirpstack.Common.Modulation.Lora) {
+        ModulationLegacy = other.ModulationLegacy;
+      }
+      if (other.Board != 0) {
+        Board = other.Board;
+      }
+      if (other.Demodulator != 0) {
+        Demodulator = other.Demodulator;
+      }
+      switch (other.ModulationConfigCase) {
+        case ModulationConfigOneofCase.LoraModulationConfig:
+          if (LoraModulationConfig == null) {
+            LoraModulationConfig = new global::Chirpstack.Gateway.LoraModulationConfig();
+          }
+          LoraModulationConfig.MergeFrom(other.LoraModulationConfig);
+          break;
+        case ModulationConfigOneofCase.FskModulationConfig:
+          if (FskModulationConfig == null) {
+            FskModulationConfig = new global::Chirpstack.Gateway.FskModulationConfig();
+          }
+          FskModulationConfig.MergeFrom(other.FskModulationConfig);
+          break;
+      }
+
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            ModulationLegacy = (global::Chirpstack.Common.Modulation) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            global::Chirpstack.Gateway.LoraModulationConfig subBuilder = new global::Chirpstack.Gateway.LoraModulationConfig();
+            if (modulationConfigCase_ == ModulationConfigOneofCase.LoraModulationConfig) {
+              subBuilder.MergeFrom(LoraModulationConfig);
+            }
+            input.ReadMessage(subBuilder);
+            LoraModulationConfig = subBuilder;
+            break;
+          }
+          case 34: {
+            global::Chirpstack.Gateway.FskModulationConfig subBuilder = new global::Chirpstack.Gateway.FskModulationConfig();
+            if (modulationConfigCase_ == ModulationConfigOneofCase.FskModulationConfig) {
+              subBuilder.MergeFrom(FskModulationConfig);
+            }
+            input.ReadMessage(subBuilder);
+            FskModulationConfig = subBuilder;
+            break;
+          }
+          case 40: {
+            Board = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            Demodulator = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            Frequency = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            ModulationLegacy = (global::Chirpstack.Common.Modulation) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            global::Chirpstack.Gateway.LoraModulationConfig subBuilder = new global::Chirpstack.Gateway.LoraModulationConfig();
+            if (modulationConfigCase_ == ModulationConfigOneofCase.LoraModulationConfig) {
+              subBuilder.MergeFrom(LoraModulationConfig);
+            }
+            input.ReadMessage(subBuilder);
+            LoraModulationConfig = subBuilder;
+            break;
+          }
+          case 34: {
+            global::Chirpstack.Gateway.FskModulationConfig subBuilder = new global::Chirpstack.Gateway.FskModulationConfig();
+            if (modulationConfigCase_ == ModulationConfigOneofCase.FskModulationConfig) {
+              subBuilder.MergeFrom(FskModulationConfig);
+            }
+            input.ReadMessage(subBuilder);
+            FskModulationConfig = subBuilder;
+            break;
+          }
+          case 40: {
+            Board = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            Demodulator = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class LoraModulationConfig : pb::IMessage<LoraModulationConfig>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<LoraModulationConfig> _parser = new pb::MessageParser<LoraModulationConfig>(() => new LoraModulationConfig());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<LoraModulationConfig> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[26]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraModulationConfig() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraModulationConfig(LoraModulationConfig other) : this() {
+      bandwidthLegacy_ = other.bandwidthLegacy_;
+      bandwidth_ = other.bandwidth_;
+      spreadingFactors_ = other.spreadingFactors_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LoraModulationConfig Clone() {
+      return new LoraModulationConfig(this);
+    }
+
+    /// <summary>Field number for the "bandwidth_legacy" field.</summary>
+    public const int BandwidthLegacyFieldNumber = 1;
+    private uint bandwidthLegacy_;
+    /// <summary>
+    /// Bandwidth (kHz).
+    /// Deprecated: use bandwidth.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint BandwidthLegacy {
+      get { return bandwidthLegacy_; }
+      set {
+        bandwidthLegacy_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "bandwidth" field.</summary>
+    public const int BandwidthFieldNumber = 3;
+    private uint bandwidth_;
+    /// <summary>
+    /// Bandwidth (Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Bandwidth {
+      get { return bandwidth_; }
+      set {
+        bandwidth_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "spreading_factors" field.</summary>
+    public const int SpreadingFactorsFieldNumber = 2;
+    private static readonly pb::FieldCodec<uint> _repeated_spreadingFactors_codec
+        = pb::FieldCodec.ForUInt32(18);
+    private readonly pbc::RepeatedField<uint> spreadingFactors_ = new pbc::RepeatedField<uint>();
+    /// <summary>
+    /// Spreading-factors.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<uint> SpreadingFactors {
+      get { return spreadingFactors_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as LoraModulationConfig);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(LoraModulationConfig other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (BandwidthLegacy != other.BandwidthLegacy) return false;
+      if (Bandwidth != other.Bandwidth) return false;
+      if(!spreadingFactors_.Equals(other.spreadingFactors_)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (BandwidthLegacy != 0) hash ^= BandwidthLegacy.GetHashCode();
+      if (Bandwidth != 0) hash ^= Bandwidth.GetHashCode();
+      hash ^= spreadingFactors_.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (BandwidthLegacy != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(BandwidthLegacy);
+      }
+      spreadingFactors_.WriteTo(output, _repeated_spreadingFactors_codec);
+      if (Bandwidth != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(Bandwidth);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (BandwidthLegacy != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(BandwidthLegacy);
+      }
+      spreadingFactors_.WriteTo(ref output, _repeated_spreadingFactors_codec);
+      if (Bandwidth != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(Bandwidth);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (BandwidthLegacy != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(BandwidthLegacy);
+      }
+      if (Bandwidth != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Bandwidth);
+      }
+      size += spreadingFactors_.CalculateSize(_repeated_spreadingFactors_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(LoraModulationConfig other) {
+      if (other == null) {
+        return;
+      }
+      if (other.BandwidthLegacy != 0) {
+        BandwidthLegacy = other.BandwidthLegacy;
+      }
+      if (other.Bandwidth != 0) {
+        Bandwidth = other.Bandwidth;
+      }
+      spreadingFactors_.Add(other.spreadingFactors_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            BandwidthLegacy = input.ReadUInt32();
+            break;
+          }
+          case 18:
+          case 16: {
+            spreadingFactors_.AddEntriesFrom(input, _repeated_spreadingFactors_codec);
+            break;
+          }
+          case 24: {
+            Bandwidth = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            BandwidthLegacy = input.ReadUInt32();
+            break;
+          }
+          case 18:
+          case 16: {
+            spreadingFactors_.AddEntriesFrom(ref input, _repeated_spreadingFactors_codec);
+            break;
+          }
+          case 24: {
+            Bandwidth = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class FskModulationConfig : pb::IMessage<FskModulationConfig>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<FskModulationConfig> _parser = new pb::MessageParser<FskModulationConfig>(() => new FskModulationConfig());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<FskModulationConfig> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[27]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FskModulationConfig() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FskModulationConfig(FskModulationConfig other) : this() {
+      bandwidthLegacy_ = other.bandwidthLegacy_;
+      bandwidth_ = other.bandwidth_;
+      bitrate_ = other.bitrate_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public FskModulationConfig Clone() {
+      return new FskModulationConfig(this);
+    }
+
+    /// <summary>Field number for the "bandwidth_legacy" field.</summary>
+    public const int BandwidthLegacyFieldNumber = 1;
+    private uint bandwidthLegacy_;
+    /// <summary>
+    /// Bandwidth (kHz).
+    /// Deprecated: use bandwidth.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint BandwidthLegacy {
+      get { return bandwidthLegacy_; }
+      set {
+        bandwidthLegacy_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "bandwidth" field.</summary>
+    public const int BandwidthFieldNumber = 3;
+    private uint bandwidth_;
+    /// <summary>
+    /// Bandwidth (Hz).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Bandwidth {
+      get { return bandwidth_; }
+      set {
+        bandwidth_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "bitrate" field.</summary>
+    public const int BitrateFieldNumber = 2;
+    private uint bitrate_;
+    /// <summary>
+    /// Bitrate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Bitrate {
+      get { return bitrate_; }
+      set {
+        bitrate_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as FskModulationConfig);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(FskModulationConfig other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (BandwidthLegacy != other.BandwidthLegacy) return false;
+      if (Bandwidth != other.Bandwidth) return false;
+      if (Bitrate != other.Bitrate) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (BandwidthLegacy != 0) hash ^= BandwidthLegacy.GetHashCode();
+      if (Bandwidth != 0) hash ^= Bandwidth.GetHashCode();
+      if (Bitrate != 0) hash ^= Bitrate.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (BandwidthLegacy != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(BandwidthLegacy);
+      }
+      if (Bitrate != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Bitrate);
+      }
+      if (Bandwidth != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(Bandwidth);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (BandwidthLegacy != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(BandwidthLegacy);
+      }
+      if (Bitrate != 0) {
+        output.WriteRawTag(16);
+        output.WriteUInt32(Bitrate);
+      }
+      if (Bandwidth != 0) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(Bandwidth);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (BandwidthLegacy != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(BandwidthLegacy);
+      }
+      if (Bandwidth != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Bandwidth);
+      }
+      if (Bitrate != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Bitrate);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(FskModulationConfig other) {
+      if (other == null) {
+        return;
+      }
+      if (other.BandwidthLegacy != 0) {
+        BandwidthLegacy = other.BandwidthLegacy;
+      }
+      if (other.Bandwidth != 0) {
+        Bandwidth = other.Bandwidth;
+      }
+      if (other.Bitrate != 0) {
+        Bitrate = other.Bitrate;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            BandwidthLegacy = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Bitrate = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            Bandwidth = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            BandwidthLegacy = input.ReadUInt32();
+            break;
+          }
+          case 16: {
+            Bitrate = input.ReadUInt32();
+            break;
+          }
+          case 24: {
+            Bandwidth = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GatewayCommandExecRequest : pb::IMessage<GatewayCommandExecRequest>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GatewayCommandExecRequest> _parser = new pb::MessageParser<GatewayCommandExecRequest>(() => new GatewayCommandExecRequest());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GatewayCommandExecRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[28]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayCommandExecRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayCommandExecRequest(GatewayCommandExecRequest other) : this() {
+      gatewayIdLegacy_ = other.gatewayIdLegacy_;
+      gatewayId_ = other.gatewayId_;
+      command_ = other.command_;
+      execId_ = other.execId_;
+      stdin_ = other.stdin_;
+      environment_ = other.environment_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayCommandExecRequest Clone() {
+      return new GatewayCommandExecRequest(this);
+    }
+
+    /// <summary>Field number for the "gateway_id_legacy" field.</summary>
+    public const int GatewayIdLegacyFieldNumber = 1;
+    private pb::ByteString gatewayIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID.
+    /// Deprecated: use gateway_id.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayIdLegacy {
+      get { return gatewayIdLegacy_; }
+      set {
+        gatewayIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 6;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "command" field.</summary>
+    public const int CommandFieldNumber = 2;
+    private string command_ = "";
+    /// <summary>
+    /// Command to execute.
+    /// This command must be pre-configured in the LoRa Gateway Bridge configuration.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Command {
+      get { return command_; }
+      set {
+        command_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "exec_id" field.</summary>
+    public const int ExecIdFieldNumber = 7;
+    private uint execId_;
+    /// <summary>
+    /// Execution request ID.
+    /// The same will be returned when the execution of the command has
+    /// completed.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ExecId {
+      get { return execId_; }
+      set {
+        execId_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "stdin" field.</summary>
+    public const int StdinFieldNumber = 4;
+    private pb::ByteString stdin_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Standard input.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Stdin {
+      get { return stdin_; }
+      set {
+        stdin_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "environment" field.</summary>
+    public const int EnvironmentFieldNumber = 5;
+    private static readonly pbc::MapField<string, string>.Codec _map_environment_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 42);
+    private readonly pbc::MapField<string, string> environment_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Environment variables.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Environment {
+      get { return environment_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GatewayCommandExecRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GatewayCommandExecRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayIdLegacy != other.GatewayIdLegacy) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (Command != other.Command) return false;
+      if (ExecId != other.ExecId) return false;
+      if (Stdin != other.Stdin) return false;
+      if (!Environment.Equals(other.Environment)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayIdLegacy.Length != 0) hash ^= GatewayIdLegacy.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (Command.Length != 0) hash ^= Command.GetHashCode();
+      if (ExecId != 0) hash ^= ExecId.GetHashCode();
+      if (Stdin.Length != 0) hash ^= Stdin.GetHashCode();
+      hash ^= Environment.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (Command.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Command);
+      }
+      if (Stdin.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteBytes(Stdin);
+      }
+      environment_.WriteTo(output, _map_environment_codec);
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(GatewayId);
+      }
+      if (ExecId != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(ExecId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (Command.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Command);
+      }
+      if (Stdin.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteBytes(Stdin);
+      }
+      environment_.WriteTo(ref output, _map_environment_codec);
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(GatewayId);
+      }
+      if (ExecId != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(ExecId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (Command.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Command);
+      }
+      if (ExecId != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ExecId);
+      }
+      if (Stdin.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Stdin);
+      }
+      size += environment_.CalculateSize(_map_environment_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GatewayCommandExecRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayIdLegacy.Length != 0) {
+        GatewayIdLegacy = other.GatewayIdLegacy;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.Command.Length != 0) {
+        Command = other.Command;
+      }
+      if (other.ExecId != 0) {
+        ExecId = other.ExecId;
+      }
+      if (other.Stdin.Length != 0) {
+        Stdin = other.Stdin;
+      }
+      environment_.Add(other.environment_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            Command = input.ReadString();
+            break;
+          }
+          case 34: {
+            Stdin = input.ReadBytes();
+            break;
+          }
+          case 42: {
+            environment_.AddEntriesFrom(input, _map_environment_codec);
+            break;
+          }
+          case 50: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 56: {
+            ExecId = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 18: {
+            Command = input.ReadString();
+            break;
+          }
+          case 34: {
+            Stdin = input.ReadBytes();
+            break;
+          }
+          case 42: {
+            environment_.AddEntriesFrom(ref input, _map_environment_codec);
+            break;
+          }
+          case 50: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 56: {
+            ExecId = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class GatewayCommandExecResponse : pb::IMessage<GatewayCommandExecResponse>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<GatewayCommandExecResponse> _parser = new pb::MessageParser<GatewayCommandExecResponse>(() => new GatewayCommandExecResponse());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<GatewayCommandExecResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[29]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayCommandExecResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayCommandExecResponse(GatewayCommandExecResponse other) : this() {
+      gatewayIdLegacy_ = other.gatewayIdLegacy_;
+      gatewayId_ = other.gatewayId_;
+      execId_ = other.execId_;
+      stdout_ = other.stdout_;
+      stderr_ = other.stderr_;
+      error_ = other.error_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public GatewayCommandExecResponse Clone() {
+      return new GatewayCommandExecResponse(this);
+    }
+
+    /// <summary>Field number for the "gateway_id_legacy" field.</summary>
+    public const int GatewayIdLegacyFieldNumber = 1;
+    private pb::ByteString gatewayIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID.
+    /// Deprecated: use gateway_id.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayIdLegacy {
+      get { return gatewayIdLegacy_; }
+      set {
+        gatewayIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 6;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "exec_id" field.</summary>
+    public const int ExecIdFieldNumber = 7;
+    private uint execId_;
+    /// <summary>
+    /// Execution request ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ExecId {
+      get { return execId_; }
+      set {
+        execId_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "stdout" field.</summary>
+    public const int StdoutFieldNumber = 3;
+    private pb::ByteString stdout_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Standard output.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Stdout {
+      get { return stdout_; }
+      set {
+        stdout_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "stderr" field.</summary>
+    public const int StderrFieldNumber = 4;
+    private pb::ByteString stderr_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Standard error.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Stderr {
+      get { return stderr_; }
+      set {
+        stderr_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "error" field.</summary>
+    public const int ErrorFieldNumber = 5;
+    private string error_ = "";
+    /// <summary>
+    /// Error message.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Error {
+      get { return error_; }
+      set {
+        error_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as GatewayCommandExecResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(GatewayCommandExecResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayIdLegacy != other.GatewayIdLegacy) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (ExecId != other.ExecId) return false;
+      if (Stdout != other.Stdout) return false;
+      if (Stderr != other.Stderr) return false;
+      if (Error != other.Error) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayIdLegacy.Length != 0) hash ^= GatewayIdLegacy.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (ExecId != 0) hash ^= ExecId.GetHashCode();
+      if (Stdout.Length != 0) hash ^= Stdout.GetHashCode();
+      if (Stderr.Length != 0) hash ^= Stderr.GetHashCode();
+      if (Error.Length != 0) hash ^= Error.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (Stdout.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteBytes(Stdout);
+      }
+      if (Stderr.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteBytes(Stderr);
+      }
+      if (Error.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Error);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(GatewayId);
+      }
+      if (ExecId != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(ExecId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (Stdout.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteBytes(Stdout);
+      }
+      if (Stderr.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteBytes(Stderr);
+      }
+      if (Error.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Error);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(GatewayId);
+      }
+      if (ExecId != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(ExecId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (ExecId != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ExecId);
+      }
+      if (Stdout.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Stdout);
+      }
+      if (Stderr.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Stderr);
+      }
+      if (Error.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Error);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(GatewayCommandExecResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayIdLegacy.Length != 0) {
+        GatewayIdLegacy = other.GatewayIdLegacy;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.ExecId != 0) {
+        ExecId = other.ExecId;
+      }
+      if (other.Stdout.Length != 0) {
+        Stdout = other.Stdout;
+      }
+      if (other.Stderr.Length != 0) {
+        Stderr = other.Stderr;
+      }
+      if (other.Error.Length != 0) {
+        Error = other.Error;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 26: {
+            Stdout = input.ReadBytes();
+            break;
+          }
+          case 34: {
+            Stderr = input.ReadBytes();
+            break;
+          }
+          case 42: {
+            Error = input.ReadString();
+            break;
+          }
+          case 50: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 56: {
+            ExecId = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 26: {
+            Stdout = input.ReadBytes();
+            break;
+          }
+          case 34: {
+            Stderr = input.ReadBytes();
+            break;
+          }
+          case 42: {
+            Error = input.ReadString();
+            break;
+          }
+          case 50: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 56: {
+            ExecId = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// RawPacketForwarderEvent contains a raw packet-forwarder event.
+  /// It can be used to access packet-forwarder features that are not (fully)
+  /// integrated with the ChirpStack Gateway Bridge.
+  /// </summary>
+  public sealed partial class RawPacketForwarderEvent : pb::IMessage<RawPacketForwarderEvent>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<RawPacketForwarderEvent> _parser = new pb::MessageParser<RawPacketForwarderEvent>(() => new RawPacketForwarderEvent());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<RawPacketForwarderEvent> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[30]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RawPacketForwarderEvent() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RawPacketForwarderEvent(RawPacketForwarderEvent other) : this() {
+      gatewayIdLegacy_ = other.gatewayIdLegacy_;
+      gatewayId_ = other.gatewayId_;
+      payload_ = other.payload_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RawPacketForwarderEvent Clone() {
+      return new RawPacketForwarderEvent(this);
+    }
+
+    /// <summary>Field number for the "gateway_id_legacy" field.</summary>
+    public const int GatewayIdLegacyFieldNumber = 1;
+    private pb::ByteString gatewayIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID.
+    /// Deprecated: use gateway_id.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayIdLegacy {
+      get { return gatewayIdLegacy_; }
+      set {
+        gatewayIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 4;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "payload" field.</summary>
+    public const int PayloadFieldNumber = 3;
+    private pb::ByteString payload_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Payload contains the raw payload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Payload {
+      get { return payload_; }
+      set {
+        payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as RawPacketForwarderEvent);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(RawPacketForwarderEvent other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayIdLegacy != other.GatewayIdLegacy) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (Payload != other.Payload) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayIdLegacy.Length != 0) hash ^= GatewayIdLegacy.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (Payload.Length != 0) hash ^= Payload.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (Payload.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteBytes(Payload);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (Payload.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteBytes(Payload);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (Payload.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Payload);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(RawPacketForwarderEvent other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayIdLegacy.Length != 0) {
+        GatewayIdLegacy = other.GatewayIdLegacy;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.Payload.Length != 0) {
+        Payload = other.Payload;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 26: {
+            Payload = input.ReadBytes();
+            break;
+          }
+          case 34: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 26: {
+            Payload = input.ReadBytes();
+            break;
+          }
+          case 34: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// RawPacketForwarderEvent contains a raw packet-forwarder command.
+  /// It can be used to access packet-forwarder features that are not (fully)
+  /// integrated with the ChirpStack Gateway Bridge.
+  /// </summary>
+  public sealed partial class RawPacketForwarderCommand : pb::IMessage<RawPacketForwarderCommand>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<RawPacketForwarderCommand> _parser = new pb::MessageParser<RawPacketForwarderCommand>(() => new RawPacketForwarderCommand());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<RawPacketForwarderCommand> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[31]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RawPacketForwarderCommand() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RawPacketForwarderCommand(RawPacketForwarderCommand other) : this() {
+      gatewayIdLegacy_ = other.gatewayIdLegacy_;
+      gatewayId_ = other.gatewayId_;
+      payload_ = other.payload_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public RawPacketForwarderCommand Clone() {
+      return new RawPacketForwarderCommand(this);
+    }
+
+    /// <summary>Field number for the "gateway_id_legacy" field.</summary>
+    public const int GatewayIdLegacyFieldNumber = 1;
+    private pb::ByteString gatewayIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID.
+    /// Deprecated: use gateway_id.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayIdLegacy {
+      get { return gatewayIdLegacy_; }
+      set {
+        gatewayIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 4;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "payload" field.</summary>
+    public const int PayloadFieldNumber = 3;
+    private pb::ByteString payload_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Payload contains the raw payload.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Payload {
+      get { return payload_; }
+      set {
+        payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as RawPacketForwarderCommand);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(RawPacketForwarderCommand other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayIdLegacy != other.GatewayIdLegacy) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (Payload != other.Payload) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayIdLegacy.Length != 0) hash ^= GatewayIdLegacy.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (Payload.Length != 0) hash ^= Payload.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (Payload.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteBytes(Payload);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (Payload.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteBytes(Payload);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (Payload.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Payload);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(RawPacketForwarderCommand other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayIdLegacy.Length != 0) {
+        GatewayIdLegacy = other.GatewayIdLegacy;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.Payload.Length != 0) {
+        Payload = other.Payload;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 26: {
+            Payload = input.ReadBytes();
+            break;
+          }
+          case 34: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 26: {
+            Payload = input.ReadBytes();
+            break;
+          }
+          case 34: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// ConnState contains the connection state of a gateway.
+  /// </summary>
+  public sealed partial class ConnState : pb::IMessage<ConnState>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<ConnState> _parser = new pb::MessageParser<ConnState>(() => new ConnState());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<ConnState> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Gateway.GwReflection.Descriptor.MessageTypes[32]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ConnState() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ConnState(ConnState other) : this() {
+      gatewayIdLegacy_ = other.gatewayIdLegacy_;
+      gatewayId_ = other.gatewayId_;
+      state_ = other.state_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ConnState Clone() {
+      return new ConnState(this);
+    }
+
+    /// <summary>Field number for the "gateway_id_legacy" field.</summary>
+    public const int GatewayIdLegacyFieldNumber = 1;
+    private pb::ByteString gatewayIdLegacy_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Gateway ID.
+    /// Deprecated: use gateway_id.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString GatewayIdLegacy {
+      get { return gatewayIdLegacy_; }
+      set {
+        gatewayIdLegacy_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 3;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "state" field.</summary>
+    public const int StateFieldNumber = 2;
+    private global::Chirpstack.Gateway.ConnState.Types.State state_ = global::Chirpstack.Gateway.ConnState.Types.State.Offline;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.ConnState.Types.State State {
+      get { return state_; }
+      set {
+        state_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as ConnState);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(ConnState other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (GatewayIdLegacy != other.GatewayIdLegacy) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (State != other.State) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (GatewayIdLegacy.Length != 0) hash ^= GatewayIdLegacy.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (State != global::Chirpstack.Gateway.ConnState.Types.State.Offline) hash ^= State.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (State != global::Chirpstack.Gateway.ConnState.Types.State.Offline) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) State);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (GatewayIdLegacy.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteBytes(GatewayIdLegacy);
+      }
+      if (State != global::Chirpstack.Gateway.ConnState.Types.State.Offline) {
+        output.WriteRawTag(16);
+        output.WriteEnum((int) State);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (GatewayIdLegacy.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(GatewayIdLegacy);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (State != global::Chirpstack.Gateway.ConnState.Types.State.Offline) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) State);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(ConnState other) {
+      if (other == null) {
+        return;
+      }
+      if (other.GatewayIdLegacy.Length != 0) {
+        GatewayIdLegacy = other.GatewayIdLegacy;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.State != global::Chirpstack.Gateway.ConnState.Types.State.Offline) {
+        State = other.State;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 16: {
+            State = (global::Chirpstack.Gateway.ConnState.Types.State) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            GatewayIdLegacy = input.ReadBytes();
+            break;
+          }
+          case 16: {
+            State = (global::Chirpstack.Gateway.ConnState.Types.State) input.ReadEnum();
+            break;
+          }
+          case 26: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+    #region Nested types
+    /// <summary>Container for nested types declared in the ConnState message type.</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static partial class Types {
+      public enum State {
+        [pbr::OriginalName("OFFLINE")] Offline = 0,
+        [pbr::OriginalName("ONLINE")] Online = 1,
+      }
+
+    }
+    #endregion
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/integration/Integration.cs b/api/csharp/Chirpstack/integration/Integration.cs
new file mode 100644
index 00000000..1a011d90
--- /dev/null
+++ b/api/csharp/Chirpstack/integration/Integration.cs
@@ -0,0 +1,4617 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: integration/integration.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Integration {
+
+  /// <summary>Holder for reflection information generated from integration/integration.proto</summary>
+  public static partial class IntegrationReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for integration/integration.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static IntegrationReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "Ch1pbnRlZ3JhdGlvbi9pbnRlZ3JhdGlvbi5wcm90bxILaW50ZWdyYXRpb24a",
+            "E2NvbW1vbi9jb21tb24ucHJvdG8aC2d3L2d3LnByb3RvGh9nb29nbGUvcHJv",
+            "dG9idWYvdGltZXN0YW1wLnByb3RvGhxnb29nbGUvcHJvdG9idWYvc3RydWN0",
+            "LnByb3RvIqICCgpEZXZpY2VJbmZvEhEKCXRlbmFudF9pZBgBIAEoCRITCgt0",
+            "ZW5hbnRfbmFtZRgCIAEoCRIWCg5hcHBsaWNhdGlvbl9pZBgDIAEoCRIYChBh",
+            "cHBsaWNhdGlvbl9uYW1lGAQgASgJEhkKEWRldmljZV9wcm9maWxlX2lkGAUg",
+            "ASgJEhsKE2RldmljZV9wcm9maWxlX25hbWUYBiABKAkSEwoLZGV2aWNlX25h",
+            "bWUYByABKAkSDwoHZGV2X2V1aRgIIAEoCRIvCgR0YWdzGAkgAygLMiEuaW50",
+            "ZWdyYXRpb24uRGV2aWNlSW5mby5UYWdzRW50cnkaKwoJVGFnc0VudHJ5EgsK",
+            "A2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEi2QIKC1VwbGlua0V2ZW50",
+            "EhgKEGRlZHVwbGljYXRpb25faWQYASABKAkSKAoEdGltZRgCIAEoCzIaLmdv",
+            "b2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLAoLZGV2aWNlX2luZm8YAyABKAsy",
+            "Fy5pbnRlZ3JhdGlvbi5EZXZpY2VJbmZvEhAKCGRldl9hZGRyGAQgASgJEgsK",
+            "A2FkchgFIAEoCBIKCgJkchgGIAEoDRINCgVmX2NudBgHIAEoDRIOCgZmX3Bv",
+            "cnQYCCABKA0SEQoJY29uZmlybWVkGAkgASgIEgwKBGRhdGEYCiABKAwSJwoG",
+            "b2JqZWN0GAsgASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdBIhCgdyeF9p",
+            "bmZvGAwgAygLMhAuZ3cuVXBsaW5rUnhJbmZvEiEKB3R4X2luZm8YDSABKAsy",
+            "EC5ndy5VcGxpbmtUeEluZm8ijwEKCUpvaW5FdmVudBIYChBkZWR1cGxpY2F0",
+            "aW9uX2lkGAEgASgJEigKBHRpbWUYAiABKAsyGi5nb29nbGUucHJvdG9idWYu",
+            "VGltZXN0YW1wEiwKC2RldmljZV9pbmZvGAMgASgLMhcuaW50ZWdyYXRpb24u",
+            "RGV2aWNlSW5mbxIQCghkZXZfYWRkchgEIAEoCSK9AQoIQWNrRXZlbnQSGAoQ",
+            "ZGVkdXBsaWNhdGlvbl9pZBgBIAEoCRIoCgR0aW1lGAIgASgLMhouZ29vZ2xl",
+            "LnByb3RvYnVmLlRpbWVzdGFtcBIsCgtkZXZpY2VfaW5mbxgDIAEoCzIXLmlu",
+            "dGVncmF0aW9uLkRldmljZUluZm8SFQoNcXVldWVfaXRlbV9pZBgEIAEoCRIU",
+            "CgxhY2tub3dsZWRnZWQYBSABKAgSEgoKZl9jbnRfZG93bhgGIAEoDSLdAQoK",
+            "VHhBY2tFdmVudBITCgtkb3dubGlua19pZBgBIAEoDRIoCgR0aW1lGAIgASgL",
+            "MhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIsCgtkZXZpY2VfaW5mbxgD",
+            "IAEoCzIXLmludGVncmF0aW9uLkRldmljZUluZm8SFQoNcXVldWVfaXRlbV9p",
+            "ZBgEIAEoCRISCgpmX2NudF9kb3duGAUgASgNEhIKCmdhdGV3YXlfaWQYBiAB",
+            "KAkSIwoHdHhfaW5mbxgHIAEoCzISLmd3LkRvd25saW5rVHhJbmZvIqYCCghM",
+            "b2dFdmVudBIoCgR0aW1lGAEgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVz",
+            "dGFtcBIsCgtkZXZpY2VfaW5mbxgCIAEoCzIXLmludGVncmF0aW9uLkRldmlj",
+            "ZUluZm8SJAoFbGV2ZWwYAyABKA4yFS5pbnRlZ3JhdGlvbi5Mb2dMZXZlbBIi",
+            "CgRjb2RlGAQgASgOMhQuaW50ZWdyYXRpb24uTG9nQ29kZRITCgtkZXNjcmlw",
+            "dGlvbhgFIAEoCRIzCgdjb250ZXh0GAYgAygLMiIuaW50ZWdyYXRpb24uTG9n",
+            "RXZlbnQuQ29udGV4dEVudHJ5Gi4KDENvbnRleHRFbnRyeRILCgNrZXkYASAB",
+            "KAkSDQoFdmFsdWUYAiABKAk6AjgBIugBCgtTdGF0dXNFdmVudBIYChBkZWR1",
+            "cGxpY2F0aW9uX2lkGAEgASgJEigKBHRpbWUYAiABKAsyGi5nb29nbGUucHJv",
+            "dG9idWYuVGltZXN0YW1wEiwKC2RldmljZV9pbmZvGAMgASgLMhcuaW50ZWdy",
+            "YXRpb24uRGV2aWNlSW5mbxIOCgZtYXJnaW4YBSABKAUSHQoVZXh0ZXJuYWxf",
+            "cG93ZXJfc291cmNlGAYgASgIEiEKGWJhdHRlcnlfbGV2ZWxfdW5hdmFpbGFi",
+            "bGUYByABKAgSFQoNYmF0dGVyeV9sZXZlbBgIIAEoAiKlAQoNTG9jYXRpb25F",
+            "dmVudBIYChBkZWR1cGxpY2F0aW9uX2lkGAEgASgJEigKBHRpbWUYAiABKAsy",
+            "Gi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEiwKC2RldmljZV9pbmZvGAMg",
+            "ASgLMhcuaW50ZWdyYXRpb24uRGV2aWNlSW5mbxIiCghsb2NhdGlvbhgEIAEo",
+            "CzIQLmNvbW1vbi5Mb2NhdGlvbiLbAQoQSW50ZWdyYXRpb25FdmVudBIYChBk",
+            "ZWR1cGxpY2F0aW9uX2lkGAEgASgJEigKBHRpbWUYAiABKAsyGi5nb29nbGUu",
+            "cHJvdG9idWYuVGltZXN0YW1wEiwKC2RldmljZV9pbmZvGAMgASgLMhcuaW50",
+            "ZWdyYXRpb24uRGV2aWNlSW5mbxIYChBpbnRlZ3JhdGlvbl9uYW1lGAQgASgJ",
+            "EhIKCmV2ZW50X3R5cGUYBSABKAkSJwoGb2JqZWN0GAYgASgLMhcuZ29vZ2xl",
+            "LnByb3RvYnVmLlN0cnVjdCKIAQoPRG93bmxpbmtDb21tYW5kEgoKAmlkGAEg",
+            "ASgJEg8KB2Rldl9ldWkYAiABKAkSEQoJY29uZmlybWVkGAMgASgIEg4KBmZf",
+            "cG9ydBgEIAEoDRIMCgRkYXRhGAUgASgMEicKBm9iamVjdBgGIAEoCzIXLmdv",
+            "b2dsZS5wcm90b2J1Zi5TdHJ1Y3QqLAoITG9nTGV2ZWwSCAoESU5GTxAAEgsK",
+            "B1dBUk5JTkcQARIJCgVFUlJPUhACKsABCgdMb2dDb2RlEgsKB1VOS05PV04Q",
+            "ABIZChVET1dOTElOS19QQVlMT0FEX1NJWkUQARIQCgxVUExJTktfQ09ERUMQ",
+            "AhISCg5ET1dOTElOS19DT0RFQxADEggKBE9UQUEQBBIWChJVUExJTktfRl9D",
+            "TlRfUkVTRVQQBRIOCgpVUExJTktfTUlDEAYSHwobVVBMSU5LX0ZfQ05UX1JF",
+            "VFJBTlNNSVNTSU9OEAcSFAoQRE9XTkxJTktfR0FURVdBWRAIQoEBCh1pby5j",
+            "aGlycHN0YWNrLmFwaS5pbnRlZ3JhdGlvbkIQSW50ZWdyYXRpb25Qcm90b1AB",
+            "WjNnaXRodWIuY29tL2Jyb2NhYXIvY2hpcnBzdGFjay9hcGkvZ28vdjQvaW50",
+            "ZWdyYXRpb26qAhZDaGlycHN0YWNrLkludGVncmF0aW9uYgZwcm90bzM="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Chirpstack.Common.CommonReflection.Descriptor, global::Chirpstack.Gateway.GwReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Chirpstack.Integration.LogLevel), typeof(global::Chirpstack.Integration.LogCode), }, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.DeviceInfo), global::Chirpstack.Integration.DeviceInfo.Parser, new[]{ "TenantId", "TenantName", "ApplicationId", "ApplicationName", "DeviceProfileId", "DeviceProfileName", "DeviceName", "DevEui", "Tags" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.UplinkEvent), global::Chirpstack.Integration.UplinkEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "DevAddr", "Adr", "Dr", "FCnt", "FPort", "Confirmed", "Data", "Object", "RxInfo", "TxInfo" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.JoinEvent), global::Chirpstack.Integration.JoinEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "DevAddr" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.AckEvent), global::Chirpstack.Integration.AckEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "QueueItemId", "Acknowledged", "FCntDown" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.TxAckEvent), global::Chirpstack.Integration.TxAckEvent.Parser, new[]{ "DownlinkId", "Time", "DeviceInfo", "QueueItemId", "FCntDown", "GatewayId", "TxInfo" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.LogEvent), global::Chirpstack.Integration.LogEvent.Parser, new[]{ "Time", "DeviceInfo", "Level", "Code", "Description", "Context" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.StatusEvent), global::Chirpstack.Integration.StatusEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "Margin", "ExternalPowerSource", "BatteryLevelUnavailable", "BatteryLevel" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.LocationEvent), global::Chirpstack.Integration.LocationEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "Location" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.IntegrationEvent), global::Chirpstack.Integration.IntegrationEvent.Parser, new[]{ "DeduplicationId", "Time", "DeviceInfo", "IntegrationName", "EventType", "Object" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Integration.DownlinkCommand), global::Chirpstack.Integration.DownlinkCommand.Parser, new[]{ "Id", "DevEui", "Confirmed", "FPort", "Data", "Object" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Enums
+  public enum LogLevel {
+    /// <summary>
+    /// Info.
+    /// </summary>
+    [pbr::OriginalName("INFO")] Info = 0,
+    /// <summary>
+    /// Warning.
+    /// </summary>
+    [pbr::OriginalName("WARNING")] Warning = 1,
+    /// <summary>
+    /// Error.
+    /// </summary>
+    [pbr::OriginalName("ERROR")] Error = 2,
+  }
+
+  public enum LogCode {
+    /// <summary>
+    /// Unknown type.
+    /// </summary>
+    [pbr::OriginalName("UNKNOWN")] Unknown = 0,
+    /// <summary>
+    /// Error related to the downlink payload size.
+    /// Usually seen when the payload exceeded the maximum allowed payload size.
+    /// </summary>
+    [pbr::OriginalName("DOWNLINK_PAYLOAD_SIZE")] DownlinkPayloadSize = 1,
+    /// <summary>
+    /// Uplink codec error.
+    /// </summary>
+    [pbr::OriginalName("UPLINK_CODEC")] UplinkCodec = 2,
+    /// <summary>
+    /// Downlink codec error.
+    /// </summary>
+    [pbr::OriginalName("DOWNLINK_CODEC")] DownlinkCodec = 3,
+    /// <summary>
+    /// OTAA error.
+    /// </summary>
+    [pbr::OriginalName("OTAA")] Otaa = 4,
+    /// <summary>
+    /// Uplink frame-counter was reset.
+    /// </summary>
+    [pbr::OriginalName("UPLINK_F_CNT_RESET")] UplinkFCntReset = 5,
+    /// <summary>
+    /// Uplink MIC error.
+    /// </summary>
+    [pbr::OriginalName("UPLINK_MIC")] UplinkMic = 6,
+    /// <summary>
+    /// Uplink frame-counter retransmission.
+    /// </summary>
+    [pbr::OriginalName("UPLINK_F_CNT_RETRANSMISSION")] UplinkFCntRetransmission = 7,
+    /// <summary>
+    /// Downlink gateway error.
+    /// </summary>
+    [pbr::OriginalName("DOWNLINK_GATEWAY")] DownlinkGateway = 8,
+  }
+
+  #endregion
+
+  #region Messages
+  /// <summary>
+  /// Device information.
+  /// </summary>
+  public sealed partial class DeviceInfo : pb::IMessage<DeviceInfo>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DeviceInfo> _parser = new pb::MessageParser<DeviceInfo>(() => new DeviceInfo());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DeviceInfo> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceInfo() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceInfo(DeviceInfo other) : this() {
+      tenantId_ = other.tenantId_;
+      tenantName_ = other.tenantName_;
+      applicationId_ = other.applicationId_;
+      applicationName_ = other.applicationName_;
+      deviceProfileId_ = other.deviceProfileId_;
+      deviceProfileName_ = other.deviceProfileName_;
+      deviceName_ = other.deviceName_;
+      devEui_ = other.devEui_;
+      tags_ = other.tags_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DeviceInfo Clone() {
+      return new DeviceInfo(this);
+    }
+
+    /// <summary>Field number for the "tenant_id" field.</summary>
+    public const int TenantIdFieldNumber = 1;
+    private string tenantId_ = "";
+    /// <summary>
+    /// Tenant ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantId {
+      get { return tenantId_; }
+      set {
+        tenantId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tenant_name" field.</summary>
+    public const int TenantNameFieldNumber = 2;
+    private string tenantName_ = "";
+    /// <summary>
+    /// Tenant name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TenantName {
+      get { return tenantName_; }
+      set {
+        tenantName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "application_id" field.</summary>
+    public const int ApplicationIdFieldNumber = 3;
+    private string applicationId_ = "";
+    /// <summary>
+    /// Application ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationId {
+      get { return applicationId_; }
+      set {
+        applicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "application_name" field.</summary>
+    public const int ApplicationNameFieldNumber = 4;
+    private string applicationName_ = "";
+    /// <summary>
+    /// Application name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string ApplicationName {
+      get { return applicationName_; }
+      set {
+        applicationName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "device_profile_id" field.</summary>
+    public const int DeviceProfileIdFieldNumber = 5;
+    private string deviceProfileId_ = "";
+    /// <summary>
+    /// Device-profile ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeviceProfileId {
+      get { return deviceProfileId_; }
+      set {
+        deviceProfileId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "device_profile_name" field.</summary>
+    public const int DeviceProfileNameFieldNumber = 6;
+    private string deviceProfileName_ = "";
+    /// <summary>
+    /// Device-profile name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeviceProfileName {
+      get { return deviceProfileName_; }
+      set {
+        deviceProfileName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "device_name" field.</summary>
+    public const int DeviceNameFieldNumber = 7;
+    private string deviceName_ = "";
+    /// <summary>
+    /// Device name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeviceName {
+      get { return deviceName_; }
+      set {
+        deviceName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 8;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tags" field.</summary>
+    public const int TagsFieldNumber = 9;
+    private static readonly pbc::MapField<string, string>.Codec _map_tags_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 74);
+    private readonly pbc::MapField<string, string> tags_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Device-profile and device tags.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Tags {
+      get { return tags_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DeviceInfo);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DeviceInfo other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (TenantId != other.TenantId) return false;
+      if (TenantName != other.TenantName) return false;
+      if (ApplicationId != other.ApplicationId) return false;
+      if (ApplicationName != other.ApplicationName) return false;
+      if (DeviceProfileId != other.DeviceProfileId) return false;
+      if (DeviceProfileName != other.DeviceProfileName) return false;
+      if (DeviceName != other.DeviceName) return false;
+      if (DevEui != other.DevEui) return false;
+      if (!Tags.Equals(other.Tags)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (TenantId.Length != 0) hash ^= TenantId.GetHashCode();
+      if (TenantName.Length != 0) hash ^= TenantName.GetHashCode();
+      if (ApplicationId.Length != 0) hash ^= ApplicationId.GetHashCode();
+      if (ApplicationName.Length != 0) hash ^= ApplicationName.GetHashCode();
+      if (DeviceProfileId.Length != 0) hash ^= DeviceProfileId.GetHashCode();
+      if (DeviceProfileName.Length != 0) hash ^= DeviceProfileName.GetHashCode();
+      if (DeviceName.Length != 0) hash ^= DeviceName.GetHashCode();
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      hash ^= Tags.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (TenantName.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(TenantName);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(ApplicationId);
+      }
+      if (ApplicationName.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ApplicationName);
+      }
+      if (DeviceProfileId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(DeviceProfileId);
+      }
+      if (DeviceProfileName.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(DeviceProfileName);
+      }
+      if (DeviceName.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(DeviceName);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(DevEui);
+      }
+      tags_.WriteTo(output, _map_tags_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (TenantId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(TenantId);
+      }
+      if (TenantName.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(TenantName);
+      }
+      if (ApplicationId.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(ApplicationId);
+      }
+      if (ApplicationName.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(ApplicationName);
+      }
+      if (DeviceProfileId.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(DeviceProfileId);
+      }
+      if (DeviceProfileName.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(DeviceProfileName);
+      }
+      if (DeviceName.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(DeviceName);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(DevEui);
+      }
+      tags_.WriteTo(ref output, _map_tags_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (TenantId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantId);
+      }
+      if (TenantName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TenantName);
+      }
+      if (ApplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationId);
+      }
+      if (ApplicationName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ApplicationName);
+      }
+      if (DeviceProfileId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeviceProfileId);
+      }
+      if (DeviceProfileName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeviceProfileName);
+      }
+      if (DeviceName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeviceName);
+      }
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      size += tags_.CalculateSize(_map_tags_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DeviceInfo other) {
+      if (other == null) {
+        return;
+      }
+      if (other.TenantId.Length != 0) {
+        TenantId = other.TenantId;
+      }
+      if (other.TenantName.Length != 0) {
+        TenantName = other.TenantName;
+      }
+      if (other.ApplicationId.Length != 0) {
+        ApplicationId = other.ApplicationId;
+      }
+      if (other.ApplicationName.Length != 0) {
+        ApplicationName = other.ApplicationName;
+      }
+      if (other.DeviceProfileId.Length != 0) {
+        DeviceProfileId = other.DeviceProfileId;
+      }
+      if (other.DeviceProfileName.Length != 0) {
+        DeviceProfileName = other.DeviceProfileName;
+      }
+      if (other.DeviceName.Length != 0) {
+        DeviceName = other.DeviceName;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      tags_.Add(other.tags_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            TenantName = input.ReadString();
+            break;
+          }
+          case 26: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 34: {
+            ApplicationName = input.ReadString();
+            break;
+          }
+          case 42: {
+            DeviceProfileId = input.ReadString();
+            break;
+          }
+          case 50: {
+            DeviceProfileName = input.ReadString();
+            break;
+          }
+          case 58: {
+            DeviceName = input.ReadString();
+            break;
+          }
+          case 66: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 74: {
+            tags_.AddEntriesFrom(input, _map_tags_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            TenantId = input.ReadString();
+            break;
+          }
+          case 18: {
+            TenantName = input.ReadString();
+            break;
+          }
+          case 26: {
+            ApplicationId = input.ReadString();
+            break;
+          }
+          case 34: {
+            ApplicationName = input.ReadString();
+            break;
+          }
+          case 42: {
+            DeviceProfileId = input.ReadString();
+            break;
+          }
+          case 50: {
+            DeviceProfileName = input.ReadString();
+            break;
+          }
+          case 58: {
+            DeviceName = input.ReadString();
+            break;
+          }
+          case 66: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 74: {
+            tags_.AddEntriesFrom(ref input, _map_tags_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// UplinkEvent is the message sent when an uplink payload has been received.
+  /// </summary>
+  public sealed partial class UplinkEvent : pb::IMessage<UplinkEvent>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UplinkEvent> _parser = new pb::MessageParser<UplinkEvent>(() => new UplinkEvent());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UplinkEvent> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkEvent() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkEvent(UplinkEvent other) : this() {
+      deduplicationId_ = other.deduplicationId_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      deviceInfo_ = other.deviceInfo_ != null ? other.deviceInfo_.Clone() : null;
+      devAddr_ = other.devAddr_;
+      adr_ = other.adr_;
+      dr_ = other.dr_;
+      fCnt_ = other.fCnt_;
+      fPort_ = other.fPort_;
+      confirmed_ = other.confirmed_;
+      data_ = other.data_;
+      object_ = other.object_ != null ? other.object_.Clone() : null;
+      rxInfo_ = other.rxInfo_.Clone();
+      txInfo_ = other.txInfo_ != null ? other.txInfo_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkEvent Clone() {
+      return new UplinkEvent(this);
+    }
+
+    /// <summary>Field number for the "deduplication_id" field.</summary>
+    public const int DeduplicationIdFieldNumber = 1;
+    private string deduplicationId_ = "";
+    /// <summary>
+    /// Deduplication ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeduplicationId {
+      get { return deduplicationId_; }
+      set {
+        deduplicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_info" field.</summary>
+    public const int DeviceInfoFieldNumber = 3;
+    private global::Chirpstack.Integration.DeviceInfo deviceInfo_;
+    /// <summary>
+    /// Device information.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Integration.DeviceInfo DeviceInfo {
+      get { return deviceInfo_; }
+      set {
+        deviceInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "dev_addr" field.</summary>
+    public const int DevAddrFieldNumber = 4;
+    private string devAddr_ = "";
+    /// <summary>
+    /// Device address.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevAddr {
+      get { return devAddr_; }
+      set {
+        devAddr_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "adr" field.</summary>
+    public const int AdrFieldNumber = 5;
+    private bool adr_;
+    /// <summary>
+    /// Device has ADR bit set.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Adr {
+      get { return adr_; }
+      set {
+        adr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "dr" field.</summary>
+    public const int DrFieldNumber = 6;
+    private uint dr_;
+    /// <summary>
+    /// Data-rate.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint Dr {
+      get { return dr_; }
+      set {
+        dr_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "f_cnt" field.</summary>
+    public const int FCntFieldNumber = 7;
+    private uint fCnt_;
+    /// <summary>
+    /// Frame counter.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FCnt {
+      get { return fCnt_; }
+      set {
+        fCnt_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "f_port" field.</summary>
+    public const int FPortFieldNumber = 8;
+    private uint fPort_;
+    /// <summary>
+    /// Frame port.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FPort {
+      get { return fPort_; }
+      set {
+        fPort_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "confirmed" field.</summary>
+    public const int ConfirmedFieldNumber = 9;
+    private bool confirmed_;
+    /// <summary>
+    /// Uplink was of type confirmed.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Confirmed {
+      get { return confirmed_; }
+      set {
+        confirmed_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "data" field.</summary>
+    public const int DataFieldNumber = 10;
+    private pb::ByteString data_ = pb::ByteString.Empty;
+    /// <summary>
+    /// FRMPayload data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Data {
+      get { return data_; }
+      set {
+        data_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "object" field.</summary>
+    public const int ObjectFieldNumber = 11;
+    private global::Google.Protobuf.WellKnownTypes.Struct object_;
+    /// <summary>
+    /// Note that this is only set when a codec is configured in the Device Profile.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Struct Object {
+      get { return object_; }
+      set {
+        object_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_info" field.</summary>
+    public const int RxInfoFieldNumber = 12;
+    private static readonly pb::FieldCodec<global::Chirpstack.Gateway.UplinkRxInfo> _repeated_rxInfo_codec
+        = pb::FieldCodec.ForMessage(98, global::Chirpstack.Gateway.UplinkRxInfo.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo> rxInfo_ = new pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo>();
+    /// <summary>
+    /// Receiving gateway RX info.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo> RxInfo {
+      get { return rxInfo_; }
+    }
+
+    /// <summary>Field number for the "tx_info" field.</summary>
+    public const int TxInfoFieldNumber = 13;
+    private global::Chirpstack.Gateway.UplinkTxInfo txInfo_;
+    /// <summary>
+    /// TX info.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.UplinkTxInfo TxInfo {
+      get { return txInfo_; }
+      set {
+        txInfo_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UplinkEvent);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UplinkEvent other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DeduplicationId != other.DeduplicationId) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(DeviceInfo, other.DeviceInfo)) return false;
+      if (DevAddr != other.DevAddr) return false;
+      if (Adr != other.Adr) return false;
+      if (Dr != other.Dr) return false;
+      if (FCnt != other.FCnt) return false;
+      if (FPort != other.FPort) return false;
+      if (Confirmed != other.Confirmed) return false;
+      if (Data != other.Data) return false;
+      if (!object.Equals(Object, other.Object)) return false;
+      if(!rxInfo_.Equals(other.rxInfo_)) return false;
+      if (!object.Equals(TxInfo, other.TxInfo)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DeduplicationId.Length != 0) hash ^= DeduplicationId.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (deviceInfo_ != null) hash ^= DeviceInfo.GetHashCode();
+      if (DevAddr.Length != 0) hash ^= DevAddr.GetHashCode();
+      if (Adr != false) hash ^= Adr.GetHashCode();
+      if (Dr != 0) hash ^= Dr.GetHashCode();
+      if (FCnt != 0) hash ^= FCnt.GetHashCode();
+      if (FPort != 0) hash ^= FPort.GetHashCode();
+      if (Confirmed != false) hash ^= Confirmed.GetHashCode();
+      if (Data.Length != 0) hash ^= Data.GetHashCode();
+      if (object_ != null) hash ^= Object.GetHashCode();
+      hash ^= rxInfo_.GetHashCode();
+      if (txInfo_ != null) hash ^= TxInfo.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(DevAddr);
+      }
+      if (Adr != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(Adr);
+      }
+      if (Dr != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(Dr);
+      }
+      if (FCnt != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(FCnt);
+      }
+      if (FPort != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(FPort);
+      }
+      if (Confirmed != false) {
+        output.WriteRawTag(72);
+        output.WriteBool(Confirmed);
+      }
+      if (Data.Length != 0) {
+        output.WriteRawTag(82);
+        output.WriteBytes(Data);
+      }
+      if (object_ != null) {
+        output.WriteRawTag(90);
+        output.WriteMessage(Object);
+      }
+      rxInfo_.WriteTo(output, _repeated_rxInfo_codec);
+      if (txInfo_ != null) {
+        output.WriteRawTag(106);
+        output.WriteMessage(TxInfo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(DevAddr);
+      }
+      if (Adr != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(Adr);
+      }
+      if (Dr != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(Dr);
+      }
+      if (FCnt != 0) {
+        output.WriteRawTag(56);
+        output.WriteUInt32(FCnt);
+      }
+      if (FPort != 0) {
+        output.WriteRawTag(64);
+        output.WriteUInt32(FPort);
+      }
+      if (Confirmed != false) {
+        output.WriteRawTag(72);
+        output.WriteBool(Confirmed);
+      }
+      if (Data.Length != 0) {
+        output.WriteRawTag(82);
+        output.WriteBytes(Data);
+      }
+      if (object_ != null) {
+        output.WriteRawTag(90);
+        output.WriteMessage(Object);
+      }
+      rxInfo_.WriteTo(ref output, _repeated_rxInfo_codec);
+      if (txInfo_ != null) {
+        output.WriteRawTag(106);
+        output.WriteMessage(TxInfo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DeduplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeduplicationId);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (deviceInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceInfo);
+      }
+      if (DevAddr.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevAddr);
+      }
+      if (Adr != false) {
+        size += 1 + 1;
+      }
+      if (Dr != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Dr);
+      }
+      if (FCnt != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FCnt);
+      }
+      if (FPort != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FPort);
+      }
+      if (Confirmed != false) {
+        size += 1 + 1;
+      }
+      if (Data.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Data);
+      }
+      if (object_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Object);
+      }
+      size += rxInfo_.CalculateSize(_repeated_rxInfo_codec);
+      if (txInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfo);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UplinkEvent other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DeduplicationId.Length != 0) {
+        DeduplicationId = other.DeduplicationId;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.deviceInfo_ != null) {
+        if (deviceInfo_ == null) {
+          DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+        }
+        DeviceInfo.MergeFrom(other.DeviceInfo);
+      }
+      if (other.DevAddr.Length != 0) {
+        DevAddr = other.DevAddr;
+      }
+      if (other.Adr != false) {
+        Adr = other.Adr;
+      }
+      if (other.Dr != 0) {
+        Dr = other.Dr;
+      }
+      if (other.FCnt != 0) {
+        FCnt = other.FCnt;
+      }
+      if (other.FPort != 0) {
+        FPort = other.FPort;
+      }
+      if (other.Confirmed != false) {
+        Confirmed = other.Confirmed;
+      }
+      if (other.Data.Length != 0) {
+        Data = other.Data;
+      }
+      if (other.object_ != null) {
+        if (object_ == null) {
+          Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+        }
+        Object.MergeFrom(other.Object);
+      }
+      rxInfo_.Add(other.rxInfo_);
+      if (other.txInfo_ != null) {
+        if (txInfo_ == null) {
+          TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+        }
+        TxInfo.MergeFrom(other.TxInfo);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            DevAddr = input.ReadString();
+            break;
+          }
+          case 40: {
+            Adr = input.ReadBool();
+            break;
+          }
+          case 48: {
+            Dr = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            FCnt = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            FPort = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            Confirmed = input.ReadBool();
+            break;
+          }
+          case 82: {
+            Data = input.ReadBytes();
+            break;
+          }
+          case 90: {
+            if (object_ == null) {
+              Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+            }
+            input.ReadMessage(Object);
+            break;
+          }
+          case 98: {
+            rxInfo_.AddEntriesFrom(input, _repeated_rxInfo_codec);
+            break;
+          }
+          case 106: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            DevAddr = input.ReadString();
+            break;
+          }
+          case 40: {
+            Adr = input.ReadBool();
+            break;
+          }
+          case 48: {
+            Dr = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            FCnt = input.ReadUInt32();
+            break;
+          }
+          case 64: {
+            FPort = input.ReadUInt32();
+            break;
+          }
+          case 72: {
+            Confirmed = input.ReadBool();
+            break;
+          }
+          case 82: {
+            Data = input.ReadBytes();
+            break;
+          }
+          case 90: {
+            if (object_ == null) {
+              Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+            }
+            input.ReadMessage(Object);
+            break;
+          }
+          case 98: {
+            rxInfo_.AddEntriesFrom(ref input, _repeated_rxInfo_codec);
+            break;
+          }
+          case 106: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// JoinEvent is the message sent when a device joined the network.
+  /// Note: this event is sent at the first uplink after OTAA.
+  /// </summary>
+  public sealed partial class JoinEvent : pb::IMessage<JoinEvent>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<JoinEvent> _parser = new pb::MessageParser<JoinEvent>(() => new JoinEvent());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<JoinEvent> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public JoinEvent() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public JoinEvent(JoinEvent other) : this() {
+      deduplicationId_ = other.deduplicationId_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      deviceInfo_ = other.deviceInfo_ != null ? other.deviceInfo_.Clone() : null;
+      devAddr_ = other.devAddr_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public JoinEvent Clone() {
+      return new JoinEvent(this);
+    }
+
+    /// <summary>Field number for the "deduplication_id" field.</summary>
+    public const int DeduplicationIdFieldNumber = 1;
+    private string deduplicationId_ = "";
+    /// <summary>
+    /// Deduplication ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeduplicationId {
+      get { return deduplicationId_; }
+      set {
+        deduplicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_info" field.</summary>
+    public const int DeviceInfoFieldNumber = 3;
+    private global::Chirpstack.Integration.DeviceInfo deviceInfo_;
+    /// <summary>
+    /// Device info.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Integration.DeviceInfo DeviceInfo {
+      get { return deviceInfo_; }
+      set {
+        deviceInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "dev_addr" field.</summary>
+    public const int DevAddrFieldNumber = 4;
+    private string devAddr_ = "";
+    /// <summary>
+    /// Device address.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevAddr {
+      get { return devAddr_; }
+      set {
+        devAddr_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as JoinEvent);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(JoinEvent other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DeduplicationId != other.DeduplicationId) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(DeviceInfo, other.DeviceInfo)) return false;
+      if (DevAddr != other.DevAddr) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DeduplicationId.Length != 0) hash ^= DeduplicationId.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (deviceInfo_ != null) hash ^= DeviceInfo.GetHashCode();
+      if (DevAddr.Length != 0) hash ^= DevAddr.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(DevAddr);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (DevAddr.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(DevAddr);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DeduplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeduplicationId);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (deviceInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceInfo);
+      }
+      if (DevAddr.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevAddr);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(JoinEvent other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DeduplicationId.Length != 0) {
+        DeduplicationId = other.DeduplicationId;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.deviceInfo_ != null) {
+        if (deviceInfo_ == null) {
+          DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+        }
+        DeviceInfo.MergeFrom(other.DeviceInfo);
+      }
+      if (other.DevAddr.Length != 0) {
+        DevAddr = other.DevAddr;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            DevAddr = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            DevAddr = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// AckEvent is the message sent when a confirmation on a confirmed downlink
+  /// has been received -or- when the downlink timed out.
+  /// </summary>
+  public sealed partial class AckEvent : pb::IMessage<AckEvent>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<AckEvent> _parser = new pb::MessageParser<AckEvent>(() => new AckEvent());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<AckEvent> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AckEvent() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AckEvent(AckEvent other) : this() {
+      deduplicationId_ = other.deduplicationId_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      deviceInfo_ = other.deviceInfo_ != null ? other.deviceInfo_.Clone() : null;
+      queueItemId_ = other.queueItemId_;
+      acknowledged_ = other.acknowledged_;
+      fCntDown_ = other.fCntDown_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public AckEvent Clone() {
+      return new AckEvent(this);
+    }
+
+    /// <summary>Field number for the "deduplication_id" field.</summary>
+    public const int DeduplicationIdFieldNumber = 1;
+    private string deduplicationId_ = "";
+    /// <summary>
+    /// Deduplication ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeduplicationId {
+      get { return deduplicationId_; }
+      set {
+        deduplicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_info" field.</summary>
+    public const int DeviceInfoFieldNumber = 3;
+    private global::Chirpstack.Integration.DeviceInfo deviceInfo_;
+    /// <summary>
+    /// Device info.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Integration.DeviceInfo DeviceInfo {
+      get { return deviceInfo_; }
+      set {
+        deviceInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "queue_item_id" field.</summary>
+    public const int QueueItemIdFieldNumber = 4;
+    private string queueItemId_ = "";
+    /// <summary>
+    /// Downlink queue item ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string QueueItemId {
+      get { return queueItemId_; }
+      set {
+        queueItemId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "acknowledged" field.</summary>
+    public const int AcknowledgedFieldNumber = 5;
+    private bool acknowledged_;
+    /// <summary>
+    /// Frame was acknowledged.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Acknowledged {
+      get { return acknowledged_; }
+      set {
+        acknowledged_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "f_cnt_down" field.</summary>
+    public const int FCntDownFieldNumber = 6;
+    private uint fCntDown_;
+    /// <summary>
+    /// Downlink frame counter to which the acknowledgement relates.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FCntDown {
+      get { return fCntDown_; }
+      set {
+        fCntDown_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as AckEvent);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(AckEvent other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DeduplicationId != other.DeduplicationId) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(DeviceInfo, other.DeviceInfo)) return false;
+      if (QueueItemId != other.QueueItemId) return false;
+      if (Acknowledged != other.Acknowledged) return false;
+      if (FCntDown != other.FCntDown) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DeduplicationId.Length != 0) hash ^= DeduplicationId.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (deviceInfo_ != null) hash ^= DeviceInfo.GetHashCode();
+      if (QueueItemId.Length != 0) hash ^= QueueItemId.GetHashCode();
+      if (Acknowledged != false) hash ^= Acknowledged.GetHashCode();
+      if (FCntDown != 0) hash ^= FCntDown.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (QueueItemId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(QueueItemId);
+      }
+      if (Acknowledged != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(Acknowledged);
+      }
+      if (FCntDown != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(FCntDown);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (QueueItemId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(QueueItemId);
+      }
+      if (Acknowledged != false) {
+        output.WriteRawTag(40);
+        output.WriteBool(Acknowledged);
+      }
+      if (FCntDown != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(FCntDown);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DeduplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeduplicationId);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (deviceInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceInfo);
+      }
+      if (QueueItemId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(QueueItemId);
+      }
+      if (Acknowledged != false) {
+        size += 1 + 1;
+      }
+      if (FCntDown != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FCntDown);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(AckEvent other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DeduplicationId.Length != 0) {
+        DeduplicationId = other.DeduplicationId;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.deviceInfo_ != null) {
+        if (deviceInfo_ == null) {
+          DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+        }
+        DeviceInfo.MergeFrom(other.DeviceInfo);
+      }
+      if (other.QueueItemId.Length != 0) {
+        QueueItemId = other.QueueItemId;
+      }
+      if (other.Acknowledged != false) {
+        Acknowledged = other.Acknowledged;
+      }
+      if (other.FCntDown != 0) {
+        FCntDown = other.FCntDown;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            QueueItemId = input.ReadString();
+            break;
+          }
+          case 40: {
+            Acknowledged = input.ReadBool();
+            break;
+          }
+          case 48: {
+            FCntDown = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            QueueItemId = input.ReadString();
+            break;
+          }
+          case 40: {
+            Acknowledged = input.ReadBool();
+            break;
+          }
+          case 48: {
+            FCntDown = input.ReadUInt32();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// TxAckEvent is the message sent when a downlink was acknowledged by the gateway
+  /// for transmission. As a downlink can be scheduled in the future, this event
+  /// does not confirm that the message has already been transmitted.
+  /// </summary>
+  public sealed partial class TxAckEvent : pb::IMessage<TxAckEvent>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<TxAckEvent> _parser = new pb::MessageParser<TxAckEvent>(() => new TxAckEvent());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<TxAckEvent> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TxAckEvent() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TxAckEvent(TxAckEvent other) : this() {
+      downlinkId_ = other.downlinkId_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      deviceInfo_ = other.deviceInfo_ != null ? other.deviceInfo_.Clone() : null;
+      queueItemId_ = other.queueItemId_;
+      fCntDown_ = other.fCntDown_;
+      gatewayId_ = other.gatewayId_;
+      txInfo_ = other.txInfo_ != null ? other.txInfo_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TxAckEvent Clone() {
+      return new TxAckEvent(this);
+    }
+
+    /// <summary>Field number for the "downlink_id" field.</summary>
+    public const int DownlinkIdFieldNumber = 1;
+    private uint downlinkId_;
+    /// <summary>
+    /// Downlink ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint DownlinkId {
+      get { return downlinkId_; }
+      set {
+        downlinkId_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_info" field.</summary>
+    public const int DeviceInfoFieldNumber = 3;
+    private global::Chirpstack.Integration.DeviceInfo deviceInfo_;
+    /// <summary>
+    /// Device info.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Integration.DeviceInfo DeviceInfo {
+      get { return deviceInfo_; }
+      set {
+        deviceInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "queue_item_id" field.</summary>
+    public const int QueueItemIdFieldNumber = 4;
+    private string queueItemId_ = "";
+    /// <summary>
+    /// Downlink queue item ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string QueueItemId {
+      get { return queueItemId_; }
+      set {
+        queueItemId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "f_cnt_down" field.</summary>
+    public const int FCntDownFieldNumber = 5;
+    private uint fCntDown_;
+    /// <summary>
+    /// Downlink frame-counter.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FCntDown {
+      get { return fCntDown_; }
+      set {
+        fCntDown_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 6;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tx_info" field.</summary>
+    public const int TxInfoFieldNumber = 7;
+    private global::Chirpstack.Gateway.DownlinkTxInfo txInfo_;
+    /// <summary>
+    /// TX info.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.DownlinkTxInfo TxInfo {
+      get { return txInfo_; }
+      set {
+        txInfo_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as TxAckEvent);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(TxAckEvent other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DownlinkId != other.DownlinkId) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(DeviceInfo, other.DeviceInfo)) return false;
+      if (QueueItemId != other.QueueItemId) return false;
+      if (FCntDown != other.FCntDown) return false;
+      if (GatewayId != other.GatewayId) return false;
+      if (!object.Equals(TxInfo, other.TxInfo)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DownlinkId != 0) hash ^= DownlinkId.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (deviceInfo_ != null) hash ^= DeviceInfo.GetHashCode();
+      if (QueueItemId.Length != 0) hash ^= QueueItemId.GetHashCode();
+      if (FCntDown != 0) hash ^= FCntDown.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (txInfo_ != null) hash ^= TxInfo.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DownlinkId != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(DownlinkId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (QueueItemId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(QueueItemId);
+      }
+      if (FCntDown != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(FCntDown);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(GatewayId);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(58);
+        output.WriteMessage(TxInfo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DownlinkId != 0) {
+        output.WriteRawTag(8);
+        output.WriteUInt32(DownlinkId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (QueueItemId.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(QueueItemId);
+      }
+      if (FCntDown != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(FCntDown);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(GatewayId);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(58);
+        output.WriteMessage(TxInfo);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DownlinkId != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(DownlinkId);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (deviceInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceInfo);
+      }
+      if (QueueItemId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(QueueItemId);
+      }
+      if (FCntDown != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FCntDown);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (txInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfo);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(TxAckEvent other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DownlinkId != 0) {
+        DownlinkId = other.DownlinkId;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.deviceInfo_ != null) {
+        if (deviceInfo_ == null) {
+          DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+        }
+        DeviceInfo.MergeFrom(other.DeviceInfo);
+      }
+      if (other.QueueItemId.Length != 0) {
+        QueueItemId = other.QueueItemId;
+      }
+      if (other.FCntDown != 0) {
+        FCntDown = other.FCntDown;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      if (other.txInfo_ != null) {
+        if (txInfo_ == null) {
+          TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+        }
+        TxInfo.MergeFrom(other.TxInfo);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 8: {
+            DownlinkId = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            QueueItemId = input.ReadString();
+            break;
+          }
+          case 40: {
+            FCntDown = input.ReadUInt32();
+            break;
+          }
+          case 50: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 58: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 8: {
+            DownlinkId = input.ReadUInt32();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            QueueItemId = input.ReadString();
+            break;
+          }
+          case 40: {
+            FCntDown = input.ReadUInt32();
+            break;
+          }
+          case 50: {
+            GatewayId = input.ReadString();
+            break;
+          }
+          case 58: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// LogEvent is the message sent when a device-related log was sent.
+  /// </summary>
+  public sealed partial class LogEvent : pb::IMessage<LogEvent>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<LogEvent> _parser = new pb::MessageParser<LogEvent>(() => new LogEvent());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<LogEvent> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LogEvent() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LogEvent(LogEvent other) : this() {
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      deviceInfo_ = other.deviceInfo_ != null ? other.deviceInfo_.Clone() : null;
+      level_ = other.level_;
+      code_ = other.code_;
+      description_ = other.description_;
+      context_ = other.context_.Clone();
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LogEvent Clone() {
+      return new LogEvent(this);
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 1;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_info" field.</summary>
+    public const int DeviceInfoFieldNumber = 2;
+    private global::Chirpstack.Integration.DeviceInfo deviceInfo_;
+    /// <summary>
+    /// Device info.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Integration.DeviceInfo DeviceInfo {
+      get { return deviceInfo_; }
+      set {
+        deviceInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "level" field.</summary>
+    public const int LevelFieldNumber = 3;
+    private global::Chirpstack.Integration.LogLevel level_ = global::Chirpstack.Integration.LogLevel.Info;
+    /// <summary>
+    /// Log level.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Integration.LogLevel Level {
+      get { return level_; }
+      set {
+        level_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "code" field.</summary>
+    public const int CodeFieldNumber = 4;
+    private global::Chirpstack.Integration.LogCode code_ = global::Chirpstack.Integration.LogCode.Unknown;
+    /// <summary>
+    /// Log code.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Integration.LogCode Code {
+      get { return code_; }
+      set {
+        code_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "description" field.</summary>
+    public const int DescriptionFieldNumber = 5;
+    private string description_ = "";
+    /// <summary>
+    /// Description message.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Description {
+      get { return description_; }
+      set {
+        description_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "context" field.</summary>
+    public const int ContextFieldNumber = 6;
+    private static readonly pbc::MapField<string, string>.Codec _map_context_codec
+        = new pbc::MapField<string, string>.Codec(pb::FieldCodec.ForString(10, ""), pb::FieldCodec.ForString(18, ""), 50);
+    private readonly pbc::MapField<string, string> context_ = new pbc::MapField<string, string>();
+    /// <summary>
+    /// Context map.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::MapField<string, string> Context {
+      get { return context_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as LogEvent);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(LogEvent other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(DeviceInfo, other.DeviceInfo)) return false;
+      if (Level != other.Level) return false;
+      if (Code != other.Code) return false;
+      if (Description != other.Description) return false;
+      if (!Context.Equals(other.Context)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (deviceInfo_ != null) hash ^= DeviceInfo.GetHashCode();
+      if (Level != global::Chirpstack.Integration.LogLevel.Info) hash ^= Level.GetHashCode();
+      if (Code != global::Chirpstack.Integration.LogCode.Unknown) hash ^= Code.GetHashCode();
+      if (Description.Length != 0) hash ^= Description.GetHashCode();
+      hash ^= Context.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (time_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (Level != global::Chirpstack.Integration.LogLevel.Info) {
+        output.WriteRawTag(24);
+        output.WriteEnum((int) Level);
+      }
+      if (Code != global::Chirpstack.Integration.LogCode.Unknown) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Code);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Description);
+      }
+      context_.WriteTo(output, _map_context_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (time_ != null) {
+        output.WriteRawTag(10);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (Level != global::Chirpstack.Integration.LogLevel.Info) {
+        output.WriteRawTag(24);
+        output.WriteEnum((int) Level);
+      }
+      if (Code != global::Chirpstack.Integration.LogCode.Unknown) {
+        output.WriteRawTag(32);
+        output.WriteEnum((int) Code);
+      }
+      if (Description.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(Description);
+      }
+      context_.WriteTo(ref output, _map_context_codec);
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (deviceInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceInfo);
+      }
+      if (Level != global::Chirpstack.Integration.LogLevel.Info) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Level);
+      }
+      if (Code != global::Chirpstack.Integration.LogCode.Unknown) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Code);
+      }
+      if (Description.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Description);
+      }
+      size += context_.CalculateSize(_map_context_codec);
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(LogEvent other) {
+      if (other == null) {
+        return;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.deviceInfo_ != null) {
+        if (deviceInfo_ == null) {
+          DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+        }
+        DeviceInfo.MergeFrom(other.DeviceInfo);
+      }
+      if (other.Level != global::Chirpstack.Integration.LogLevel.Info) {
+        Level = other.Level;
+      }
+      if (other.Code != global::Chirpstack.Integration.LogCode.Unknown) {
+        Code = other.Code;
+      }
+      if (other.Description.Length != 0) {
+        Description = other.Description;
+      }
+      context_.Add(other.context_);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 18: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 24: {
+            Level = (global::Chirpstack.Integration.LogLevel) input.ReadEnum();
+            break;
+          }
+          case 32: {
+            Code = (global::Chirpstack.Integration.LogCode) input.ReadEnum();
+            break;
+          }
+          case 42: {
+            Description = input.ReadString();
+            break;
+          }
+          case 50: {
+            context_.AddEntriesFrom(input, _map_context_codec);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 18: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 24: {
+            Level = (global::Chirpstack.Integration.LogLevel) input.ReadEnum();
+            break;
+          }
+          case 32: {
+            Code = (global::Chirpstack.Integration.LogCode) input.ReadEnum();
+            break;
+          }
+          case 42: {
+            Description = input.ReadString();
+            break;
+          }
+          case 50: {
+            context_.AddEntriesFrom(ref input, _map_context_codec);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// StatusEvent is the message sent when a device-status mac-command was sent
+  /// by the device.
+  /// </summary>
+  public sealed partial class StatusEvent : pb::IMessage<StatusEvent>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<StatusEvent> _parser = new pb::MessageParser<StatusEvent>(() => new StatusEvent());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<StatusEvent> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StatusEvent() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StatusEvent(StatusEvent other) : this() {
+      deduplicationId_ = other.deduplicationId_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      deviceInfo_ = other.deviceInfo_ != null ? other.deviceInfo_.Clone() : null;
+      margin_ = other.margin_;
+      externalPowerSource_ = other.externalPowerSource_;
+      batteryLevelUnavailable_ = other.batteryLevelUnavailable_;
+      batteryLevel_ = other.batteryLevel_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public StatusEvent Clone() {
+      return new StatusEvent(this);
+    }
+
+    /// <summary>Field number for the "deduplication_id" field.</summary>
+    public const int DeduplicationIdFieldNumber = 1;
+    private string deduplicationId_ = "";
+    /// <summary>
+    /// Deduplication ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeduplicationId {
+      get { return deduplicationId_; }
+      set {
+        deduplicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_info" field.</summary>
+    public const int DeviceInfoFieldNumber = 3;
+    private global::Chirpstack.Integration.DeviceInfo deviceInfo_;
+    /// <summary>
+    /// Device info.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Integration.DeviceInfo DeviceInfo {
+      get { return deviceInfo_; }
+      set {
+        deviceInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "margin" field.</summary>
+    public const int MarginFieldNumber = 5;
+    private int margin_;
+    /// <summary>
+    /// The demodulation signal-to-noise ratio in dB for the last successfully
+    /// received device-status request by the Network Server.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int Margin {
+      get { return margin_; }
+      set {
+        margin_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "external_power_source" field.</summary>
+    public const int ExternalPowerSourceFieldNumber = 6;
+    private bool externalPowerSource_;
+    /// <summary>
+    /// Device is connected to an external power source.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool ExternalPowerSource {
+      get { return externalPowerSource_; }
+      set {
+        externalPowerSource_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "battery_level_unavailable" field.</summary>
+    public const int BatteryLevelUnavailableFieldNumber = 7;
+    private bool batteryLevelUnavailable_;
+    /// <summary>
+    /// Battery level is not available.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool BatteryLevelUnavailable {
+      get { return batteryLevelUnavailable_; }
+      set {
+        batteryLevelUnavailable_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "battery_level" field.</summary>
+    public const int BatteryLevelFieldNumber = 8;
+    private float batteryLevel_;
+    /// <summary>
+    /// Battery level.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public float BatteryLevel {
+      get { return batteryLevel_; }
+      set {
+        batteryLevel_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as StatusEvent);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(StatusEvent other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DeduplicationId != other.DeduplicationId) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(DeviceInfo, other.DeviceInfo)) return false;
+      if (Margin != other.Margin) return false;
+      if (ExternalPowerSource != other.ExternalPowerSource) return false;
+      if (BatteryLevelUnavailable != other.BatteryLevelUnavailable) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(BatteryLevel, other.BatteryLevel)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DeduplicationId.Length != 0) hash ^= DeduplicationId.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (deviceInfo_ != null) hash ^= DeviceInfo.GetHashCode();
+      if (Margin != 0) hash ^= Margin.GetHashCode();
+      if (ExternalPowerSource != false) hash ^= ExternalPowerSource.GetHashCode();
+      if (BatteryLevelUnavailable != false) hash ^= BatteryLevelUnavailable.GetHashCode();
+      if (BatteryLevel != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(BatteryLevel);
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (Margin != 0) {
+        output.WriteRawTag(40);
+        output.WriteInt32(Margin);
+      }
+      if (ExternalPowerSource != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(ExternalPowerSource);
+      }
+      if (BatteryLevelUnavailable != false) {
+        output.WriteRawTag(56);
+        output.WriteBool(BatteryLevelUnavailable);
+      }
+      if (BatteryLevel != 0F) {
+        output.WriteRawTag(69);
+        output.WriteFloat(BatteryLevel);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (Margin != 0) {
+        output.WriteRawTag(40);
+        output.WriteInt32(Margin);
+      }
+      if (ExternalPowerSource != false) {
+        output.WriteRawTag(48);
+        output.WriteBool(ExternalPowerSource);
+      }
+      if (BatteryLevelUnavailable != false) {
+        output.WriteRawTag(56);
+        output.WriteBool(BatteryLevelUnavailable);
+      }
+      if (BatteryLevel != 0F) {
+        output.WriteRawTag(69);
+        output.WriteFloat(BatteryLevel);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DeduplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeduplicationId);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (deviceInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceInfo);
+      }
+      if (Margin != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Margin);
+      }
+      if (ExternalPowerSource != false) {
+        size += 1 + 1;
+      }
+      if (BatteryLevelUnavailable != false) {
+        size += 1 + 1;
+      }
+      if (BatteryLevel != 0F) {
+        size += 1 + 4;
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(StatusEvent other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DeduplicationId.Length != 0) {
+        DeduplicationId = other.DeduplicationId;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.deviceInfo_ != null) {
+        if (deviceInfo_ == null) {
+          DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+        }
+        DeviceInfo.MergeFrom(other.DeviceInfo);
+      }
+      if (other.Margin != 0) {
+        Margin = other.Margin;
+      }
+      if (other.ExternalPowerSource != false) {
+        ExternalPowerSource = other.ExternalPowerSource;
+      }
+      if (other.BatteryLevelUnavailable != false) {
+        BatteryLevelUnavailable = other.BatteryLevelUnavailable;
+      }
+      if (other.BatteryLevel != 0F) {
+        BatteryLevel = other.BatteryLevel;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 40: {
+            Margin = input.ReadInt32();
+            break;
+          }
+          case 48: {
+            ExternalPowerSource = input.ReadBool();
+            break;
+          }
+          case 56: {
+            BatteryLevelUnavailable = input.ReadBool();
+            break;
+          }
+          case 69: {
+            BatteryLevel = input.ReadFloat();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 40: {
+            Margin = input.ReadInt32();
+            break;
+          }
+          case 48: {
+            ExternalPowerSource = input.ReadBool();
+            break;
+          }
+          case 56: {
+            BatteryLevelUnavailable = input.ReadBool();
+            break;
+          }
+          case 69: {
+            BatteryLevel = input.ReadFloat();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// LocationEvent is the message sent when a geolocation resolve was returned.
+  /// </summary>
+  public sealed partial class LocationEvent : pb::IMessage<LocationEvent>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<LocationEvent> _parser = new pb::MessageParser<LocationEvent>(() => new LocationEvent());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<LocationEvent> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LocationEvent() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LocationEvent(LocationEvent other) : this() {
+      deduplicationId_ = other.deduplicationId_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      deviceInfo_ = other.deviceInfo_ != null ? other.deviceInfo_.Clone() : null;
+      location_ = other.location_ != null ? other.location_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public LocationEvent Clone() {
+      return new LocationEvent(this);
+    }
+
+    /// <summary>Field number for the "deduplication_id" field.</summary>
+    public const int DeduplicationIdFieldNumber = 1;
+    private string deduplicationId_ = "";
+    /// <summary>
+    /// Deduplication ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeduplicationId {
+      get { return deduplicationId_; }
+      set {
+        deduplicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_info" field.</summary>
+    public const int DeviceInfoFieldNumber = 3;
+    private global::Chirpstack.Integration.DeviceInfo deviceInfo_;
+    /// <summary>
+    /// Device info.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Integration.DeviceInfo DeviceInfo {
+      get { return deviceInfo_; }
+      set {
+        deviceInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "location" field.</summary>
+    public const int LocationFieldNumber = 4;
+    private global::Chirpstack.Common.Location location_;
+    /// <summary>
+    /// Location.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.Location Location {
+      get { return location_; }
+      set {
+        location_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as LocationEvent);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(LocationEvent other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DeduplicationId != other.DeduplicationId) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(DeviceInfo, other.DeviceInfo)) return false;
+      if (!object.Equals(Location, other.Location)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DeduplicationId.Length != 0) hash ^= DeduplicationId.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (deviceInfo_ != null) hash ^= DeviceInfo.GetHashCode();
+      if (location_ != null) hash ^= Location.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(Location);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (location_ != null) {
+        output.WriteRawTag(34);
+        output.WriteMessage(Location);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DeduplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeduplicationId);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (deviceInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceInfo);
+      }
+      if (location_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Location);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(LocationEvent other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DeduplicationId.Length != 0) {
+        DeduplicationId = other.DeduplicationId;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.deviceInfo_ != null) {
+        if (deviceInfo_ == null) {
+          DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+        }
+        DeviceInfo.MergeFrom(other.DeviceInfo);
+      }
+      if (other.location_ != null) {
+        if (location_ == null) {
+          Location = new global::Chirpstack.Common.Location();
+        }
+        Location.MergeFrom(other.Location);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            if (location_ == null) {
+              Location = new global::Chirpstack.Common.Location();
+            }
+            input.ReadMessage(Location);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// IntegrationEvent is the message that can be sent by an integration.
+  /// It allows for sending events which are provided by an external integration
+  /// which are "not native" to ChirpStack.
+  /// </summary>
+  public sealed partial class IntegrationEvent : pb::IMessage<IntegrationEvent>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<IntegrationEvent> _parser = new pb::MessageParser<IntegrationEvent>(() => new IntegrationEvent());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<IntegrationEvent> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[8]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public IntegrationEvent() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public IntegrationEvent(IntegrationEvent other) : this() {
+      deduplicationId_ = other.deduplicationId_;
+      time_ = other.time_ != null ? other.time_.Clone() : null;
+      deviceInfo_ = other.deviceInfo_ != null ? other.deviceInfo_.Clone() : null;
+      integrationName_ = other.integrationName_;
+      eventType_ = other.eventType_;
+      object_ = other.object_ != null ? other.object_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public IntegrationEvent Clone() {
+      return new IntegrationEvent(this);
+    }
+
+    /// <summary>Field number for the "deduplication_id" field.</summary>
+    public const int DeduplicationIdFieldNumber = 1;
+    private string deduplicationId_ = "";
+    /// <summary>
+    /// Deduplication ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DeduplicationId {
+      get { return deduplicationId_; }
+      set {
+        deduplicationId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "time" field.</summary>
+    public const int TimeFieldNumber = 2;
+    private global::Google.Protobuf.WellKnownTypes.Timestamp time_;
+    /// <summary>
+    /// Timestamp.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Timestamp Time {
+      get { return time_; }
+      set {
+        time_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "device_info" field.</summary>
+    public const int DeviceInfoFieldNumber = 3;
+    private global::Chirpstack.Integration.DeviceInfo deviceInfo_;
+    /// <summary>
+    /// Device info.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Integration.DeviceInfo DeviceInfo {
+      get { return deviceInfo_; }
+      set {
+        deviceInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "integration_name" field.</summary>
+    public const int IntegrationNameFieldNumber = 4;
+    private string integrationName_ = "";
+    /// <summary>
+    /// Integration name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string IntegrationName {
+      get { return integrationName_; }
+      set {
+        integrationName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "event_type" field.</summary>
+    public const int EventTypeFieldNumber = 5;
+    private string eventType_ = "";
+    /// <summary>
+    /// Event type.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string EventType {
+      get { return eventType_; }
+      set {
+        eventType_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "object" field.</summary>
+    public const int ObjectFieldNumber = 6;
+    private global::Google.Protobuf.WellKnownTypes.Struct object_;
+    /// <summary>
+    /// Struct containing the event object.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Struct Object {
+      get { return object_; }
+      set {
+        object_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as IntegrationEvent);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(IntegrationEvent other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DeduplicationId != other.DeduplicationId) return false;
+      if (!object.Equals(Time, other.Time)) return false;
+      if (!object.Equals(DeviceInfo, other.DeviceInfo)) return false;
+      if (IntegrationName != other.IntegrationName) return false;
+      if (EventType != other.EventType) return false;
+      if (!object.Equals(Object, other.Object)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DeduplicationId.Length != 0) hash ^= DeduplicationId.GetHashCode();
+      if (time_ != null) hash ^= Time.GetHashCode();
+      if (deviceInfo_ != null) hash ^= DeviceInfo.GetHashCode();
+      if (IntegrationName.Length != 0) hash ^= IntegrationName.GetHashCode();
+      if (EventType.Length != 0) hash ^= EventType.GetHashCode();
+      if (object_ != null) hash ^= Object.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (IntegrationName.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(IntegrationName);
+      }
+      if (EventType.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(EventType);
+      }
+      if (object_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Object);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DeduplicationId.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DeduplicationId);
+      }
+      if (time_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(Time);
+      }
+      if (deviceInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(DeviceInfo);
+      }
+      if (IntegrationName.Length != 0) {
+        output.WriteRawTag(34);
+        output.WriteString(IntegrationName);
+      }
+      if (EventType.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteString(EventType);
+      }
+      if (object_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Object);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DeduplicationId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DeduplicationId);
+      }
+      if (time_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Time);
+      }
+      if (deviceInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceInfo);
+      }
+      if (IntegrationName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(IntegrationName);
+      }
+      if (EventType.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(EventType);
+      }
+      if (object_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Object);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(IntegrationEvent other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DeduplicationId.Length != 0) {
+        DeduplicationId = other.DeduplicationId;
+      }
+      if (other.time_ != null) {
+        if (time_ == null) {
+          Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+        }
+        Time.MergeFrom(other.Time);
+      }
+      if (other.deviceInfo_ != null) {
+        if (deviceInfo_ == null) {
+          DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+        }
+        DeviceInfo.MergeFrom(other.DeviceInfo);
+      }
+      if (other.IntegrationName.Length != 0) {
+        IntegrationName = other.IntegrationName;
+      }
+      if (other.EventType.Length != 0) {
+        EventType = other.EventType;
+      }
+      if (other.object_ != null) {
+        if (object_ == null) {
+          Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+        }
+        Object.MergeFrom(other.Object);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            IntegrationName = input.ReadString();
+            break;
+          }
+          case 42: {
+            EventType = input.ReadString();
+            break;
+          }
+          case 50: {
+            if (object_ == null) {
+              Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+            }
+            input.ReadMessage(Object);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DeduplicationId = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (time_ == null) {
+              Time = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+            }
+            input.ReadMessage(Time);
+            break;
+          }
+          case 26: {
+            if (deviceInfo_ == null) {
+              DeviceInfo = new global::Chirpstack.Integration.DeviceInfo();
+            }
+            input.ReadMessage(DeviceInfo);
+            break;
+          }
+          case 34: {
+            IntegrationName = input.ReadString();
+            break;
+          }
+          case 42: {
+            EventType = input.ReadString();
+            break;
+          }
+          case 50: {
+            if (object_ == null) {
+              Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+            }
+            input.ReadMessage(Object);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  /// <summary>
+  /// DownlinkCommand is the command to enqueue a downlink payload for the given
+  /// device.
+  /// </summary>
+  public sealed partial class DownlinkCommand : pb::IMessage<DownlinkCommand>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DownlinkCommand> _parser = new pb::MessageParser<DownlinkCommand>(() => new DownlinkCommand());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DownlinkCommand> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Integration.IntegrationReflection.Descriptor.MessageTypes[9]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkCommand() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkCommand(DownlinkCommand other) : this() {
+      id_ = other.id_;
+      devEui_ = other.devEui_;
+      confirmed_ = other.confirmed_;
+      fPort_ = other.fPort_;
+      data_ = other.data_;
+      object_ = other.object_ != null ? other.object_.Clone() : null;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkCommand Clone() {
+      return new DownlinkCommand(this);
+    }
+
+    /// <summary>Field number for the "id" field.</summary>
+    public const int IdFieldNumber = 1;
+    private string id_ = "";
+    /// <summary>
+    /// ID (UUID).
+    /// If left blank, a random UUID will be generated.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Id {
+      get { return id_; }
+      set {
+        id_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 2;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "confirmed" field.</summary>
+    public const int ConfirmedFieldNumber = 3;
+    private bool confirmed_;
+    /// <summary>
+    /// Confirmed.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Confirmed {
+      get { return confirmed_; }
+      set {
+        confirmed_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "f_port" field.</summary>
+    public const int FPortFieldNumber = 4;
+    private uint fPort_;
+    /// <summary>
+    /// FPort (must be > 0).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint FPort {
+      get { return fPort_; }
+      set {
+        fPort_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "data" field.</summary>
+    public const int DataFieldNumber = 5;
+    private pb::ByteString data_ = pb::ByteString.Empty;
+    /// <summary>
+    /// Data.
+    /// Or use the json_object field when a codec has been configured.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString Data {
+      get { return data_; }
+      set {
+        data_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "object" field.</summary>
+    public const int ObjectFieldNumber = 6;
+    private global::Google.Protobuf.WellKnownTypes.Struct object_;
+    /// <summary>
+    /// Only use this when a codec has been configured that can encode this
+    /// object to bytes.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Google.Protobuf.WellKnownTypes.Struct Object {
+      get { return object_; }
+      set {
+        object_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DownlinkCommand);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DownlinkCommand other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Id != other.Id) return false;
+      if (DevEui != other.DevEui) return false;
+      if (Confirmed != other.Confirmed) return false;
+      if (FPort != other.FPort) return false;
+      if (Data != other.Data) return false;
+      if (!object.Equals(Object, other.Object)) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Id.Length != 0) hash ^= Id.GetHashCode();
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (Confirmed != false) hash ^= Confirmed.GetHashCode();
+      if (FPort != 0) hash ^= FPort.GetHashCode();
+      if (Data.Length != 0) hash ^= Data.GetHashCode();
+      if (object_ != null) hash ^= Object.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(DevEui);
+      }
+      if (Confirmed != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(Confirmed);
+      }
+      if (FPort != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(FPort);
+      }
+      if (Data.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteBytes(Data);
+      }
+      if (object_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Object);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (Id.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Id);
+      }
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(DevEui);
+      }
+      if (Confirmed != false) {
+        output.WriteRawTag(24);
+        output.WriteBool(Confirmed);
+      }
+      if (FPort != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(FPort);
+      }
+      if (Data.Length != 0) {
+        output.WriteRawTag(42);
+        output.WriteBytes(Data);
+      }
+      if (object_ != null) {
+        output.WriteRawTag(50);
+        output.WriteMessage(Object);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (Id.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Id);
+      }
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (Confirmed != false) {
+        size += 1 + 1;
+      }
+      if (FPort != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(FPort);
+      }
+      if (Data.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(Data);
+      }
+      if (object_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(Object);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DownlinkCommand other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Id.Length != 0) {
+        Id = other.Id;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.Confirmed != false) {
+        Confirmed = other.Confirmed;
+      }
+      if (other.FPort != 0) {
+        FPort = other.FPort;
+      }
+      if (other.Data.Length != 0) {
+        Data = other.Data;
+      }
+      if (other.object_ != null) {
+        if (object_ == null) {
+          Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+        }
+        Object.MergeFrom(other.Object);
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 24: {
+            Confirmed = input.ReadBool();
+            break;
+          }
+          case 32: {
+            FPort = input.ReadUInt32();
+            break;
+          }
+          case 42: {
+            Data = input.ReadBytes();
+            break;
+          }
+          case 50: {
+            if (object_ == null) {
+              Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+            }
+            input.ReadMessage(Object);
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            Id = input.ReadString();
+            break;
+          }
+          case 18: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 24: {
+            Confirmed = input.ReadBool();
+            break;
+          }
+          case 32: {
+            FPort = input.ReadUInt32();
+            break;
+          }
+          case 42: {
+            Data = input.ReadBytes();
+            break;
+          }
+          case 50: {
+            if (object_ == null) {
+              Object = new global::Google.Protobuf.WellKnownTypes.Struct();
+            }
+            input.ReadMessage(Object);
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/api/csharp/Chirpstack/meta/Meta.cs b/api/csharp/Chirpstack/meta/Meta.cs
new file mode 100644
index 00000000..4bf0fc70
--- /dev/null
+++ b/api/csharp/Chirpstack/meta/Meta.cs
@@ -0,0 +1,968 @@
+// <auto-generated>
+//     Generated by the protocol buffer compiler.  DO NOT EDIT!
+//     source: meta/meta.proto
+// </auto-generated>
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chirpstack.Meta {
+
+  /// <summary>Holder for reflection information generated from meta/meta.proto</summary>
+  public static partial class MetaReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for meta/meta.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static MetaReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "Cg9tZXRhL21ldGEucHJvdG8SBG1ldGEaE2NvbW1vbi9jb21tb24ucHJvdG8a",
+            "C2d3L2d3LnByb3RvIvABCgpVcGxpbmtNZXRhEg8KB2Rldl9ldWkYASABKAkS",
+            "IQoHdHhfaW5mbxgCIAEoCzIQLmd3LlVwbGlua1R4SW5mbxIhCgdyeF9pbmZv",
+            "GAMgAygLMhAuZ3cuVXBsaW5rUnhJbmZvEh4KFnBoeV9wYXlsb2FkX2J5dGVf",
+            "Y291bnQYBCABKA0SHgoWbWFjX2NvbW1hbmRfYnl0ZV9jb3VudBgFIAEoDRIm",
+            "Ch5hcHBsaWNhdGlvbl9wYXlsb2FkX2J5dGVfY291bnQYBiABKA0SIwoMbWVz",
+            "c2FnZV90eXBlGAcgASgOMg0uY29tbW9uLk1UeXBlIoECCgxEb3dubGlua01l",
+            "dGESDwoHZGV2X2V1aRgBIAEoCRIaChJtdWx0aWNhc3RfZ3JvdXBfaWQYAiAB",
+            "KAkSIwoHdHhfaW5mbxgDIAEoCzISLmd3LkRvd25saW5rVHhJbmZvEh4KFnBo",
+            "eV9wYXlsb2FkX2J5dGVfY291bnQYBCABKA0SHgoWbWFjX2NvbW1hbmRfYnl0",
+            "ZV9jb3VudBgFIAEoDRImCh5hcHBsaWNhdGlvbl9wYXlsb2FkX2J5dGVfY291",
+            "bnQYBiABKA0SIwoMbWVzc2FnZV90eXBlGAcgASgOMg0uY29tbW9uLk1UeXBl",
+            "EhIKCmdhdGV3YXlfaWQYCCABKAlCaAoWaW8uY2hpcnBzdGFjay5hcGkubWV0",
+            "YUIJTWV0YVByb3RvUAFaL2dpdGh1Yi5jb20vY2hpcnBzdGFjay9jaGlycHN0",
+            "YWNrL2FwaS9nby92NC9tZXRhqgIPQ2hpcnBzdGFjay5NZXRhYgZwcm90bzM="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { global::Chirpstack.Common.CommonReflection.Descriptor, global::Chirpstack.Gateway.GwReflection.Descriptor, },
+          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Meta.UplinkMeta), global::Chirpstack.Meta.UplinkMeta.Parser, new[]{ "DevEui", "TxInfo", "RxInfo", "PhyPayloadByteCount", "MacCommandByteCount", "ApplicationPayloadByteCount", "MessageType" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chirpstack.Meta.DownlinkMeta), global::Chirpstack.Meta.DownlinkMeta.Parser, new[]{ "DevEui", "MulticastGroupId", "TxInfo", "PhyPayloadByteCount", "MacCommandByteCount", "ApplicationPayloadByteCount", "MessageType", "GatewayId" }, null, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  public sealed partial class UplinkMeta : pb::IMessage<UplinkMeta>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<UplinkMeta> _parser = new pb::MessageParser<UplinkMeta>(() => new UplinkMeta());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<UplinkMeta> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Meta.MetaReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkMeta() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkMeta(UplinkMeta other) : this() {
+      devEui_ = other.devEui_;
+      txInfo_ = other.txInfo_ != null ? other.txInfo_.Clone() : null;
+      rxInfo_ = other.rxInfo_.Clone();
+      phyPayloadByteCount_ = other.phyPayloadByteCount_;
+      macCommandByteCount_ = other.macCommandByteCount_;
+      applicationPayloadByteCount_ = other.applicationPayloadByteCount_;
+      messageType_ = other.messageType_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public UplinkMeta Clone() {
+      return new UplinkMeta(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tx_info" field.</summary>
+    public const int TxInfoFieldNumber = 2;
+    private global::Chirpstack.Gateway.UplinkTxInfo txInfo_;
+    /// <summary>
+    /// TX meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.UplinkTxInfo TxInfo {
+      get { return txInfo_; }
+      set {
+        txInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "rx_info" field.</summary>
+    public const int RxInfoFieldNumber = 3;
+    private static readonly pb::FieldCodec<global::Chirpstack.Gateway.UplinkRxInfo> _repeated_rxInfo_codec
+        = pb::FieldCodec.ForMessage(26, global::Chirpstack.Gateway.UplinkRxInfo.Parser);
+    private readonly pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo> rxInfo_ = new pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo>();
+    /// <summary>
+    /// RX meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pbc::RepeatedField<global::Chirpstack.Gateway.UplinkRxInfo> RxInfo {
+      get { return rxInfo_; }
+    }
+
+    /// <summary>Field number for the "phy_payload_byte_count" field.</summary>
+    public const int PhyPayloadByteCountFieldNumber = 4;
+    private uint phyPayloadByteCount_;
+    /// <summary>
+    /// PHYPayload byte count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint PhyPayloadByteCount {
+      get { return phyPayloadByteCount_; }
+      set {
+        phyPayloadByteCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "mac_command_byte_count" field.</summary>
+    public const int MacCommandByteCountFieldNumber = 5;
+    private uint macCommandByteCount_;
+    /// <summary>
+    /// MAC-Command byte count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint MacCommandByteCount {
+      get { return macCommandByteCount_; }
+      set {
+        macCommandByteCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "application_payload_byte_count" field.</summary>
+    public const int ApplicationPayloadByteCountFieldNumber = 6;
+    private uint applicationPayloadByteCount_;
+    /// <summary>
+    /// Application payload byte count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ApplicationPayloadByteCount {
+      get { return applicationPayloadByteCount_; }
+      set {
+        applicationPayloadByteCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "message_type" field.</summary>
+    public const int MessageTypeFieldNumber = 7;
+    private global::Chirpstack.Common.MType messageType_ = global::Chirpstack.Common.MType.JoinRequest;
+    /// <summary>
+    /// Message type.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.MType MessageType {
+      get { return messageType_; }
+      set {
+        messageType_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as UplinkMeta);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(UplinkMeta other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      if (!object.Equals(TxInfo, other.TxInfo)) return false;
+      if(!rxInfo_.Equals(other.rxInfo_)) return false;
+      if (PhyPayloadByteCount != other.PhyPayloadByteCount) return false;
+      if (MacCommandByteCount != other.MacCommandByteCount) return false;
+      if (ApplicationPayloadByteCount != other.ApplicationPayloadByteCount) return false;
+      if (MessageType != other.MessageType) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (txInfo_ != null) hash ^= TxInfo.GetHashCode();
+      hash ^= rxInfo_.GetHashCode();
+      if (PhyPayloadByteCount != 0) hash ^= PhyPayloadByteCount.GetHashCode();
+      if (MacCommandByteCount != 0) hash ^= MacCommandByteCount.GetHashCode();
+      if (ApplicationPayloadByteCount != 0) hash ^= ApplicationPayloadByteCount.GetHashCode();
+      if (MessageType != global::Chirpstack.Common.MType.JoinRequest) hash ^= MessageType.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxInfo);
+      }
+      rxInfo_.WriteTo(output, _repeated_rxInfo_codec);
+      if (PhyPayloadByteCount != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(PhyPayloadByteCount);
+      }
+      if (MacCommandByteCount != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(MacCommandByteCount);
+      }
+      if (ApplicationPayloadByteCount != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(ApplicationPayloadByteCount);
+      }
+      if (MessageType != global::Chirpstack.Common.MType.JoinRequest) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) MessageType);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(TxInfo);
+      }
+      rxInfo_.WriteTo(ref output, _repeated_rxInfo_codec);
+      if (PhyPayloadByteCount != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(PhyPayloadByteCount);
+      }
+      if (MacCommandByteCount != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(MacCommandByteCount);
+      }
+      if (ApplicationPayloadByteCount != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(ApplicationPayloadByteCount);
+      }
+      if (MessageType != global::Chirpstack.Common.MType.JoinRequest) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) MessageType);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (txInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfo);
+      }
+      size += rxInfo_.CalculateSize(_repeated_rxInfo_codec);
+      if (PhyPayloadByteCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(PhyPayloadByteCount);
+      }
+      if (MacCommandByteCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(MacCommandByteCount);
+      }
+      if (ApplicationPayloadByteCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ApplicationPayloadByteCount);
+      }
+      if (MessageType != global::Chirpstack.Common.MType.JoinRequest) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) MessageType);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(UplinkMeta other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.txInfo_ != null) {
+        if (txInfo_ == null) {
+          TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+        }
+        TxInfo.MergeFrom(other.TxInfo);
+      }
+      rxInfo_.Add(other.rxInfo_);
+      if (other.PhyPayloadByteCount != 0) {
+        PhyPayloadByteCount = other.PhyPayloadByteCount;
+      }
+      if (other.MacCommandByteCount != 0) {
+        MacCommandByteCount = other.MacCommandByteCount;
+      }
+      if (other.ApplicationPayloadByteCount != 0) {
+        ApplicationPayloadByteCount = other.ApplicationPayloadByteCount;
+      }
+      if (other.MessageType != global::Chirpstack.Common.MType.JoinRequest) {
+        MessageType = other.MessageType;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 26: {
+            rxInfo_.AddEntriesFrom(input, _repeated_rxInfo_codec);
+            break;
+          }
+          case 32: {
+            PhyPayloadByteCount = input.ReadUInt32();
+            break;
+          }
+          case 40: {
+            MacCommandByteCount = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            ApplicationPayloadByteCount = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            MessageType = (global::Chirpstack.Common.MType) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.UplinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 26: {
+            rxInfo_.AddEntriesFrom(ref input, _repeated_rxInfo_codec);
+            break;
+          }
+          case 32: {
+            PhyPayloadByteCount = input.ReadUInt32();
+            break;
+          }
+          case 40: {
+            MacCommandByteCount = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            ApplicationPayloadByteCount = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            MessageType = (global::Chirpstack.Common.MType) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  public sealed partial class DownlinkMeta : pb::IMessage<DownlinkMeta>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<DownlinkMeta> _parser = new pb::MessageParser<DownlinkMeta>(() => new DownlinkMeta());
+    private pb::UnknownFieldSet _unknownFields;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<DownlinkMeta> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chirpstack.Meta.MetaReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkMeta() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkMeta(DownlinkMeta other) : this() {
+      devEui_ = other.devEui_;
+      multicastGroupId_ = other.multicastGroupId_;
+      txInfo_ = other.txInfo_ != null ? other.txInfo_.Clone() : null;
+      phyPayloadByteCount_ = other.phyPayloadByteCount_;
+      macCommandByteCount_ = other.macCommandByteCount_;
+      applicationPayloadByteCount_ = other.applicationPayloadByteCount_;
+      messageType_ = other.messageType_;
+      gatewayId_ = other.gatewayId_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public DownlinkMeta Clone() {
+      return new DownlinkMeta(this);
+    }
+
+    /// <summary>Field number for the "dev_eui" field.</summary>
+    public const int DevEuiFieldNumber = 1;
+    private string devEui_ = "";
+    /// <summary>
+    /// Device EUI (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DevEui {
+      get { return devEui_; }
+      set {
+        devEui_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "multicast_group_id" field.</summary>
+    public const int MulticastGroupIdFieldNumber = 2;
+    private string multicastGroupId_ = "";
+    /// <summary>
+    /// Multicast Group ID (UUID).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string MulticastGroupId {
+      get { return multicastGroupId_; }
+      set {
+        multicastGroupId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "tx_info" field.</summary>
+    public const int TxInfoFieldNumber = 3;
+    private global::Chirpstack.Gateway.DownlinkTxInfo txInfo_;
+    /// <summary>
+    /// TX meta-data.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Gateway.DownlinkTxInfo TxInfo {
+      get { return txInfo_; }
+      set {
+        txInfo_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "phy_payload_byte_count" field.</summary>
+    public const int PhyPayloadByteCountFieldNumber = 4;
+    private uint phyPayloadByteCount_;
+    /// <summary>
+    /// PHYPayload byte count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint PhyPayloadByteCount {
+      get { return phyPayloadByteCount_; }
+      set {
+        phyPayloadByteCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "mac_command_byte_count" field.</summary>
+    public const int MacCommandByteCountFieldNumber = 5;
+    private uint macCommandByteCount_;
+    /// <summary>
+    /// MAC-Command byte count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint MacCommandByteCount {
+      get { return macCommandByteCount_; }
+      set {
+        macCommandByteCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "application_payload_byte_count" field.</summary>
+    public const int ApplicationPayloadByteCountFieldNumber = 6;
+    private uint applicationPayloadByteCount_;
+    /// <summary>
+    /// Application payload byte count.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint ApplicationPayloadByteCount {
+      get { return applicationPayloadByteCount_; }
+      set {
+        applicationPayloadByteCount_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "message_type" field.</summary>
+    public const int MessageTypeFieldNumber = 7;
+    private global::Chirpstack.Common.MType messageType_ = global::Chirpstack.Common.MType.JoinRequest;
+    /// <summary>
+    /// Message type.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::Chirpstack.Common.MType MessageType {
+      get { return messageType_; }
+      set {
+        messageType_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "gateway_id" field.</summary>
+    public const int GatewayIdFieldNumber = 8;
+    private string gatewayId_ = "";
+    /// <summary>
+    /// Gateway ID (EUI64).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string GatewayId {
+      get { return gatewayId_; }
+      set {
+        gatewayId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as DownlinkMeta);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(DownlinkMeta other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (DevEui != other.DevEui) return false;
+      if (MulticastGroupId != other.MulticastGroupId) return false;
+      if (!object.Equals(TxInfo, other.TxInfo)) return false;
+      if (PhyPayloadByteCount != other.PhyPayloadByteCount) return false;
+      if (MacCommandByteCount != other.MacCommandByteCount) return false;
+      if (ApplicationPayloadByteCount != other.ApplicationPayloadByteCount) return false;
+      if (MessageType != other.MessageType) return false;
+      if (GatewayId != other.GatewayId) return false;
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (DevEui.Length != 0) hash ^= DevEui.GetHashCode();
+      if (MulticastGroupId.Length != 0) hash ^= MulticastGroupId.GetHashCode();
+      if (txInfo_ != null) hash ^= TxInfo.GetHashCode();
+      if (PhyPayloadByteCount != 0) hash ^= PhyPayloadByteCount.GetHashCode();
+      if (MacCommandByteCount != 0) hash ^= MacCommandByteCount.GetHashCode();
+      if (ApplicationPayloadByteCount != 0) hash ^= ApplicationPayloadByteCount.GetHashCode();
+      if (MessageType != global::Chirpstack.Common.MType.JoinRequest) hash ^= MessageType.GetHashCode();
+      if (GatewayId.Length != 0) hash ^= GatewayId.GetHashCode();
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(MulticastGroupId);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(TxInfo);
+      }
+      if (PhyPayloadByteCount != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(PhyPayloadByteCount);
+      }
+      if (MacCommandByteCount != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(MacCommandByteCount);
+      }
+      if (ApplicationPayloadByteCount != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(ApplicationPayloadByteCount);
+      }
+      if (MessageType != global::Chirpstack.Common.MType.JoinRequest) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) MessageType);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (DevEui.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(DevEui);
+      }
+      if (MulticastGroupId.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(MulticastGroupId);
+      }
+      if (txInfo_ != null) {
+        output.WriteRawTag(26);
+        output.WriteMessage(TxInfo);
+      }
+      if (PhyPayloadByteCount != 0) {
+        output.WriteRawTag(32);
+        output.WriteUInt32(PhyPayloadByteCount);
+      }
+      if (MacCommandByteCount != 0) {
+        output.WriteRawTag(40);
+        output.WriteUInt32(MacCommandByteCount);
+      }
+      if (ApplicationPayloadByteCount != 0) {
+        output.WriteRawTag(48);
+        output.WriteUInt32(ApplicationPayloadByteCount);
+      }
+      if (MessageType != global::Chirpstack.Common.MType.JoinRequest) {
+        output.WriteRawTag(56);
+        output.WriteEnum((int) MessageType);
+      }
+      if (GatewayId.Length != 0) {
+        output.WriteRawTag(66);
+        output.WriteString(GatewayId);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (DevEui.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(DevEui);
+      }
+      if (MulticastGroupId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(MulticastGroupId);
+      }
+      if (txInfo_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(TxInfo);
+      }
+      if (PhyPayloadByteCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(PhyPayloadByteCount);
+      }
+      if (MacCommandByteCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(MacCommandByteCount);
+      }
+      if (ApplicationPayloadByteCount != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ApplicationPayloadByteCount);
+      }
+      if (MessageType != global::Chirpstack.Common.MType.JoinRequest) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) MessageType);
+      }
+      if (GatewayId.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(GatewayId);
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(DownlinkMeta other) {
+      if (other == null) {
+        return;
+      }
+      if (other.DevEui.Length != 0) {
+        DevEui = other.DevEui;
+      }
+      if (other.MulticastGroupId.Length != 0) {
+        MulticastGroupId = other.MulticastGroupId;
+      }
+      if (other.txInfo_ != null) {
+        if (txInfo_ == null) {
+          TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+        }
+        TxInfo.MergeFrom(other.TxInfo);
+      }
+      if (other.PhyPayloadByteCount != 0) {
+        PhyPayloadByteCount = other.PhyPayloadByteCount;
+      }
+      if (other.MacCommandByteCount != 0) {
+        MacCommandByteCount = other.MacCommandByteCount;
+      }
+      if (other.ApplicationPayloadByteCount != 0) {
+        ApplicationPayloadByteCount = other.ApplicationPayloadByteCount;
+      }
+      if (other.MessageType != global::Chirpstack.Common.MType.JoinRequest) {
+        MessageType = other.MessageType;
+      }
+      if (other.GatewayId.Length != 0) {
+        GatewayId = other.GatewayId;
+      }
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 26: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 32: {
+            PhyPayloadByteCount = input.ReadUInt32();
+            break;
+          }
+          case 40: {
+            MacCommandByteCount = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            ApplicationPayloadByteCount = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            MessageType = (global::Chirpstack.Common.MType) input.ReadEnum();
+            break;
+          }
+          case 66: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            break;
+          case 10: {
+            DevEui = input.ReadString();
+            break;
+          }
+          case 18: {
+            MulticastGroupId = input.ReadString();
+            break;
+          }
+          case 26: {
+            if (txInfo_ == null) {
+              TxInfo = new global::Chirpstack.Gateway.DownlinkTxInfo();
+            }
+            input.ReadMessage(TxInfo);
+            break;
+          }
+          case 32: {
+            PhyPayloadByteCount = input.ReadUInt32();
+            break;
+          }
+          case 40: {
+            MacCommandByteCount = input.ReadUInt32();
+            break;
+          }
+          case 48: {
+            ApplicationPayloadByteCount = input.ReadUInt32();
+            break;
+          }
+          case 56: {
+            MessageType = (global::Chirpstack.Common.MType) input.ReadEnum();
+            break;
+          }
+          case 66: {
+            GatewayId = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code