diff --git a/SfeduSchedule.Abstractions/ScheduleDTO.CS b/SfeduSchedule.Abstractions/ScheduleDTO.CS
new file mode 100644
index 0000000..eb9e40c
--- /dev/null
+++ b/SfeduSchedule.Abstractions/ScheduleDTO.CS
@@ -0,0 +1,709 @@
+//  
+// Вот этим сайтом https://app.quicktype.io/?l=csharp
+// Не является точной копией ответа, могут быть отличия
+
+#nullable enable
+#pragma warning disable CS8618
+#pragma warning disable CS8601
+#pragma warning disable CS8603
+
+namespace SfeduSchedule
+{
+    using System;
+    using System.Collections.Generic;
+
+    using System.Text.Json;
+    using System.Text.Json.Serialization;
+    using System.Globalization;
+
+    public partial class Schedule
+    {
+        [JsonPropertyName("_embedded")]
+        public Embedded Embedded { get; set; }
+
+        [JsonPropertyName("page")]
+        public Page Page { get; set; }
+    }
+
+    public partial class Embedded
+    {
+        [JsonPropertyName("events")]
+        public Event[] Events { get; set; }
+
+        [JsonPropertyName("course-unit-realizations")]
+        public CourseUnitRealization[] CourseUnitRealizations { get; set; }
+
+        [JsonPropertyName("cycle-realizations")]
+        public CycleRealization[] CycleRealizations { get; set; }
+
+        [JsonPropertyName("lesson-realization-teams")]
+        public LessonRealizationTeam[] LessonRealizationTeams { get; set; }
+
+        [JsonPropertyName("lesson-realizations")]
+        public LessonRealization[] LessonRealizations { get; set; }
+
+        [JsonPropertyName("event-locations")]
+        public EventLocation[] EventLocations { get; set; }
+
+        [JsonPropertyName("durations")]
+        public Duration[] Durations { get; set; }
+
+        [JsonPropertyName("event-rooms")]
+        public EventRoom[] EventRooms { get; set; }
+
+        [JsonPropertyName("rooms")]
+        public Room[] Rooms { get; set; }
+
+        [JsonPropertyName("buildings")]
+        public BuildingElement[] Buildings { get; set; }
+
+        [JsonPropertyName("event-teams")]
+        public EventTeam[] EventTeams { get; set; }
+
+        [JsonPropertyName("event-organizers")]
+        public EventOrganizer[] EventOrganizers { get; set; }
+
+        [JsonPropertyName("event-attendees")]
+        public EventAttendee[] EventAttendees { get; set; }
+
+        [JsonPropertyName("persons")]
+        public Person[] Persons { get; set; }
+    }
+
+    public partial class BuildingElement
+    {
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+
+        [JsonPropertyName("nameShort")]
+        public string NameShort { get; set; }
+
+        [JsonPropertyName("address")]
+        public string Address { get; set; }
+
+        [JsonPropertyName("searchableAddress")]
+        public string SearchableAddress { get; set; }
+
+        [JsonPropertyName("displayOrder")]
+        public long DisplayOrder { get; set; }
+
+        [JsonPropertyName("_links")]
+        public BuildingLinks Links { get; set; }
+
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+    }
+
+    public partial class BuildingLinks
+    {
+        [JsonPropertyName("self")]
+        public Self Self { get; set; }
+    }
+
+    public partial class Self
+    {
+        [JsonPropertyName("href")]
+        public string Href { get; set; }
+    }
+
+    public partial class CourseUnitRealization
+    {
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+
+        [JsonPropertyName("nameShort")]
+        public string NameShort { get; set; }
+
+        [JsonPropertyName("prototypeId")]
+        public Guid PrototypeId { get; set; }
+
+        [JsonPropertyName("_links")]
+        public CourseUnitRealizationLinks Links { get; set; }
+
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+    }
+
+    public partial class CourseUnitRealizationLinks
+    {
+        [JsonPropertyName("self")]
+        public Self Self { get; set; }
+
+        [JsonPropertyName("planning-period")]
+        public Self PlanningPeriod { get; set; }
+    }
+
+    public partial class CycleRealization
+    {
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+
+        [JsonPropertyName("nameShort")]
+        public string NameShort { get; set; }
+
+        [JsonPropertyName("code")]
+        public string Code { get; set; }
+
+        [JsonPropertyName("courseUnitRealizationNameShort")]
+        public string CourseUnitRealizationNameShort { get; set; }
+
+        [JsonPropertyName("_links")]
+        public CycleRealizationLinks Links { get; set; }
+
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+    }
+
+    public partial class CycleRealizationLinks
+    {
+        [JsonPropertyName("self")]
+        public Self Self { get; set; }
+
+        [JsonPropertyName("course-unit-realization")]
+        public Self CourseUnitRealization { get; set; }
+    }
+
+    public partial class Duration
+    {
+        [JsonPropertyName("eventId")]
+        public Guid EventId { get; set; }
+
+        [JsonPropertyName("value")]
+        public long Value { get; set; }
+
+        [JsonPropertyName("timeUnitId")]
+        public string TimeUnitId { get; set; }
+
+        [JsonPropertyName("minutes")]
+        public long Minutes { get; set; }
+
+        [JsonPropertyName("_links")]
+        public DurationLinks Links { get; set; }
+    }
+
+    public partial class DurationLinks
+    {
+        [JsonPropertyName("self")]
+        public Self[] Self { get; set; }
+
+        [JsonPropertyName("time-unit")]
+        public Self TimeUnit { get; set; }
+    }
+
+    public partial class EventAttendee
+    {
+        [JsonPropertyName("roleId")]
+        public string RoleId { get; set; }
+
+        [JsonPropertyName("roleName")]
+        public string RoleName { get; set; }
+
+        [JsonPropertyName("roleNamePlural")]
+        public string RoleNamePlural { get; set; }
+
+        [JsonPropertyName("roleDisplayOrder")]
+        public long RoleDisplayOrder { get; set; }
+
+        [JsonPropertyName("_links")]
+        public EventAttendeeLinks Links { get; set; }
+
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+    }
+
+    public partial class EventAttendeeLinks
+    {
+        [JsonPropertyName("self")]
+        public Self Self { get; set; }
+
+        [JsonPropertyName("event")]
+        public Self Event { get; set; }
+
+        [JsonPropertyName("person")]
+        public Self Person { get; set; }
+    }
+
+    public partial class EventLocation
+    {
+        [JsonPropertyName("eventId")]
+        public Guid EventId { get; set; }
+
+        [JsonPropertyName("customLocation")]
+        public string CustomLocation { get; set; }
+
+        [JsonPropertyName("_links")]
+        public EventLocationLinks Links { get; set; }
+    }
+
+    public partial class EventLocationLinks
+    {
+        [JsonPropertyName("self")]
+        public Self[] Self { get; set; }
+
+        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+        [JsonPropertyName("event-rooms")]
+        public Self EventRooms { get; set; }
+    }
+
+    public partial class EventOrganizer
+    {
+        [JsonPropertyName("eventId")]
+        public Guid EventId { get; set; }
+
+        [JsonPropertyName("_links")]
+        public EventOrganizerLinks Links { get; set; }
+    }
+
+    public partial class EventOrganizerLinks
+    {
+        [JsonPropertyName("self")]
+        public Self Self { get; set; }
+
+        [JsonPropertyName("event")]
+        public Self Event { get; set; }
+
+        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+        [JsonPropertyName("event-attendees")]
+        public EventAttendees? EventAttendees { get; set; }
+    }
+
+    public partial class EventRoom
+    {
+        [JsonPropertyName("_links")]
+        public EventRoomLinks Links { get; set; }
+
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+    }
+
+    public partial class EventRoomLinks
+    {
+        [JsonPropertyName("self")]
+        public Self Self { get; set; }
+
+        [JsonPropertyName("event")]
+        public Self Event { get; set; }
+
+        [JsonPropertyName("room")]
+        public Self Room { get; set; }
+    }
+
+    public partial class EventTeam
+    {
+        [JsonPropertyName("eventId")]
+        public Guid EventId { get; set; }
+
+        [JsonPropertyName("size")]
+        public long Size { get; set; }
+
+        [JsonPropertyName("_links")]
+        public EventTeamLinks Links { get; set; }
+    }
+
+    public partial class EventTeamLinks
+    {
+        [JsonPropertyName("self")]
+        public Self Self { get; set; }
+
+        [JsonPropertyName("event")]
+        public Self Event { get; set; }
+    }
+
+    public partial class Event
+    {
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+
+        [JsonPropertyName("nameShort")]
+        public string NameShort { get; set; }
+
+        [JsonPropertyName("description")]
+        public object Description { get; set; }
+
+        [JsonPropertyName("typeId")]
+        public string TypeId { get; set; }
+
+        [JsonPropertyName("formatId")]
+        public string FormatId { get; set; }
+
+        [JsonPropertyName("start")]
+        public DateTime Start { get; set; }
+
+        [JsonPropertyName("end")]
+        public DateTime End { get; set; }
+
+        [JsonPropertyName("startsAtLocal")]
+        public DateTime StartsAtLocal { get; set; }
+
+        [JsonPropertyName("endsAtLocal")]
+        public DateTime EndsAtLocal { get; set; }
+
+        [JsonPropertyName("startsAt")]
+        public DateTime StartsAt { get; set; }
+
+        [JsonPropertyName("endsAt")]
+        public DateTime EndsAt { get; set; }
+
+        [JsonPropertyName("holdingStatus")]
+        public HoldingStatus HoldingStatus { get; set; }
+
+        [JsonPropertyName("repeatedLessonRealization")]
+        public RepeatedLessonRealization RepeatedLessonRealization { get; set; }
+
+        [JsonPropertyName("userRoleIds")]
+        public string[] UserRoleIds { get; set; }
+
+        [JsonPropertyName("lessonTemplateId")]
+        public Guid LessonTemplateId { get; set; }
+
+        [JsonPropertyName("__version")]
+        public long Version { get; set; }
+
+        [JsonPropertyName("_links")]
+        public Dictionary Links { get; set; }
+
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+    }
+
+    public partial class HoldingStatus
+    {
+        [JsonPropertyName("id")]
+        public string Id { get; set; }
+
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+
+        [JsonPropertyName("audModifiedAt")]
+        public DateTimeOffset? AudModifiedAt { get; set; }
+
+        [JsonPropertyName("audModifiedBy")]
+        public Guid? AudModifiedBy { get; set; }
+
+        [JsonPropertyName("audModifiedBySystem")]
+        public bool? AudModifiedBySystem { get; set; }
+    }
+
+    public partial class RepeatedLessonRealization
+    {
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+
+        [JsonPropertyName("lessonTeamName")]
+        public string LessonTeamName { get; set; }
+    }
+
+    public partial class LessonRealizationTeam
+    {
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+
+        [JsonPropertyName("cycleRealizationId")]
+        public Guid CycleRealizationId { get; set; }
+
+        [JsonPropertyName("_links")]
+        public BuildingLinks Links { get; set; }
+
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+    }
+
+    public partial class LessonRealization
+    {
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+
+        [JsonPropertyName("nameShort")]
+        public string NameShort { get; set; }
+
+        [JsonPropertyName("prototypeId")]
+        public Guid PrototypeId { get; set; }
+
+        [JsonPropertyName("ordinal")]
+        public long Ordinal { get; set; }
+
+        [JsonPropertyName("_links")]
+        public BuildingLinks Links { get; set; }
+
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+    }
+
+    public partial class Person
+    {
+        [JsonPropertyName("lastName")]
+        public string LastName { get; set; }
+
+        [JsonPropertyName("firstName")]
+        public string FirstName { get; set; }
+
+        [JsonPropertyName("middleName")]
+        public string MiddleName { get; set; }
+
+        [JsonPropertyName("fullName")]
+        public string FullName { get; set; }
+
+        [JsonPropertyName("_links")]
+        public BuildingLinks Links { get; set; }
+
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+    }
+
+    public partial class Room
+    {
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+
+        [JsonPropertyName("nameShort")]
+        public string NameShort { get; set; }
+
+        [JsonPropertyName("building")]
+        public RoomBuilding Building { get; set; }
+
+        [JsonPropertyName("projectorAvailable")]
+        public bool ProjectorAvailable { get; set; }
+
+        [JsonPropertyName("totalCapacity")]
+        public long TotalCapacity { get; set; }
+
+        [JsonPropertyName("workingCapacity")]
+        public long WorkingCapacity { get; set; }
+
+        [JsonPropertyName("deletedAtUtc")]
+        public object DeletedAtUtc { get; set; }
+
+        [JsonPropertyName("_links")]
+        public RoomLinks Links { get; set; }
+
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+    }
+
+    public partial class RoomBuilding
+    {
+        [JsonPropertyName("id")]
+        public Guid Id { get; set; }
+
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+
+        [JsonPropertyName("nameShort")]
+        public string NameShort { get; set; }
+
+        [JsonPropertyName("address")]
+        public string Address { get; set; }
+
+        [JsonPropertyName("displayOrder")]
+        public long DisplayOrder { get; set; }
+    }
+
+    public partial class RoomLinks
+    {
+        [JsonPropertyName("self")]
+        public Self Self { get; set; }
+
+        [JsonPropertyName("type")]
+        public Self Type { get; set; }
+
+        [JsonPropertyName("building")]
+        public Self Building { get; set; }
+    }
+
+    public partial class Page
+    {
+        [JsonPropertyName("size")]
+        public long Size { get; set; }
+
+        [JsonPropertyName("totalElements")]
+        public long TotalElements { get; set; }
+
+        [JsonPropertyName("totalPages")]
+        public long TotalPages { get; set; }
+
+        [JsonPropertyName("number")]
+        public long Number { get; set; }
+    }
+
+    public partial struct EventAttendees
+    {
+        public Self Self;
+        public Self[] SelfArray;
+
+        public static implicit operator EventAttendees(Self Self) => new EventAttendees { Self = Self };
+        public static implicit operator EventAttendees(Self[] SelfArray) => new EventAttendees { SelfArray = SelfArray };
+    }
+
+    public partial class Schedule
+    {
+        public static Schedule FromJson(string json) => JsonSerializer.Deserialize(json, SfeduSchedule.Converter.Settings);
+    }
+
+    public static class Serialize
+    {
+        public static string ToJson(this Schedule self) => JsonSerializer.Serialize(self, SfeduSchedule.Converter.Settings);
+    }
+
+    internal static class Converter
+    {
+        public static readonly JsonSerializerOptions Settings = new(JsonSerializerDefaults.General)
+        {
+            Converters =
+            {
+                EventAttendeesConverter.Singleton,
+                new DateOnlyConverter(),
+                new TimeOnlyConverter(),
+                IsoDateTimeOffsetConverter.Singleton
+            },
+        };
+    }
+
+    internal class EventAttendeesConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(EventAttendees);
+
+        public override EventAttendees Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        {
+            switch (reader.TokenType)
+            {
+                case JsonTokenType.StartObject:
+                    var objectValue = JsonSerializer.Deserialize(ref reader, options);
+                    return new EventAttendees { Self = objectValue };
+                case JsonTokenType.StartArray:
+                    var arrayValue = JsonSerializer.Deserialize(ref reader, options);
+                    return new EventAttendees { SelfArray = arrayValue };
+            }
+            throw new Exception("Cannot unmarshal type EventAttendees");
+        }
+
+        public override void Write(Utf8JsonWriter writer, EventAttendees value, JsonSerializerOptions options)
+        {
+            if (value.SelfArray != null)
+            {
+                JsonSerializer.Serialize(writer, value.SelfArray, options);
+                return;
+            }
+            if (value.Self != null)
+            {
+                JsonSerializer.Serialize(writer, value.Self, options);
+                return;
+            }
+            throw new Exception("Cannot marshal type EventAttendees");
+        }
+
+        public static readonly EventAttendeesConverter Singleton = new EventAttendeesConverter();
+    }
+    
+    public class DateOnlyConverter : JsonConverter
+    {
+        private readonly string serializationFormat;
+        public DateOnlyConverter() : this(null) { }
+
+        public DateOnlyConverter(string? serializationFormat)
+        {
+                this.serializationFormat = serializationFormat ?? "yyyy-MM-dd";
+        }
+
+        public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        {
+                var value = reader.GetString();
+                return DateOnly.Parse(value!);
+        }
+
+        public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options)
+                => writer.WriteStringValue(value.ToString(serializationFormat));
+    }
+
+    public class TimeOnlyConverter : JsonConverter
+    {
+        private readonly string serializationFormat;
+
+        public TimeOnlyConverter() : this(null) { }
+
+        public TimeOnlyConverter(string? serializationFormat)
+        {
+                this.serializationFormat = serializationFormat ?? "HH:mm:ss.fff";
+        }
+
+        public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        {
+                var value = reader.GetString();
+                return TimeOnly.Parse(value!);
+        }
+
+        public override void Write(Utf8JsonWriter writer, TimeOnly value, JsonSerializerOptions options)
+                => writer.WriteStringValue(value.ToString(serializationFormat));
+    }
+
+    internal class IsoDateTimeOffsetConverter : JsonConverter
+    {
+        public override bool CanConvert(Type t) => t == typeof(DateTimeOffset);
+
+        private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";
+
+        private DateTimeStyles _dateTimeStyles = DateTimeStyles.RoundtripKind;
+        private string? _dateTimeFormat;
+        private CultureInfo? _culture;
+
+        public DateTimeStyles DateTimeStyles
+        {
+                get => _dateTimeStyles;
+                set => _dateTimeStyles = value;
+        }
+
+        public string? DateTimeFormat
+        {
+                get => _dateTimeFormat ?? string.Empty;
+                set => _dateTimeFormat = (string.IsNullOrEmpty(value)) ? null : value;
+        }
+
+        public CultureInfo Culture
+        {
+                get => _culture ?? CultureInfo.CurrentCulture;
+                set => _culture = value;
+        }
+
+        public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
+        {
+                string text;
+
+
+                if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal
+                        || (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal)
+                {
+                        value = value.ToUniversalTime();
+                }
+
+                text = value.ToString(_dateTimeFormat ?? DefaultDateTimeFormat, Culture);
+
+                writer.WriteStringValue(text);
+        }
+
+        public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        {
+                string? dateText = reader.GetString();
+
+                if (string.IsNullOrEmpty(dateText) == false)
+                {
+                        if (!string.IsNullOrEmpty(_dateTimeFormat))
+                        {
+                                return DateTimeOffset.ParseExact(dateText, _dateTimeFormat, Culture, _dateTimeStyles);
+                        }
+                        else
+                        {
+                                return DateTimeOffset.Parse(dateText, Culture, _dateTimeStyles);
+                        }
+                }
+                else
+                {
+                        return default(DateTimeOffset);
+                }
+        }
+
+
+        public static readonly IsoDateTimeOffsetConverter Singleton = new IsoDateTimeOffsetConverter();
+    }
+}
+#pragma warning restore CS8618
+#pragma warning restore CS8601
+#pragma warning restore CS8603
diff --git a/SfeduSchedule/Controllers/ScheduleController.cs b/SfeduSchedule/Controllers/ScheduleController.cs
index 87a6b26..1aa5daa 100644
--- a/SfeduSchedule/Controllers/ScheduleController.cs
+++ b/SfeduSchedule/Controllers/ScheduleController.cs
@@ -6,80 +6,124 @@ using Microsoft.AspNetCore.RateLimiting;
 using SfeduSchedule.Abstractions;
 using SfeduSchedule.Services;
 
-namespace SfeduSchedule.Controllers
+namespace SfeduSchedule.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+[EnableRateLimiting("throttle")]
+public class ScheduleController(ModeusService modeusService, ILogger logger) : ControllerBase
 {
-    [ApiController]
-    [Route("api/[controller]")]
-    [EnableRateLimiting("throttle")]
-    public class ScheduleController(ModeusService modeusService, ILogger logger) : ControllerBase
+    /// 
+    /// Получить расписание по пользовательскому запросу.
+    /// 
+    /// Объект запроса, содержащий параметры фильтрации расписания.
+    /// Список событий расписания.
+    /// Возвращает расписание
+    /// Слишком много запросов
+    [HttpPost]
+    public async Task Post([FromBody] ModeusScheduleRequest request)
     {
-
-        /// 
-        /// Получить расписание по пользовательскому запросу.
-        /// 
-        /// Объект запроса, содержащий параметры фильтрации расписания.
-        /// Список событий расписания.
-        /// Возвращает расписание
-        /// Слишком много запросов
-        [HttpPost]
-        public async Task Post([FromBody] ModeusScheduleRequest request)
+        string? schedule;
+        try
         {
-            string? schedule;
-            try
-            {
-                schedule = await modeusService.GetScheduleAsync(request);
-            }
-            catch (HttpRequestException e)
-            {
-                logger.LogError("Ошибка при получении расписания\n\n" + e.Message + "\n\n" + e.StackTrace + "\n\n JSON: " + JsonSerializer.Serialize(request, GlobalVariables.jsonSerializerOptions));
-                return StatusCode((int)(e.StatusCode ?? HttpStatusCode.InternalServerError), "Proxied Modeus: " + e.Message);
-            }
-            return Ok(schedule);
+            schedule = await modeusService.GetScheduleAsync(request);
+        }
+        catch (HttpRequestException e)
+        {
+            logger.LogError("Ошибка при получении расписания\n\n" + e.Message + "\n\n" + e.StackTrace +
+                            "\n\n JSON: " +
+                            JsonSerializer.Serialize(request, GlobalVariables.jsonSerializerOptions));
+            return StatusCode((int)(e.StatusCode ?? HttpStatusCode.InternalServerError),
+                "Proxied Modeus: " + e.Message);
         }
 
-        /// 
-        /// Поиск аудиторий по пользовательскому запросу.
-        /// 
-        /// Объект запроса, содержащий параметры фильтрации аудиторий.
-        /// Список аудиторий.
-        /// Возвращает список аудиторий
-        /// Слишком много запросов
-        [HttpPost]
-        [Route("rooms/search")]
-        public async Task SearchRooms([FromBody] RoomSearchRequest request)
-        {
-            string? rooms;
-            try
-            {
-                rooms = await modeusService.SearchRoomsAsync(request);
-            }
-            catch (HttpRequestException e)
-            {
-                logger.LogError("Ошибка при поиске аудиторий\n\n" + e.Message + "\n\n" + e.StackTrace + "\n\n JSON: " + JsonSerializer.Serialize(request, GlobalVariables.jsonSerializerOptions));
-                return StatusCode((int)(e.StatusCode ?? HttpStatusCode.InternalServerError), "Proxied Modeus: " + e.Message);
-            }
-            return Ok(rooms);
-        }
-
-        /// 
-        /// Получить GUID пользователя по полному имени. (требуется авторизация)
-        /// 
-        /// Полное имя пользователя.
-        /// GUID пользователя.
-        /// Возвращает GUID пользователя
-        /// Пользователь не найден
-        /// Неавторизованный
-        [HttpGet]
-        [Authorize(AuthenticationSchemes = "ApiKey")]
-        [DisableRateLimiting]
-        [Route("getguid")]
-        public async Task GetGuid(string fullname)
-        {
-            var guid = await modeusService.GetGuidAsync(fullname);
-            if (string.IsNullOrEmpty(guid))
-                return NotFound();
-
-            return Ok(guid);
-        }
+        return Ok(schedule);
     }
-}
+
+    /// 
+    /// Поиск аудиторий по пользовательскому запросу.
+    /// 
+    /// Объект запроса, содержащий параметры фильтрации аудиторий.
+    /// Список аудиторий.
+    /// Возвращает список аудиторий
+    /// Слишком много запросов
+    [HttpPost]
+    [Route("rooms/search")]
+    public async Task SearchRooms([FromBody] RoomSearchRequest request)
+    {
+        string? rooms;
+        try
+        {
+            rooms = await modeusService.SearchRoomsAsync(request);
+        }
+        catch (HttpRequestException e)
+        {
+            logger.LogError("Ошибка при поиске аудиторий\n\n" + e.Message + "\n\n" + e.StackTrace + "\n\n JSON: " +
+                            JsonSerializer.Serialize(request, GlobalVariables.jsonSerializerOptions));
+            return StatusCode((int)(e.StatusCode ?? HttpStatusCode.InternalServerError),
+                "Proxied Modeus: " + e.Message);
+        }
+
+        return Ok(rooms);
+    }
+
+    /// 
+    /// Получить GUID пользователя по полному имени. (требуется авторизация)
+    /// 
+    /// Полное имя пользователя.
+    /// GUID пользователя.
+    /// Возвращает GUID пользователя
+    /// Пользователь не найден
+    /// Неавторизованный
+    [HttpGet]
+    [Authorize(AuthenticationSchemes = "ApiKey")]
+    [DisableRateLimiting]
+    [Route("getguid")]
+    public async Task GetGuid(string fullname)
+    {
+        var guid = await modeusService.GetGuidAsync(fullname);
+        if (string.IsNullOrEmpty(guid))
+            return NotFound();
+
+        return Ok(guid);
+    }
+
+    /// 
+    /// Получить расписание в формате ICS по пользовательскому запросу.
+    /// 
+    /// Объект запроса, содержащий параметры фильтрации расписания.
+    /// Файл ICS с расписанием за -1 неделя + 1 месяц
+    /// Возвращает файл ICS с расписанием
+    /// Расписание не найдено
+    /// Слишком много запросов
+    [HttpPost]
+    [Route("ics")]
+    public async Task PostIcs([FromBody] ModeusScheduleRequest request)
+    {
+        var ics = await modeusService.GetIcsAsync(request);
+        if (string.IsNullOrEmpty(ics))
+            return NotFound();
+
+        return new FileContentResult(System.Text.Encoding.UTF8.GetBytes(ics), "text/calendar")
+        {
+            FileDownloadName = "schedule.ics"
+        };
+    }
+        
+    /// 
+    /// Получить расписание в формате ICS для указанного пользователя за -1 неделя + 1 месяц.
+    /// 
+    /// 
+    /// Файл ICS с расписанием
+    /// Возвращает файл ICS с расписанием
+    /// Расписание не найдено
+    /// Слишком много запросов
+    [HttpGet]
+    [Route("ics")]
+    public async Task GetIcs([FromQuery] Guid attendeePersonId)
+    {
+        return await PostIcs(new ModeusScheduleRequest(1000, DateTime.UtcNow.AddDays(-7),
+            DateTime.UtcNow.AddMonths(1), null, new List { attendeePersonId }, null, null, null, null, null,
+            null, null));
+    }
+}
\ No newline at end of file
diff --git a/SfeduSchedule/Services/ModeusService.cs b/SfeduSchedule/Services/ModeusService.cs
index e33dbae..e785683 100644
--- a/SfeduSchedule/Services/ModeusService.cs
+++ b/SfeduSchedule/Services/ModeusService.cs
@@ -1,4 +1,7 @@
 using System.Text.Json;
+using Ical.Net.CalendarComponents;
+using Ical.Net.DataTypes;
+using Ical.Net.Serialization;
 using Microsoft.Net.Http.Headers;
 using SfeduSchedule.Abstractions;
 
@@ -75,5 +78,58 @@ namespace SfeduSchedule.Services
             return personId;
         }
         
+        public async Task GetIcsAsync(ModeusScheduleRequest msr)
+        {
+            var schedule = await GetScheduleAsync(msr);
+            if (schedule == null)
+            {
+                _logger.LogError("GetIcsAsync: Schedule is null. Request: {@msr}", msr);
+                return null;
+            }
+
+            Schedule? scheduleJson;
+            try
+            {
+                scheduleJson = Schedule.FromJson(schedule);
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "GetIcsAsync: Deserialization failed. Schedule: {Schedule}", schedule);
+                return null;
+            }
+
+            if (scheduleJson?.Embedded?.Events is not { Length: > 0 } events)
+            {
+                if (scheduleJson == null)
+                    _logger.LogError("GetIcsAsync: scheduleJson is null. Schedule: {Schedule}", schedule);
+                else if (scheduleJson.Embedded == null)
+                    _logger.LogError("GetIcsAsync: scheduleJson.Embedded is null. scheduleJson: {@scheduleJson}", scheduleJson);
+                else if (scheduleJson.Embedded.Events == null)
+                    _logger.LogError("GetIcsAsync: scheduleJson.Embedded.Events is null. Embedded: {@Embedded}", scheduleJson.Embedded);
+                else
+                    _logger.LogWarning("GetIcsAsync: scheduleJson.Embedded.Events is empty. Embedded: {@Embedded}", scheduleJson.Embedded);
+                return null;
+            }
+
+            var calendar = new Ical.Net.Calendar();
+            calendar.AddTimeZone(new VTimeZone(_configuration["TZ"]!));
+
+            foreach (var e in scheduleJson.Embedded.Events)
+            {
+                calendar.Events.Add(new CalendarEvent
+                {
+                    Summary = e.Name,
+                    Description = e.NameShort,
+                    Start = new CalDateTime(e.StartsAtLocal, _configuration["TZ"]!),
+                    End = new CalDateTime(e.EndsAtLocal, _configuration["TZ"]!),
+                });
+            }
+            var serializer = new CalendarSerializer();
+            var serializedCalendar = serializer.SerializeToString(calendar);
+            _logger.LogInformation("GetIcsAsync: Serialized calendar created. Length: {Length}", serializedCalendar?.Length ?? 0);
+            return serializedCalendar;
+            
+        }
+        
     }
 }
diff --git a/SfeduSchedule/SfeduSchedule.csproj b/SfeduSchedule/SfeduSchedule.csproj
index 1c16d82..fbbee2b 100644
--- a/SfeduSchedule/SfeduSchedule.csproj
+++ b/SfeduSchedule/SfeduSchedule.csproj
@@ -10,6 +10,7 @@
     
 
     
+