diff --git a/playwright/e2eTests/triggerOperations.spec.ts b/playwright/e2eTests/triggerOperations.spec.ts
index 4036a5f20..0ebefe729 100644
--- a/playwright/e2eTests/triggerOperations.spec.ts
+++ b/playwright/e2eTests/triggerOperations.spec.ts
@@ -2,6 +2,14 @@ import { test as base, expect } from "@playwright/test";
 import { TriggerInfoPage } from "../pages/triggerInfo.page";
 import { TriggerForm } from "../pages/triggerForm";
 import { MainPage } from "../pages/main.page";
+import {
+    calculateMaintenanceTime,
+    getMaintenanceCaption,
+    Maintenance,
+    MaintenanceList,
+} from "../../src/Domain/Maintenance";
+import { humanizeDuration } from "../../src/helpers/DateUtil";
+import { maintenanceDelta } from "../../src/Domain/Trigger";
 
 const triggerName = "test trigger name";
 const testTriggerDescription = "test trigger description";
@@ -48,7 +56,39 @@ test("Add trigger", async ({ triggerName, triggerDescription, page }) => {
     await expect(page).toHaveURL(`/trigger/${responseJson.id}`);
     await expect(page.getByText(triggerName)).toBeVisible();
     await expect(page.getByText(triggerDescription)).toBeVisible();
-    await page.waitForTimeout(1000);
+});
+
+test("Set trigger maintenance for all intervals", async ({ triggerName, page }) => {
+    const mainPage = new MainPage(page);
+    await mainPage.gotoMainPage();
+    await page.getByText(triggerName).click();
+
+    const triggerInfoPage = new TriggerInfoPage(page);
+
+    for (const maintenance of MaintenanceList) {
+        await test.step(`Set maintenance to ${getMaintenanceCaption(maintenance)}`, async () => {
+            const setMaintenanceRequestPromise = page.waitForRequest("**/trigger/*/setMaintenance");
+
+            const expectedTriggerTime = calculateMaintenanceTime(maintenance);
+
+            await triggerInfoPage.triggerMaintenance.click();
+
+            await page.getByText(getMaintenanceCaption(maintenance)).click();
+
+            const setMaintenanceRequest = await setMaintenanceRequestPromise;
+
+            const requestBody = JSON.parse(setMaintenanceRequest.postData() || "{}");
+            expect(requestBody).not.toBeNull();
+            expect(requestBody.trigger).toEqual(expectedTriggerTime);
+
+            if (maintenance === Maintenance.off) {
+                await expect(page.getByText("Maintenance")).toBeVisible();
+            } else
+                await expect(
+                    page.getByText(humanizeDuration(maintenanceDelta(expectedTriggerTime)))
+                ).toBeVisible();
+        });
+    }
 });
 
 test("Duplicate trigger", async ({
diff --git a/playwright/pages/triggerInfo.page.ts b/playwright/pages/triggerInfo.page.ts
index e28c6af7f..3bbaf2d1d 100644
--- a/playwright/pages/triggerInfo.page.ts
+++ b/playwright/pages/triggerInfo.page.ts
@@ -8,6 +8,7 @@ export class TriggerInfoPage {
     readonly menuListButton: Locator;
     readonly duplicateButton: Locator;
     readonly deleteButton: Locator;
+    readonly triggerMaintenance: Locator;
 
     constructor(page: Page) {
         this.page = page;
@@ -16,5 +17,6 @@ export class TriggerInfoPage {
         this.menuListButton = page.getByText("Other");
         this.duplicateButton = page.getByText("Duplicate");
         this.deleteButton = page.getByText("Delete");
+        this.triggerMaintenance = page.locator("[data-tid='TriggerMaintenanceButton']");
     }
 }
diff --git a/src/Api/MoiraApi.ts b/src/Api/MoiraApi.ts
deleted file mode 100644
index 0d2ea3db6..000000000
--- a/src/Api/MoiraApi.ts
+++ /dev/null
@@ -1,563 +0,0 @@
-import * as queryString from "query-string";
-import { EventList } from "../Domain/Event";
-import { Trigger, TriggerList, TriggerState, ValidateTargetsResult } from "../Domain/Trigger";
-import { Settings } from "../Domain/Settings";
-import { TagStat } from "../Domain/Tag";
-import { PatternList } from "../Domain/Pattern";
-import { NotificationList } from "../Domain/Notification";
-import { Contact, ContactList } from "../Domain/Contact";
-import { ContactCreateInfo } from "../Domain/ContactCreateInfo";
-import { Subscription } from "../Domain/Subscription";
-import { Schedule } from "../Domain/Schedule";
-import { NotifierState } from "../Domain/MoiraServiceStates";
-import { Team } from "../Domain/Team";
-
-export type SubscriptionCreateInfo = {
-    sched: Schedule;
-    tags: Array<string>;
-    throttling: boolean;
-    contacts: Array<string>;
-    enabled: boolean;
-    any_tags: boolean;
-    user?: string;
-    team_id?: string;
-    id?: string;
-    ignore_recoverings: boolean;
-    ignore_warnings: boolean;
-    plotting?: {
-        enabled: boolean;
-        theme: "light" | "dark";
-    };
-};
-
-export type TagList = {
-    list: Array<string>;
-};
-
-export type TagStatList = {
-    list: Array<TagStat>;
-};
-
-export class ApiError extends Error {
-    status: number;
-
-    constructor({ message, status }: { message: string; status: number }) {
-        super(message);
-        this.name = "ApiError";
-        this.status = status;
-    }
-}
-
-const statusCode = {
-    NOT_FOUND: 404,
-};
-
-export { statusCode };
-
-export default class MoiraApi {
-    apiUrl: string;
-
-    triggerListPageSize = 20;
-
-    eventHistoryPageSize = 100;
-
-    constructor(apiUrl: string) {
-        this.apiUrl = apiUrl;
-    }
-
-    static async checkStatus(response: Response): Promise<void> {
-        if (!(response.status >= 200 && response.status < 300)) {
-            const serverResponse = await response.json();
-
-            throw new ApiError({
-                message: serverResponse
-                    ? serverResponse.status +
-                      (serverResponse.error ? `: ${serverResponse.error}` : "")
-                    : serverResponse.error,
-                status: response.status,
-            });
-        }
-    }
-
-    async get<T>(url: string, init?: RequestInit): Promise<T> {
-        const fullUrl = this.apiUrl + url;
-        const response = await fetch(fullUrl, {
-            ...init,
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async getSettings(): Promise<Settings> {
-        const result = await this.get<Settings>("/user/settings");
-
-        result.subscriptions.forEach((s) => {
-            s.tags = s.tags === null ? [] : s.tags;
-        });
-        return result;
-    }
-
-    async getContactList(): Promise<ContactList> {
-        const url = `${this.apiUrl}/contact`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    getSettingsByTeam(teamId: string): Promise<Settings> {
-        return this.get<Settings>(`/teams/${encodeURIComponent(teamId)}/settings`);
-    }
-
-    async addContact(contact: ContactCreateInfo): Promise<Contact> {
-        const url = `${this.apiUrl}/contact`;
-        const response = await fetch(url, {
-            method: "PUT",
-            credentials: "same-origin",
-            body: JSON.stringify(contact),
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async addTeamContact(contact: ContactCreateInfo, team: Team): Promise<Contact> {
-        const url = `${this.apiUrl}/teams/${encodeURIComponent(team.id)}/contacts`;
-        const response = await fetch(url, {
-            method: "POST",
-            credentials: "same-origin",
-            body: JSON.stringify(contact),
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async testContact(contactId: string): Promise<void> {
-        const url = `${this.apiUrl}/contact/${encodeURIComponent(contactId)}/test`;
-        const response = await fetch(url, {
-            method: "POST",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async updateContact(contact: Contact): Promise<Contact> {
-        const url = `${this.apiUrl}/contact/${encodeURIComponent(contact.id)}`;
-        const response = await fetch(url, {
-            method: "PUT",
-            credentials: "same-origin",
-            body: JSON.stringify(contact),
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async addSubscription(subscription: SubscriptionCreateInfo): Promise<Subscription> {
-        const url = `${this.apiUrl}/subscription`;
-        if (subscription.id != null) {
-            throw new Error("InvalidProgramState: id of subscription must be null or undefined");
-        }
-        const response = await fetch(url, {
-            method: "PUT",
-            credentials: "same-origin",
-            body: JSON.stringify(subscription),
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async addTeamSubscription(
-        subscription: SubscriptionCreateInfo,
-        team: Team
-    ): Promise<Subscription> {
-        const url = `${this.apiUrl}/teams/${encodeURIComponent(team.id)}/subscriptions`;
-        if (subscription.id != null) {
-            throw new Error("InvalidProgramState: id of subscription must be null or undefined");
-        }
-        const response = await fetch(url, {
-            method: "POST",
-            credentials: "same-origin",
-            body: JSON.stringify(subscription),
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async updateSubscription(subscription: Subscription): Promise<Subscription> {
-        const url = `${this.apiUrl}/subscription/${encodeURIComponent(subscription.id)}`;
-        const response = await fetch(url, {
-            method: "PUT",
-            credentials: "same-origin",
-            body: JSON.stringify(subscription),
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async testSubscription(subscriptionId: string): Promise<void> {
-        const url = `${this.apiUrl}/subscription/${encodeURIComponent(subscriptionId)}/test`;
-        const response = await fetch(url, {
-            method: "PUT",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async deleteContact(contactId: string): Promise<void> {
-        const url = `${this.apiUrl}/contact/${encodeURIComponent(contactId)}`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async getPatternList(): Promise<PatternList> {
-        const url = `${this.apiUrl}/pattern`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async delPattern(pattern: string): Promise<void> {
-        const url = `${this.apiUrl}/pattern/${encodeURIComponent(pattern)}`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async getTagList(): Promise<TagList> {
-        const url = `${this.apiUrl}/tag`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async getTagStats(): Promise<TagStatList> {
-        const url = `${this.apiUrl}/tag/stats`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async delTag(tag: string): Promise<void> {
-        const url = `${this.apiUrl}/tag/${encodeURIComponent(tag)}`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async getTriggerList(
-        page: number,
-        onlyProblems: boolean,
-        tags: Array<string>,
-        searchText: string
-    ): Promise<TriggerList> {
-        const url = `${this.apiUrl}/trigger/search?${queryString.stringify(
-            {
-                p: page,
-                size: this.triggerListPageSize,
-                tags,
-                onlyProblems,
-                text: searchText,
-            },
-            { arrayFormat: "index", encode: true }
-        )}`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async getTrigger(id: string, params?: { populated: boolean }): Promise<Trigger> {
-        const url = `${this.apiUrl}/trigger/${encodeURIComponent(id)}${
-            params ? `?${queryString.stringify(params)}` : ""
-        }`;
-
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async addTrigger(
-        data: Partial<Trigger>
-    ): Promise<{
-        [key: string]: string;
-    }> {
-        const url = `${this.apiUrl}/trigger`;
-        const response = await fetch(url, {
-            method: "PUT",
-            body: JSON.stringify(data),
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async setTrigger(
-        id: string,
-        data: Partial<Trigger>
-    ): Promise<{
-        [key: string]: string;
-    }> {
-        const url = `${this.apiUrl}/trigger/${encodeURIComponent(id)}`;
-        const response = await fetch(url, {
-            method: "PUT",
-            body: JSON.stringify(data),
-            credentials: "same-origin",
-        });
-
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async delTrigger(id: string): Promise<void> {
-        const url = `${this.apiUrl}/trigger/${encodeURIComponent(id)}`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    validateTarget = async (trigger: Partial<Trigger>): Promise<ValidateTargetsResult> => {
-        const url = `${this.apiUrl}/trigger/check`;
-        const response = await fetch(url, {
-            method: "PUT",
-            body: JSON.stringify(trigger),
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    };
-
-    async setMaintenance(
-        triggerId: string,
-        data: {
-            trigger?: number;
-            metrics?: {
-                [metric: string]: number;
-            };
-        }
-    ): Promise<void> {
-        const url = `${this.apiUrl}/trigger/${encodeURIComponent(triggerId)}/setMaintenance`;
-        const response = await fetch(url, {
-            method: "PUT",
-            body: JSON.stringify(data),
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async getTriggerState(id: string): Promise<TriggerState> {
-        const url = `${this.apiUrl}/trigger/${encodeURIComponent(id)}/state`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async getTriggerEvents(id: string, page: number): Promise<EventList> {
-        const url = `${this.apiUrl}/event/${encodeURIComponent(id)}?p=${page}&size=${
-            this.eventHistoryPageSize
-        }`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async delThrottling(triggerId: string): Promise<void> {
-        const url = `${this.apiUrl}/trigger/${encodeURIComponent(triggerId)}/throttling`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async delMetric(triggerId: string, metric: string): Promise<void> {
-        const url = `${this.apiUrl}/trigger/${encodeURIComponent(
-            triggerId
-        )}/metrics?name=${encodeURIComponent(metric)}`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async delNoDataMetric(triggerId: string): Promise<void> {
-        const url = `${this.apiUrl}/trigger/${encodeURIComponent(triggerId)}/metrics/nodata`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async getNotificationList(): Promise<NotificationList> {
-        const url = `${this.apiUrl}/notification?start=0&end=-1`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async delNotification(id: string): Promise<void> {
-        const url = `${this.apiUrl}/notification?id=${encodeURIComponent(id)}`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async delAllNotifications(): Promise<void> {
-        const url = `${this.apiUrl}/notification/all`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async delAllNotificationEvents(): Promise<void> {
-        const url = `${this.apiUrl}/event/all`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async delSubscription(subscriptionId: string): Promise<void> {
-        const url = `${this.apiUrl}/subscription/${encodeURIComponent(subscriptionId)}`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async getNotifierState(): Promise<NotifierState> {
-        const url = `${this.apiUrl}/health/notifier`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async setNotifierState(status: NotifierState): Promise<NotifierState> {
-        const url = `${this.apiUrl}/health/notifier`;
-        const response = await fetch(url, {
-            method: "PUT",
-            credentials: "same-origin",
-            body: JSON.stringify(status),
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async getTeams(): Promise<{ teams: Team[] }> {
-        const url = `${this.apiUrl}/teams`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async addTeam(team: Partial<Team>): Promise<{ id: string }> {
-        const url = `${this.apiUrl}/teams`;
-        const response = await fetch(url, {
-            method: "POST",
-            body: JSON.stringify(team),
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async updateTeam(team: Team): Promise<{ id: string }> {
-        const url = `${this.apiUrl}/teams/${encodeURIComponent(team.id)}`;
-        const response = await fetch(url, {
-            method: "PATCH",
-            body: JSON.stringify(team),
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async delTeam(teamId: string): Promise<void> {
-        const url = `${this.apiUrl}/teams/${encodeURIComponent(teamId)}`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-
-    async getUsers(teamId: string): Promise<{ usernames: string[] }> {
-        const url = `${this.apiUrl}/teams/${encodeURIComponent(teamId)}/users`;
-        const response = await fetch(url, {
-            method: "GET",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async addUser(teamId: string, userName: string): Promise<void> {
-        const url = `${this.apiUrl}/teams/${encodeURIComponent(teamId)}/users`;
-        const response = await fetch(url, {
-            method: "POST",
-            body: JSON.stringify({ usernames: [userName] }),
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-        return response.json();
-    }
-
-    async delUser(teamId: string, userName: string): Promise<void> {
-        const url = `${this.apiUrl}/teams/${encodeURIComponent(teamId)}/users/${encodeURIComponent(
-            userName
-        )}`;
-        const response = await fetch(url, {
-            method: "DELETE",
-            credentials: "same-origin",
-        });
-        await MoiraApi.checkStatus(response);
-    }
-}
diff --git a/src/Api/MoiraApiInjection.tsx b/src/Api/MoiraApiInjection.tsx
deleted file mode 100644
index a13e41d94..000000000
--- a/src/Api/MoiraApiInjection.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import * as React from "react";
-import MoiraApi from "./MoiraApi";
-
-const ApiContext = React.createContext<MoiraApi>(new MoiraApi("/api"));
-
-export const ApiProvider = ApiContext.Provider;
-
-export function withMoiraApi<ComponentProps extends { moiraApi: MoiraApi }>(
-    Component: React.ComponentType<ComponentProps>
-): React.ComponentType<Omit<ComponentProps, "moiraApi">> {
-    function ComponentWithApi(props: Omit<ComponentProps, "moiraApi">) {
-        const moiraApi = React.useContext(ApiContext);
-        return <Component {...(props as ComponentProps)} moiraApi={moiraApi} />;
-    }
-
-    ComponentWithApi.displayName = `withApi(${
-        Component.displayName || Component.name || "Component"
-    })`;
-    return ComponentWithApi;
-}
diff --git a/src/Components/CreateSubscriptionModal/CreateSubscriptionModal.tsx b/src/Components/CreateSubscriptionModal/CreateSubscriptionModal.tsx
index 7ce9b50f4..2cd81411e 100644
--- a/src/Components/CreateSubscriptionModal/CreateSubscriptionModal.tsx
+++ b/src/Components/CreateSubscriptionModal/CreateSubscriptionModal.tsx
@@ -6,7 +6,7 @@ import { Fill, RowStack } from "@skbkontur/react-stack-layout";
 import { Contact } from "../../Domain/Contact";
 import { omitSubscription } from "../../helpers/omitTypes";
 import SubscriptionEditor from "../SubscriptionEditor/SubscriptionEditor";
-import { SubscriptionCreateInfo } from "../../Api/MoiraApi";
+import { SubscriptionCreateInfo } from "../../Domain/Subscription";
 import FileLoader from "../FileLoader/FileLoader";
 import ModalError from "../ModalError/ModalError";
 import { useParams } from "react-router";
diff --git a/src/Components/MaintenanceSelect/MaintenanceSelect.tsx b/src/Components/MaintenanceSelect/MaintenanceSelect.tsx
index d4985042b..a4b227bda 100644
--- a/src/Components/MaintenanceSelect/MaintenanceSelect.tsx
+++ b/src/Components/MaintenanceSelect/MaintenanceSelect.tsx
@@ -41,7 +41,11 @@ export default function MaintenanceSelect(props: MaintenanceSelectProps): React.
     return (
         <RenderLayer onClickOutside={handleClose} onFocusOutside={handleClose} active={opened}>
             <span ref={containerEl} className={cn("container")}>
-                <Button onClick={() => setOpened(true)} use="link">
+                <Button
+                    data-tid="TriggerMaintenanceButton"
+                    onClick={() => setOpened(true)}
+                    use="link"
+                >
                     {caption} <ArrowTriangleDownIcon color="#6b99d3" />
                 </Button>
                 {opened ? (
diff --git a/src/Components/MetricListItem/MetricListItem.tsx b/src/Components/MetricListItem/MetricListItem.tsx
index f53a083c4..7a4bc8963 100644
--- a/src/Components/MetricListItem/MetricListItem.tsx
+++ b/src/Components/MetricListItem/MetricListItem.tsx
@@ -1,14 +1,15 @@
 import StatusIndicator from "../StatusIndicator/StatusIndicator";
-import { format, fromUnixTime, getUnixTime } from "date-fns";
+import { format, fromUnixTime } from "date-fns";
 import MetricValues from "../MetricValues/MetricValues";
 import MaintenanceSelect from "../MaintenanceSelect/MaintenanceSelect";
 import { Tooltip } from "@skbkontur/react-ui/components/Tooltip";
 import UserIcon from "@skbkontur/react-icons/User";
 import * as React from "react";
-import { getUTCDate, humanizeDuration } from "../../helpers/DateUtil";
+import { humanizeDuration } from "../../helpers/DateUtil";
 import { Metric } from "../../Domain/Metric";
 import { useHistory } from "react-router";
 import { ConfirmMetricDeletionWithTransformNull } from "../ConfirmMetricDeletionWithTransformNull/ConfirmMetricDeletionWithTransformNull";
+import { maintenanceDelta } from "../../Domain/Trigger";
 import classNames from "classnames/bind";
 
 import styles from "../MetricList/MetricList.less";
@@ -19,10 +20,6 @@ function maintenanceCaption(delta: number): React.ReactNode {
     return <span>{delta <= 0 ? "Maintenance" : humanizeDuration(delta)}</span>;
 }
 
-function maintenanceDelta(maintenance?: number | null): number {
-    return (maintenance || 0) - getUnixTime(getUTCDate());
-}
-
 const hideTargetsNames = (values: { [metric: string]: number } | undefined) => {
     return !values || Object.keys(values).length === 1;
 };
diff --git a/src/Components/SubscriptionEditor/SubscriptionEditor.tsx b/src/Components/SubscriptionEditor/SubscriptionEditor.tsx
index 7ceaedb5a..93ff060ac 100644
--- a/src/Components/SubscriptionEditor/SubscriptionEditor.tsx
+++ b/src/Components/SubscriptionEditor/SubscriptionEditor.tsx
@@ -2,7 +2,7 @@ import React, { FC } from "react";
 import { Toggle } from "@skbkontur/react-ui/components/Toggle";
 import { Checkbox } from "@skbkontur/react-ui/components/Checkbox";
 import { ValidationWrapperV1, tooltip, ValidationInfo } from "@skbkontur/react-ui-validations";
-import { SubscriptionCreateInfo } from "../../Api/MoiraApi";
+import { SubscriptionCreateInfo } from "../../Domain/Subscription";
 import { Contact } from "../../Domain/Contact";
 import { Schedule } from "../../Domain/Schedule";
 import { Subscription } from "../../Domain/Subscription";
diff --git a/src/Components/TriggerList/TriggerList.tsx b/src/Components/TriggerList/TriggerList.tsx
index 1958aaec2..90a9f4b7e 100644
--- a/src/Components/TriggerList/TriggerList.tsx
+++ b/src/Components/TriggerList/TriggerList.tsx
@@ -18,6 +18,7 @@ type Props = {
 
 export default function TriggerList(props: Props): React.ReactElement {
     const { items, searchMode, onChange, onRemove, history } = props;
+
     return (
         <div>
             {items.length === 0 ? (
diff --git a/src/Components/TriggerListItem/TriggerListItem.tsx b/src/Components/TriggerListItem/TriggerListItem.tsx
index d03e81fca..40ff7d1ea 100644
--- a/src/Components/TriggerListItem/TriggerListItem.tsx
+++ b/src/Components/TriggerListItem/TriggerListItem.tsx
@@ -1,4 +1,5 @@
 import * as React from "react";
+import { useState, useMemo } from "react";
 import { History } from "history";
 import { format, fromUnixTime } from "date-fns";
 import { Link as ReactRouterLink } from "react-router-dom";
@@ -21,7 +22,6 @@ import classNames from "classnames/bind";
 import styles from "./TriggerListItem.less";
 
 const cn = classNames.bind(styles);
-
 import _ from "lodash";
 
 type Props = {
@@ -32,142 +32,34 @@ type Props = {
     history: History;
 };
 
-type State = {
-    showMetrics: boolean;
-    metrics: MetricItemList;
-    sortingColumn: SortingColumn;
-    sortingDown: boolean;
-};
-
-export default class TriggerListItem extends React.Component<Props, State> {
-    public state: State;
-
-    constructor(props: Props) {
-        super(props);
-        this.state = {
-            showMetrics: false,
-            metrics: props.data.last_check?.metrics || {},
-            sortingColumn: "event",
-            sortingDown: false,
-        };
-    }
-
-    render(): React.ReactNode {
-        const { searchMode, data } = this.props;
-        const { id, name, targets, tags, throttling, highlights } = data;
-        const { showMetrics } = this.state;
-        const metrics = this.renderMetrics();
-        const searchModeName = highlights && highlights.name;
-
-        return (
-            <div className={cn("row", { active: showMetrics })}>
-                <div
-                    className={cn("state", { active: metrics })}
-                    onClick={() => {
-                        if (metrics) {
-                            this.toggleMetrics();
-                        }
-                    }}
-                    data-tid="TriggerListItem_status"
-                >
-                    {this.renderStatus()}
-                    {this.renderCounters()}
-                </div>
-                <div className={cn("data")}>
-                    <ReactRouterLink
-                        className={cn("header")}
-                        to={getPageLink("trigger", id)}
-                        data-tid="TriggerListItem_header"
-                    >
-                        <div className={cn("link")}>
-                            <div className={cn("title")}>
-                                {searchMode ? (
-                                    <div
-                                        className={cn("name")}
-                                        dangerouslySetInnerHTML={{
-                                            __html: sanitize(searchModeName || name),
-                                        }}
-                                    />
-                                ) : (
-                                    <div className={cn("name")}>{name}</div>
-                                )}
-                                {throttling !== 0 && (
-                                    <div
-                                        className={cn("flag")}
-                                        title={`Throttling until
-                                            ${format(
-                                                fromUnixTime(throttling),
-                                                "MMMM d, HH:mm:ss"
-                                            )}`}
-                                    >
-                                        <FlagSolidIcon />
-                                    </div>
-                                )}
-                            </div>
-                            <div className={cn({ targets: true })}>
-                                {targets.map((target) => (
-                                    <div key={target} className={cn("target")}>
-                                        {target}
-                                    </div>
-                                ))}
-                            </div>
-                        </div>
-                    </ReactRouterLink>
-                    <div className={cn("tags")}>
-                        <TagGroup
-                            onClick={(tag) => {
-                                this.props.history?.push(
-                                    `/?${queryString.stringify(
-                                        { tags: [tag] },
-                                        {
-                                            arrayFormat: "index",
-                                            encode: true,
-                                        }
-                                    )}`
-                                );
-                            }}
-                            tags={tags}
-                        />
-                    </div>
-                    {showMetrics && <div className={cn("metrics")}>{metrics}</div>}
-                </div>
-            </div>
-        );
-    }
+const TriggerListItem: React.FC<Props> = ({ data, searchMode, onChange, onRemove, history }) => {
+    const [showMetrics, setShowMetrics] = useState(false);
+    const [sortingColumn, setSortingColumn] = useState<SortingColumn>("event");
+    const [sortingDown, setSortingDown] = useState(false);
 
-    handleSort(column: SortingColumn) {
-        const { sortingColumn, sortingDown } = this.state;
+    const metrics = data.last_check?.metrics;
 
+    const handleSort = (column: SortingColumn) => {
         if (column === sortingColumn) {
-            this.setState({ sortingDown: !sortingDown });
+            setSortingDown((prev) => !prev);
         } else {
-            this.setState({
-                sortingColumn: column,
-                sortingDown: true,
-            });
+            setSortingColumn(column);
+            setSortingDown(true);
         }
-    }
+    };
 
-    getHasExceptionState(): boolean {
-        const { data } = this.props;
-        const { state: triggerStatus } = data.last_check || {};
-        return triggerStatus === Status.EXCEPTION;
-    }
+    const hasExceptionState = data.last_check?.state === Status.EXCEPTION;
 
-    toggleMetrics(): void {
-        const { showMetrics } = this.state;
-        this.setState({ showMetrics: !showMetrics });
-    }
+    const filterMetricsByStatus = useMemo(
+        () => (status: Status): MetricItemList =>
+            _.pickBy(metrics, (metric) => metric.state === status),
+        [metrics]
+    );
 
-    filterMetricsByStatus(status: Status): MetricItemList {
-        const { metrics } = this.state;
-        return _.pickBy(metrics, (metric) => metric.state === status);
-    }
-
-    renderCounters(): React.ReactElement {
+    const renderCounters = (): React.ReactElement => {
         const counters = StatusesInOrder.map((status) => ({
             status,
-            count: Object.keys(this.filterMetricsByStatus(status)).length,
+            count: Object.keys(filterMetricsByStatus(status)).length,
         }))
             .filter(({ count }) => count !== 0)
             .map(({ status, count }) => (
@@ -180,33 +72,29 @@ export default class TriggerListItem extends React.Component<Props, State> {
                 {counters.length !== 0 ? counters : <span className={cn("NA")}>N/A</span>}
             </div>
         );
-    }
+    };
 
-    renderStatus(): React.ReactElement {
-        const { data } = this.props;
-        const { state: triggerStatus } = data.last_check || {};
+    const renderStatus = (): React.ReactElement => {
+        const triggerStatus = data.last_check?.state;
         const metricStatuses = StatusesInOrder.filter(
-            (x) => Object.keys(this.filterMetricsByStatus(x)).length !== 0
+            (x) => Object.keys(filterMetricsByStatus(x)).length !== 0
         );
         const notOkStatuses = metricStatuses.filter((x) => x !== Status.OK);
-        let statuses: Status[];
-        if (triggerStatus && (triggerStatus !== Status.OK || metricStatuses.length === 0)) {
-            statuses = [triggerStatus];
-        } else if (notOkStatuses.length !== 0) {
-            statuses = notOkStatuses;
-        } else {
-            statuses = [Status.OK];
-        }
+        const statuses =
+            triggerStatus && (triggerStatus !== Status.OK || metricStatuses.length === 0)
+                ? [triggerStatus]
+                : notOkStatuses.length !== 0
+                ? notOkStatuses
+                : [Status.OK];
         return (
             <div className={cn("indicator")}>
                 <StatusIndicator statuses={statuses} />
             </div>
         );
-    }
+    };
 
-    renderExceptionHelpMessage(): React.ReactElement {
-        const { data } = this.props;
-        const hasExpression = data.expression != null && data.expression !== "";
+    const renderExceptionHelpMessage = (): React.ReactElement => {
+        const hasExpression = !!data.expression;
         const hasMultipleTargets = data.targets.length > 1;
         return (
             <div className={cn("exception-message")}>
@@ -219,43 +107,110 @@ export default class TriggerListItem extends React.Component<Props, State> {
                 page.
             </div>
         );
-    }
+    };
 
-    renderMetrics(): React.ReactNode {
-        const { onChange, onRemove, data } = this.props;
-        const { sortingColumn, sortingDown } = this.state;
-        if (!onChange || !onRemove) {
-            return null;
-        }
+    const renderMetrics = (): React.ReactNode => {
+        if (!onChange || !onRemove) return null;
         const statuses = StatusesInOrder.filter(
-            (x) => Object.keys(this.filterMetricsByStatus(x)).length !== 0
+            (x) => Object.keys(filterMetricsByStatus(x)).length !== 0
         );
-        if (statuses.length === 0) {
-            return null;
-        }
-        const metrics: Array<React.ReactElement> = statuses.map((status: Status) => (
-            <Tab key={status} id={status} label={getStatusCaption(status)}>
-                <MetricListView
-                    items={sortMetrics(
-                        this.filterMetricsByStatus(status),
-                        sortingColumn,
-                        sortingDown
-                    )}
-                    sortingColumn={sortingColumn}
-                    onSort={(column) => this.handleSort(column)}
-                    sortingDown={sortingDown}
-                    onChange={(metric: string, maintenance: number) =>
-                        onChange?.(data.id, metric, maintenance)
-                    }
-                    onRemove={(metric: string) => onRemove(metric)}
-                />
-            </Tab>
-        ));
+        if (statuses.length === 0) return null;
+
         return (
             <div className={cn("metrics")}>
-                {this.getHasExceptionState() && this.renderExceptionHelpMessage()}
-                <Tabs value={statuses[0]}>{metrics}</Tabs>
+                {hasExceptionState && renderExceptionHelpMessage()}
+                <Tabs value={statuses[0]}>
+                    {statuses.map((status) => (
+                        <Tab key={status} id={status} label={getStatusCaption(status)}>
+                            <MetricListView
+                                items={sortMetrics(
+                                    filterMetricsByStatus(status),
+                                    sortingColumn,
+                                    sortingDown
+                                )}
+                                sortingColumn={sortingColumn}
+                                onSort={handleSort}
+                                sortingDown={sortingDown}
+                                onChange={(metric, maintenance) =>
+                                    onChange?.(data.id, metric, maintenance)
+                                }
+                                onRemove={onRemove}
+                            />
+                        </Tab>
+                    ))}
+                </Tabs>
             </div>
         );
-    }
-}
+    };
+
+    const searchModeName = data.highlights?.name;
+
+    return (
+        <div className={cn("row", { active: showMetrics })}>
+            <div
+                className={cn("state", { active: metrics })}
+                onClick={() => metrics && setShowMetrics((prev) => !prev)}
+                data-tid="TriggerListItem_status"
+            >
+                {renderStatus()}
+                {renderCounters()}
+            </div>
+            <div className={cn("data")}>
+                <ReactRouterLink
+                    className={cn("header")}
+                    to={getPageLink("trigger", data.id)}
+                    data-tid="TriggerListItem_header"
+                >
+                    <div className={cn("link")}>
+                        <div className={cn("title")}>
+                            {searchMode ? (
+                                <div
+                                    className={cn("name")}
+                                    dangerouslySetInnerHTML={{
+                                        __html: sanitize(searchModeName || data.name),
+                                    }}
+                                />
+                            ) : (
+                                <div className={cn("name")}>{data.name}</div>
+                            )}
+                            {data.throttling !== 0 && (
+                                <div
+                                    className={cn("flag")}
+                                    title={`Throttling until ${format(
+                                        fromUnixTime(data.throttling),
+                                        "MMMM d, HH:mm:ss"
+                                    )}`}
+                                >
+                                    <FlagSolidIcon />
+                                </div>
+                            )}
+                        </div>
+                        <div className={cn("targets")}>
+                            {data.targets.map((target) => (
+                                <div key={target} className={cn("target")}>
+                                    {target}
+                                </div>
+                            ))}
+                        </div>
+                    </div>
+                </ReactRouterLink>
+                <div className={cn("tags")}>
+                    <TagGroup
+                        onClick={(tag) =>
+                            history.push(
+                                `/?${queryString.stringify(
+                                    { tags: [tag] },
+                                    { arrayFormat: "index", encode: true }
+                                )}`
+                            )
+                        }
+                        tags={data.tags}
+                    />
+                </div>
+                {showMetrics && <div className={cn("metrics")}>{renderMetrics()}</div>}
+            </div>
+        </div>
+    );
+};
+
+export default TriggerListItem;
diff --git a/src/Components/TriggerSaveWarningModal/TriggerSaveWarningModal.tsx b/src/Components/TriggerSaveWarningModal/TriggerSaveWarningModal.tsx
index 7dd02145d..c5039ebcd 100644
--- a/src/Components/TriggerSaveWarningModal/TriggerSaveWarningModal.tsx
+++ b/src/Components/TriggerSaveWarningModal/TriggerSaveWarningModal.tsx
@@ -9,7 +9,7 @@ export function TriggerSaveWarningModal({
 }: {
     isOpen: boolean;
     onClose: () => void;
-    onSave: () => Promise<void>;
+    onSave: () => void;
 }) {
     return (
         (isOpen && (
diff --git a/src/Containers/HeaderContainer.tsx b/src/Containers/HeaderContainer.tsx
index 63ea64d16..3031a7f59 100644
--- a/src/Containers/HeaderContainer.tsx
+++ b/src/Containers/HeaderContainer.tsx
@@ -1,56 +1,25 @@
-import * as React from "react";
-import MoiraApi from "../Api/MoiraApi";
-import { withMoiraApi } from "../Api/MoiraApiInjection";
+import React from "react";
 import Bar from "../Components/Bar/Bar";
 import Header from "../Components/Header/Header";
 import MoiraServiceStates from "../Domain/MoiraServiceStates";
+import { useGetNotifierStateQuery } from "../services/NotifierApi";
 
-type Props = {
-    moiraApi: MoiraApi;
+interface IHeaderContainerProps {
     className: string;
-};
-
-type State = {
-    notifierStateMessage?: string;
-};
-
-class HeaderContainer extends React.Component<Props, State> {
-    state: State = {};
-
-    componentDidMount() {
-        this.getData();
-    }
+}
 
-    render(): React.ReactElement {
-        const { notifierStateMessage } = this.state;
-        const { className } = this.props;
-        return (
-            <div className={className}>
-                {notifierStateMessage && <Bar message={notifierStateMessage} />}
-                <Header />
-            </div>
-        );
-    }
+export const HeaderContainer: React.FC<IHeaderContainerProps> = ({ className }) => {
+    const { data: notifierState } = useGetNotifierStateQuery(undefined, {
+        refetchOnMountOrArgChange: true,
+    });
 
-    async getData() {
-        const { moiraApi } = this.props;
-        try {
-            const { state, message } = await moiraApi.getNotifierState();
-            switch (state) {
-                case MoiraServiceStates.OK:
-                    this.setState({ notifierStateMessage: undefined });
-                    break;
-                case MoiraServiceStates.ERROR:
-                    this.setState({ notifierStateMessage: message });
-                    break;
-                default:
-                    break;
-            }
-        } catch (error) {
-            console.error(error);
-            // ToDo: do something with this error
-        }
-    }
-}
+    const notifierStateMessage =
+        notifierState?.state === MoiraServiceStates.ERROR ? notifierState?.message : undefined;
 
-export default withMoiraApi(HeaderContainer);
+    return (
+        <div className={className}>
+            {notifierStateMessage && <Bar message={notifierStateMessage} />}
+            <Header />
+        </div>
+    );
+};
diff --git a/src/Containers/NotificationListContainer.tsx b/src/Containers/NotificationListContainer.tsx
index 2c19deeb2..da9cb1fc0 100644
--- a/src/Containers/NotificationListContainer.tsx
+++ b/src/Containers/NotificationListContainer.tsx
@@ -3,75 +3,44 @@ import { Button } from "@skbkontur/react-ui/components/Button";
 import { Flexbox } from "../Components/Flexbox/FlexBox";
 import { Toggle } from "@skbkontur/react-ui/components/Toggle";
 import TrashIcon from "@skbkontur/react-icons/Trash";
-import MoiraApi from "../Api/MoiraApi";
-import { withMoiraApi } from "../Api/MoiraApiInjection";
 import MoiraServiceStates from "../Domain/MoiraServiceStates";
 import { Layout, LayoutContent, LayoutTitle } from "../Components/Layout/Layout";
 import NotificationList from "../Components/NotificationList/NotificationList";
 import { setDocumentTitle } from "../helpers/setDocumentTitle";
-import { NotificationsState, UIState } from "../store/selectors";
-import {
-    deleteNotification,
-    setNotifierEnabled,
-    deleteAllNotifications,
-} from "../store/Reducers/NotificationListContainerReducer.slice";
-import { toggleLoading, setError } from "../store/Reducers/UIReducer.slice";
-import { useAppDispatch, useAppSelector } from "../store/hooks";
+import { UIState } from "../store/selectors";
+import { useAppSelector } from "../store/hooks";
 import { useLoadNotificationsData } from "../hooks/useLoadNotificationsData";
 import { composeNotifications } from "../helpers/composeNotifications";
 import { ConfirmModalHeaderData } from "../Domain/Global";
 import useConfirmModal from "../hooks/useConfirmModal";
+import {
+    useDeleteAllNotificationEventsMutation,
+    useDeleteAllNotificationsMutation,
+    useDeleteNotificationMutation,
+} from "../services/NotificationsApi";
+import { useSetNotifierStateMutation } from "../services/NotifierApi";
 
-type TProps = { moiraApi: MoiraApi };
-
-const NotificationListContainer: FC<TProps> = ({ moiraApi }) => {
-    const dispatch = useAppDispatch();
-    const { notificationList, notifierEnabled } = useAppSelector(NotificationsState);
+const NotificationListContainer: FC = () => {
     const { isLoading, error } = useAppSelector(UIState);
-    const { loadNotificationsData } = useLoadNotificationsData(moiraApi);
+    const { notifierEnabled, notificationList, notificationAmount } = useLoadNotificationsData();
+    const [deleteNotification] = useDeleteNotificationMutation();
+    const [deleteAllNotifications] = useDeleteAllNotificationsMutation();
+    const [deleleteAllNotificationEvents] = useDeleteAllNotificationEventsMutation();
+    const [setNotifierState] = useSetNotifierStateMutation();
     const [ConfirmModal, setModalData] = useConfirmModal();
 
-    const notificationAmount = notificationList.length;
     const layoutTitle = `Notifications ${notificationAmount}`;
 
-    const removeNotification = async (id: string) => {
-        dispatch(toggleLoading(true));
-        try {
-            await moiraApi.delNotification(id);
-            dispatch(deleteNotification(id));
-        } catch (error) {
-            dispatch(setError(error.message));
-        } finally {
-            dispatch(toggleLoading(false));
-        }
-    };
-
     const removeAllNotifications = async () => {
         setModalData({ isOpen: false });
-        dispatch(toggleLoading(true));
-        try {
-            await moiraApi.delAllNotificationEvents();
-            await moiraApi.delAllNotifications();
-            dispatch(deleteAllNotifications());
-        } catch (error) {
-            dispatch(setError(error.message));
-        } finally {
-            dispatch(toggleLoading(false));
-        }
+        deleteAllNotifications();
+        deleleteAllNotificationEvents();
     };
 
     const toggleNotifier = async (enable: boolean) => {
         setModalData({ isOpen: false });
-        dispatch(toggleLoading(true));
-        try {
-            const state = enable ? MoiraServiceStates.OK : MoiraServiceStates.ERROR;
-            await moiraApi.setNotifierState({ state });
-            dispatch(setNotifierEnabled(enable));
-        } catch (error) {
-            dispatch(setError(error.message));
-        } finally {
-            dispatch(toggleLoading(false));
-        }
+        const state = enable ? MoiraServiceStates.OK : MoiraServiceStates.ERROR;
+        setNotifierState({ state });
     };
 
     const handleDisableNotifier = () => {
@@ -87,20 +56,20 @@ const NotificationListContainer: FC<TProps> = ({ moiraApi }) => {
     };
 
     const onRemoveAllNotificationsBtnClick = () => {
-        setModalData({
-            isOpen: true,
-            header: ConfirmModalHeaderData.deleteAllNotifications(notificationAmount),
-            button: {
-                text: "Delete",
-                use: "danger",
-                onConfirm: removeAllNotifications,
-            },
-        });
+        notificationAmount &&
+            setModalData({
+                isOpen: true,
+                header: ConfirmModalHeaderData.deleteAllNotifications(notificationAmount),
+                button: {
+                    text: "Delete",
+                    use: "danger",
+                    onConfirm: removeAllNotifications,
+                },
+            });
     };
 
     useEffect(() => {
         setDocumentTitle("Notifications");
-        loadNotificationsData();
     }, []);
 
     return (
@@ -129,7 +98,7 @@ const NotificationListContainer: FC<TProps> = ({ moiraApi }) => {
                 {notificationList && (
                     <NotificationList
                         items={composeNotifications(notificationList)}
-                        onRemove={removeNotification}
+                        onRemove={(id) => deleteNotification({ id })}
                     />
                 )}
             </LayoutContent>
@@ -137,4 +106,4 @@ const NotificationListContainer: FC<TProps> = ({ moiraApi }) => {
     );
 };
 
-export default withMoiraApi(NotificationListContainer);
+export default NotificationListContainer;
diff --git a/src/Containers/PatternListContainer.tsx b/src/Containers/PatternListContainer.tsx
index 9ab552e0c..168691e29 100644
--- a/src/Containers/PatternListContainer.tsx
+++ b/src/Containers/PatternListContainer.tsx
@@ -1,61 +1,38 @@
-import React, { useState, useEffect } from "react";
-import MoiraApi from "../Api/MoiraApi";
-import { Pattern } from "../Domain/Pattern";
-import { withMoiraApi } from "../Api/MoiraApiInjection";
+import React, { useEffect } from "react";
 import PatternList from "../Components/PatternList/PatternList";
 import { Layout, LayoutContent, LayoutTitle } from "../Components/Layout/Layout";
 import { setDocumentTitle } from "../helpers/setDocumentTitle";
-import { toggleLoading, setError } from "../store/Reducers/UIReducer.slice";
-import { useAppDispatch, useAppSelector } from "../store/hooks";
+import { useAppSelector } from "../store/hooks";
 import { UIState } from "../store/selectors";
 import { useSortData } from "../hooks/useSortData";
+import { useDeletePatternMutation, useGetPatternsQuery } from "../services/PatternsApi";
 
-export type TPatternListContainerProps = { moiraApi: MoiraApi };
-
-const PatternListContainer: React.FC<TPatternListContainerProps> = ({ moiraApi }) => {
-    const dispatch = useAppDispatch();
+const PatternListContainer: React.FC = () => {
     const { isLoading, error } = useAppSelector(UIState);
-    const [list, setList] = useState<Pattern[] | undefined>();
-    const { sortedData, sortConfig, handleSort } = useSortData(list ?? [], "metrics");
+    const { data: patterns } = useGetPatternsQuery();
+    const [deletePattern] = useDeletePatternMutation();
+
+    const { sortedData, sortConfig, handleSort } = useSortData(patterns ?? [], "metrics");
 
     useEffect(() => {
         setDocumentTitle("Patterns");
-        getData();
     }, []);
 
-    const getData = async () => {
-        dispatch(toggleLoading(true));
-        try {
-            const { list } = await moiraApi.getPatternList();
-            setList(list);
-        } catch (error) {
-            dispatch(setError(error.message));
-        } finally {
-            dispatch(toggleLoading(false));
-        }
-    };
-
-    const removePattern = async (pattern: string) => {
-        dispatch(toggleLoading(true));
-        await moiraApi.delPattern(pattern);
-        getData();
-    };
-
     return (
         <Layout loading={isLoading} error={error}>
             <LayoutContent>
                 <LayoutTitle>Patterns</LayoutTitle>
-                {list && (
+                {
                     <PatternList
                         items={sortedData}
                         onSort={handleSort}
                         sortConfig={sortConfig}
-                        onRemove={removePattern}
+                        onRemove={deletePattern}
                     />
-                )}
+                }
             </LayoutContent>
         </Layout>
     );
 };
 
-export default withMoiraApi(PatternListContainer);
+export default PatternListContainer;
diff --git a/src/Containers/SettingsContainer.tsx b/src/Containers/SettingsContainer.tsx
index 7bc6fa9b8..db800264f 100644
--- a/src/Containers/SettingsContainer.tsx
+++ b/src/Containers/SettingsContainer.tsx
@@ -84,7 +84,7 @@ const SettingsContainer: FC<ISettingsContainerProps> = ({ isTeamMember, history
                 {settings && tags && (
                     <SubscriptionListContainer
                         teams={teams}
-                        tags={tags.list}
+                        tags={tags}
                         contacts={settings.contacts}
                         subscriptions={settings.subscriptions}
                     />
diff --git a/src/Containers/TriggerAddContainer.tsx b/src/Containers/TriggerAddContainer.tsx
index ead8633e0..2a08616de 100644
--- a/src/Containers/TriggerAddContainer.tsx
+++ b/src/Containers/TriggerAddContainer.tsx
@@ -1,11 +1,8 @@
 import React, { useState, useRef, useEffect } from "react";
-import { RouteComponentProps } from "react-router";
 import { ValidationContainer } from "@skbkontur/react-ui-validations";
 import { Button } from "@skbkontur/react-ui/components/Button";
 import { Fill, RowStack as LayoutRowStack } from "@skbkontur/react-stack-layout";
 import { useSaveTrigger } from "../hooks/useSaveTrigger";
-import MoiraApi from "../Api/MoiraApi";
-import { withMoiraApi } from "../Api/MoiraApiInjection";
 import { DEFAULT_TRIGGER_TTL, Trigger, TriggerSource } from "../Domain/Trigger";
 import { getPageLink } from "../Domain/Global";
 import { Status } from "../Domain/Status";
@@ -17,17 +14,17 @@ import TriggerEditForm from "../Components/TriggerEditForm/TriggerEditForm";
 import { RowStack, ColumnStack, Fit } from "../Components/ItemsStack/ItemsStack";
 import FileLoader from "../Components/FileLoader/FileLoader";
 import { useValidateTarget } from "../hooks/useValidateTarget";
-import {
-    setError,
-    setIsLoading,
-    setIsSaveButtonDisabled,
-    setIsSaveModalVisible,
-    useTriggerFormContainerReducer,
-} from "../hooks/useTriggerFormContainerReducer";
 import { TriggerSaveWarningModal } from "../Components/TriggerSaveWarningModal/TriggerSaveWarningModal";
 import { setDocumentTitle } from "../helpers/setDocumentTitle";
-import { useAppSelector } from "../store/hooks";
-import { ConfigState } from "../store/selectors";
+import { useAppDispatch, useAppSelector } from "../store/hooks";
+import { ConfigState, TriggerFormState, UIState } from "../store/selectors";
+import { useGetTagsQuery } from "../services/TagsApi";
+import { setError } from "../store/Reducers/UIReducer.slice";
+import {
+    setIsSaveModalVisible,
+    setIsSaveButtonDisabled,
+} from "../store/Reducers/TriggerFormReducer.slice";
+import { useHistory } from "react-router";
 
 const defaultTrigger: Partial<Trigger> = {
     name: "",
@@ -61,16 +58,17 @@ const defaultTrigger: Partial<Trigger> = {
     alone_metrics: {},
 };
 
-type Props = RouteComponentProps & { moiraApi: MoiraApi };
-
-const TriggerAddContainer = (props: Props) => {
+const TriggerAddContainer = () => {
+    const history = useHistory();
     const { config } = useAppSelector(ConfigState);
-    const [state, dispatch] = useTriggerFormContainerReducer();
+    const { validationResult, isSaveModalVisible } = useAppSelector(TriggerFormState);
+    const { isLoading, error } = useAppSelector(UIState);
+    const dispatch = useAppDispatch();
     const [trigger, setTrigger] = useState<Partial<Trigger>>(defaultTrigger);
-    const [tags, setTags] = useState<string[] | undefined>(undefined);
     const validationContainer = useRef<ValidationContainer>(null);
-    const validateTarget = useValidateTarget(props.moiraApi, dispatch, props.history);
-    const saveTrigger = useSaveTrigger(props.moiraApi, dispatch, props.history);
+    const { data: tags } = useGetTagsQuery();
+    const validateTarget = useValidateTarget(dispatch, history);
+    const saveTrigger = useSaveTrigger(history);
 
     const handleSubmit = async () => {
         const isFormValid = await validationContainer.current?.validate();
@@ -120,35 +118,30 @@ const TriggerAddContainer = (props: Props) => {
         }
     };
 
-    const getData = async () => {
+    const setTriggerWithSearchTags = () => {
         const localDataString = localStorage.getItem("moiraSettings");
         const { tags: localTags } = localDataString ? JSON.parse(localDataString) : { tags: [] };
 
-        try {
-            const { list } = await props.moiraApi.getTagList();
-            setTrigger((prev) => {
-                return { ...prev, tags: localTags };
-            });
-            setTags(list);
-        } catch (error) {
-            dispatch(setError(error.message));
-        } finally {
-            dispatch(setIsLoading(false));
-        }
+        setTrigger((prev) => {
+            return { ...prev, tags: localTags };
+        });
     };
 
     useEffect(() => {
         setDocumentTitle("Add trigger");
-        getData();
+        setTriggerWithSearchTags();
     }, []);
 
     return (
-        <Layout loading={state.isLoading} error={state.error}>
+        <Layout loading={isLoading} error={error}>
             <LayoutContent>
                 <TriggerSaveWarningModal
-                    isOpen={state.isSaveModalVisible}
+                    isOpen={isSaveModalVisible}
                     onClose={() => dispatch(setIsSaveModalVisible(false))}
-                    onSave={() => saveTrigger(trigger)}
+                    onSave={() => {
+                        saveTrigger(trigger);
+                        dispatch(setIsSaveModalVisible(false));
+                    }}
                 />
                 <LayoutRowStack baseline block gap={6} style={{ maxWidth: "800px" }}>
                     <Fill>
@@ -168,7 +161,7 @@ const TriggerAddContainer = (props: Props) => {
                                             metricSourceClusters={config.metric_source_clusters}
                                             tags={tags || []}
                                             onChange={handleChange}
-                                            validationResult={state.validationResult}
+                                            validationResult={validationResult}
                                         />
                                     )}
                                 </ValidationContainer>
@@ -198,4 +191,4 @@ const TriggerAddContainer = (props: Props) => {
     );
 };
 
-export default withMoiraApi(TriggerAddContainer);
+export default TriggerAddContainer;
diff --git a/src/Containers/TriggerDuplicateContainer.tsx b/src/Containers/TriggerDuplicateContainer.tsx
index 7201505c6..f98806105 100644
--- a/src/Containers/TriggerDuplicateContainer.tsx
+++ b/src/Containers/TriggerDuplicateContainer.tsx
@@ -3,55 +3,58 @@ import { RouteComponentProps } from "react-router";
 import { ValidationContainer } from "@skbkontur/react-ui-validations";
 import { Button } from "@skbkontur/react-ui/components/Button";
 import { useSaveTrigger } from "../hooks/useSaveTrigger";
-import MoiraApi from "../Api/MoiraApi";
-import { withMoiraApi } from "../Api/MoiraApiInjection";
 import TriggerSource, { Trigger } from "../Domain/Trigger";
 import { getPageLink } from "../Domain/Global";
 import RouterLink from "../Components/RouterLink/RouterLink";
 import { Layout, LayoutContent, LayoutTitle } from "../Components/Layout/Layout";
 import TriggerEditForm from "../Components/TriggerEditForm/TriggerEditForm";
 import { ColumnStack, RowStack, Fit } from "../Components/ItemsStack/ItemsStack";
-import {
-    setError,
-    setIsLoading,
-    setIsSaveButtonDisabled,
-    setIsSaveModalVisible,
-    useTriggerFormContainerReducer,
-} from "../hooks/useTriggerFormContainerReducer";
+import { setError } from "../store/Reducers/UIReducer.slice";
 import { useValidateTarget } from "../hooks/useValidateTarget";
 import { TriggerSaveWarningModal } from "../Components/TriggerSaveWarningModal/TriggerSaveWarningModal";
 import { setDocumentTitle } from "../helpers/setDocumentTitle";
-import { useAppSelector } from "../store/hooks";
-import { ConfigState } from "../store/selectors";
+import { useAppDispatch, useAppSelector } from "../store/hooks";
+import { ConfigState, TriggerFormState, UIState } from "../store/selectors";
+import { useGetTriggerQuery } from "../services/TriggerApi";
+import { useGetTagsQuery } from "../services/TagsApi";
+import {
+    setIsSaveModalVisible,
+    setIsSaveButtonDisabled,
+} from "../store/Reducers/TriggerFormReducer.slice";
+
+type Props = RouteComponentProps<{ id: string }>;
 
-// TODO check id wasn't undefined
-type Props = RouteComponentProps<{ id?: string }> & { moiraApi: MoiraApi };
+const cleanTrigger = (sourceTrigger: Trigger): Partial<Trigger> => {
+    const trigger: Partial<Trigger> = { ...sourceTrigger };
+
+    delete trigger.id;
+    delete trigger.last_check;
+    delete trigger.throttling;
+
+    return {
+        ...trigger,
+        name: `${trigger.name} (copy)`,
+        sched: trigger.sched
+            ? { ...trigger.sched, tzOffset: new Date().getTimezoneOffset() }
+            : undefined,
+    };
+};
 
 const TriggerDuplicateContainer = (props: Props) => {
     const { config } = useAppSelector(ConfigState);
-    const [state, dispatch] = useTriggerFormContainerReducer();
+    const { isSaveModalVisible, validationResult } = useAppSelector(TriggerFormState);
+    const { isLoading, error } = useAppSelector(UIState);
+    const dispatch = useAppDispatch();
+    const { id } = props.match.params;
+    const { data: sourceTrigger } = useGetTriggerQuery({
+        triggerId: id,
+    });
+    const { data: tags } = useGetTagsQuery();
     const [trigger, setTrigger] = useState<Partial<Trigger> | undefined>(undefined);
-    const [tags, setTags] = useState<string[] | undefined>(undefined);
 
     const validationContainer = useRef<ValidationContainer>(null);
-    const validateTarget = useValidateTarget(props.moiraApi, dispatch, props.history);
-    const saveTrigger = useSaveTrigger(props.moiraApi, dispatch, props.history);
-
-    const cleanTrigger = (sourceTrigger: Trigger): Partial<Trigger> => {
-        const trigger: Partial<Trigger> = { ...sourceTrigger };
-
-        delete trigger.id;
-        delete trigger.last_check;
-        delete trigger.throttling;
-
-        return {
-            ...trigger,
-            name: `${trigger.name} (copy)`,
-            sched: trigger.sched
-                ? { ...trigger.sched, tzOffset: new Date().getTimezoneOffset() }
-                : undefined,
-        };
-    };
+    const validateTarget = useValidateTarget(dispatch, props.history);
+    const saveTrigger = useSaveTrigger(props.history);
 
     const handleSubmit = async () => {
         const isFormValid = await validationContainer.current?.validate();
@@ -82,49 +85,31 @@ const TriggerDuplicateContainer = (props: Props) => {
             return { ...prev, ...update };
         });
         dispatch(setError(null));
-
         if (update.targets) {
             dispatch(setIsSaveButtonDisabled(false));
         }
     };
 
-    const getData = async () => {
-        const { id } = props.match.params;
-        if (typeof id !== "string") {
-            dispatch(setError("Wrong trigger id"));
-            dispatch(setIsLoading(false));
-            return;
-        }
-
-        try {
-            const [sourceTrigger, { list }] = await Promise.all([
-                props.moiraApi.getTrigger(id),
-                props.moiraApi.getTagList(),
-            ]);
-
-            const trigger = cleanTrigger(sourceTrigger);
-            setTrigger(trigger);
-            setTags(list);
-        } catch (error) {
-            dispatch(setError(error.message));
-        } finally {
-            dispatch(setIsLoading(false));
-        }
-    };
-
     useEffect(() => {
         setDocumentTitle("Duplicate trigger");
-        dispatch(setIsLoading(true));
-        getData();
-    }, []);
+
+        if (sourceTrigger) {
+            setTrigger(cleanTrigger(sourceTrigger));
+        } else {
+            setTrigger(undefined);
+        }
+    }, [sourceTrigger]);
 
     return (
-        <Layout loading={state.isLoading} error={state.error}>
+        <Layout loading={isLoading} error={error}>
             <LayoutContent>
                 <TriggerSaveWarningModal
-                    isOpen={state.isSaveModalVisible}
+                    isOpen={isSaveModalVisible}
                     onClose={() => dispatch(setIsSaveModalVisible(false))}
-                    onSave={() => saveTrigger(trigger)}
+                    onSave={() => {
+                        saveTrigger(trigger);
+                        dispatch(setIsSaveModalVisible(false));
+                    }}
                 />
                 <LayoutTitle>Duplicate trigger</LayoutTitle>
                 {trigger && (
@@ -139,7 +124,7 @@ const TriggerDuplicateContainer = (props: Props) => {
                                             remoteAllowed={config.remoteAllowed}
                                             metricSourceClusters={config.metric_source_clusters}
                                             onChange={handleChange}
-                                            validationResult={state.validationResult}
+                                            validationResult={validationResult}
                                         />
                                     )}
                                 </ValidationContainer>
@@ -175,4 +160,4 @@ const TriggerDuplicateContainer = (props: Props) => {
     );
 };
 
-export default withMoiraApi(TriggerDuplicateContainer);
+export default TriggerDuplicateContainer;
diff --git a/src/Containers/TriggerEditContainer.tsx b/src/Containers/TriggerEditContainer.tsx
index b0f2a892a..d324a33fd 100644
--- a/src/Containers/TriggerEditContainer.tsx
+++ b/src/Containers/TriggerEditContainer.tsx
@@ -3,38 +3,43 @@ import { RouteComponentProps } from "react-router";
 import { ValidationContainer } from "@skbkontur/react-ui-validations";
 import { Button } from "@skbkontur/react-ui";
 import { useSaveTrigger } from "../hooks/useSaveTrigger";
-import MoiraApi from "../Api/MoiraApi";
-import { withMoiraApi } from "../Api/MoiraApiInjection";
 import { Trigger, TriggerSource } from "../Domain/Trigger";
 import { getPageLink } from "../Domain/Global";
 import RouterLink from "../Components/RouterLink/RouterLink";
 import { Layout, LayoutContent, LayoutTitle } from "../Components/Layout/Layout";
 import TriggerEditForm from "../Components/TriggerEditForm/TriggerEditForm";
 import { ColumnStack, RowStack, Fit } from "../Components/ItemsStack/ItemsStack";
-import {
-    setError,
-    setIsLoading,
-    setIsSaveButtonDisabled,
-    setIsSaveModalVisible,
-    useTriggerFormContainerReducer,
-} from "../hooks/useTriggerFormContainerReducer";
 import { useValidateTarget } from "../hooks/useValidateTarget";
 import { TriggerSaveWarningModal } from "../Components/TriggerSaveWarningModal/TriggerSaveWarningModal";
 import { setDocumentTitle } from "../helpers/setDocumentTitle";
-import { useAppSelector } from "../store/hooks";
-import { ConfigState } from "../store/selectors";
+import { useAppDispatch, useAppSelector } from "../store/hooks";
+import { ConfigState, TriggerFormState, UIState } from "../store/selectors";
+import { setError } from "../store/Reducers/UIReducer.slice";
+import { useGetTagsQuery } from "../services/TagsApi";
+import { useGetTriggerQuery } from "../services/TriggerApi";
+import {
+    setIsSaveButtonDisabled,
+    setIsSaveModalVisible,
+} from "../store/Reducers/TriggerFormReducer.slice";
 
-type Props = RouteComponentProps<{ id?: string }> & { moiraApi: MoiraApi };
+type Props = RouteComponentProps<{ id: string }>;
 
 const TriggerEditContainer = (props: Props) => {
-    const [state, dispatch] = useTriggerFormContainerReducer();
     const [trigger, setTrigger] = useState<Trigger | undefined>(undefined);
-    const [tags, setTags] = useState<string[] | undefined>(undefined);
     const { config } = useAppSelector(ConfigState);
+    const { validationResult, isSaveModalVisible } = useAppSelector(TriggerFormState);
+    const { isLoading, error } = useAppSelector(UIState);
+    const dispatch = useAppDispatch();
+
+    const { id } = props.match.params;
+    const { data: sourceTrigger } = useGetTriggerQuery({
+        triggerId: id,
+    });
+    const { data: tags } = useGetTagsQuery();
 
     const validationContainer = useRef<ValidationContainer>(null);
-    const validateTarget = useValidateTarget(props.moiraApi, dispatch, props.history);
-    const saveTrigger = useSaveTrigger(props.moiraApi, dispatch, props.history);
+    const validateTarget = useValidateTarget(dispatch, props.history);
+    const saveTrigger = useSaveTrigger(props.history);
 
     const handleSubmit = async () => {
         const isFormValid = await validationContainer.current?.validate();
@@ -68,41 +73,26 @@ const TriggerEditContainer = (props: Props) => {
         }
     };
 
-    const getData = async () => {
-        if (typeof props.match.params.id !== "string") {
-            dispatch(setError("Wrong trigger id"));
-            dispatch(setIsLoading(false));
-            return;
-        }
-
-        try {
-            const [trigger, { list }] = await Promise.all([
-                props.moiraApi.getTrigger(props.match.params.id),
-                props.moiraApi.getTagList(),
-            ]);
-
-            setTrigger(trigger);
-            setTags(list);
-        } catch (error) {
-            dispatch(setError(error.message));
-        } finally {
-            dispatch(setIsLoading(false));
-        }
-    };
-
     useEffect(() => {
         setDocumentTitle("Edit trigger");
-        dispatch(setIsLoading(true));
-        getData();
-    }, []);
+
+        if (sourceTrigger) {
+            setTrigger(sourceTrigger);
+        } else {
+            setTrigger(undefined);
+        }
+    }, [sourceTrigger]);
 
     return (
-        <Layout loading={state.isLoading} error={state.error}>
+        <Layout loading={isLoading} error={error}>
             <LayoutContent>
                 <TriggerSaveWarningModal
-                    isOpen={state.isSaveModalVisible}
+                    isOpen={isSaveModalVisible}
                     onClose={() => dispatch(setIsSaveModalVisible(false))}
-                    onSave={() => saveTrigger(trigger)}
+                    onSave={() => {
+                        saveTrigger(trigger);
+                        dispatch(setIsSaveModalVisible(false));
+                    }}
                 />
                 <LayoutTitle>Edit trigger</LayoutTitle>
                 {trigger && (
@@ -117,7 +107,7 @@ const TriggerEditContainer = (props: Props) => {
                                             remoteAllowed={config.remoteAllowed}
                                             metricSourceClusters={config.metric_source_clusters}
                                             onChange={handleChange}
-                                            validationResult={state.validationResult}
+                                            validationResult={validationResult}
                                         />
                                     )}
                                 </ValidationContainer>
@@ -129,7 +119,6 @@ const TriggerEditContainer = (props: Props) => {
                                             use="primary"
                                             onClick={handleSubmit}
                                             data-tid="Save Trigger"
-                                            disabled={state.isSaveButtonDisabled}
                                         >
                                             Save trigger
                                         </Button>
@@ -149,4 +138,4 @@ const TriggerEditContainer = (props: Props) => {
     );
 };
 
-export default withMoiraApi(TriggerEditContainer);
+export default TriggerEditContainer;
diff --git a/src/Domain/Maintenance.test.ts b/src/Domain/Maintenance.test.ts
deleted file mode 100644
index f389a5e26..000000000
--- a/src/Domain/Maintenance.test.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import MockDate from "mockdate";
-import MoiraApi from "../Api/MoiraApi";
-import { calculateMaintenanceTime, Maintenance, setTriggerMaintenance } from "./Maintenance";
-
-const mockFn = jest.fn();
-
-describe("Maintenance", () => {
-    const triggerId = "f96c5608-bd00-4ea2-abd5-be677cdf7e03";
-    const api = ({ setMaintenance: mockFn } as unknown) as MoiraApi;
-
-    beforeEach(() => {
-        MockDate.set("Tue Jun 09 2020 09:00:00 GMT+0300");
-    });
-
-    afterEach(() => {
-        MockDate.reset();
-        mockFn.mockClear();
-    });
-
-    it(`call setTriggerMaintenance with off`, async () => {
-        await setTriggerMaintenance(api, triggerId, calculateMaintenanceTime(Maintenance.off));
-        expect(api.setMaintenance).toBeCalledWith(triggerId, { trigger: 0 });
-    });
-    it(`call setTriggerMaintenance with quarterHour "15 min"`, async () => {
-        await setTriggerMaintenance(
-            api,
-            triggerId,
-            calculateMaintenanceTime(Maintenance.quarterHour)
-        );
-        expect(api.setMaintenance).toBeCalledWith(triggerId, { trigger: 1591683300 });
-    });
-    it(`call setTriggerMaintenance with oneHour "1 hour"`, async () => {
-        await setTriggerMaintenance(api, triggerId, calculateMaintenanceTime(Maintenance.oneHour));
-        expect(api.setMaintenance).toBeCalledWith(triggerId, { trigger: 1591686000 });
-    });
-    it(`call setTriggerMaintenance with oneDay "1 day"`, async () => {
-        await setTriggerMaintenance(api, triggerId, calculateMaintenanceTime(Maintenance.oneDay));
-        expect(api.setMaintenance).toBeCalledWith(triggerId, { trigger: 1591768800 });
-    });
-    it(`call setTriggerMaintenance with oneWeek "1 week"`, async () => {
-        await setTriggerMaintenance(api, triggerId, calculateMaintenanceTime(Maintenance.oneWeek));
-        expect(api.setMaintenance).toBeCalledWith(triggerId, { trigger: 1592287200 });
-    });
-    it(`call setTriggerMaintenance with oneMonth "1 month"`, async () => {
-        await setTriggerMaintenance(api, triggerId, calculateMaintenanceTime(Maintenance.oneMonth));
-        expect(api.setMaintenance).toBeCalledWith(triggerId, { trigger: 1594274400 });
-    });
-});
diff --git a/src/Domain/Maintenance.ts b/src/Domain/Maintenance.ts
index c97aa4e63..39aa665f8 100644
--- a/src/Domain/Maintenance.ts
+++ b/src/Domain/Maintenance.ts
@@ -1,6 +1,5 @@
 import { getUnixTime, addMinutes } from "date-fns";
 import { getUTCDate } from "../helpers/DateUtil";
-import MoiraApi from "../Api/MoiraApi";
 
 export enum Maintenance {
     off = "off",
@@ -15,7 +14,6 @@ export enum Maintenance {
 }
 
 export const MaintenanceList = [
-    Maintenance.off,
     Maintenance.quarterHour,
     Maintenance.oneHour,
     Maintenance.threeHours,
@@ -24,6 +22,7 @@ export const MaintenanceList = [
     Maintenance.oneWeek,
     Maintenance.twoWeeks,
     Maintenance.oneMonth,
+    Maintenance.off,
 ];
 
 const MaintenanceTimes = {
@@ -64,22 +63,3 @@ export function calculateMaintenanceTime(maintenance: Maintenance): number {
         ? getUnixTime(addMinutes(getUTCDate(), maintenanceTime))
         : maintenanceTime;
 }
-
-export async function setMetricMaintenance(
-    moiraApi: MoiraApi,
-    triggerId: string,
-    metric: string,
-    maintenance: number
-): Promise<void> {
-    await moiraApi.setMaintenance(triggerId, {
-        metrics: { [metric]: maintenance },
-    });
-}
-
-export async function setTriggerMaintenance(
-    moiraApi: MoiraApi,
-    triggerId: string,
-    maintenance: number
-): Promise<void> {
-    await moiraApi.setMaintenance(triggerId, { trigger: maintenance });
-}
diff --git a/src/Domain/Subscription.ts b/src/Domain/Subscription.ts
index 97ae42f65..444f6cb2d 100644
--- a/src/Domain/Subscription.ts
+++ b/src/Domain/Subscription.ts
@@ -17,3 +17,9 @@ export type Subscription = {
         theme: "light" | "dark";
     };
 };
+
+export type SubscriptionCreateInfo = Omit<Subscription, "id" | "user" | "any_tags"> & {
+    id?: string;
+    user?: string;
+    any_tags: boolean;
+};
diff --git a/src/Domain/Tag.ts b/src/Domain/Tag.ts
index 20268f7ef..8cde198e9 100644
--- a/src/Domain/Tag.ts
+++ b/src/Domain/Tag.ts
@@ -8,3 +8,11 @@ export type TagStat = {
     subscriptions: Array<Subscription>;
     triggers: Array<string>;
 };
+
+export type TagList = {
+    list: Array<string>;
+};
+
+export type TagStatList = {
+    list: Array<TagStat>;
+};
diff --git a/src/PrivateRoutes/AdminRoute.tsx b/src/PrivateRoutes/AdminRoute.tsx
index c231a14a9..0dbe975ba 100644
--- a/src/PrivateRoutes/AdminRoute.tsx
+++ b/src/PrivateRoutes/AdminRoute.tsx
@@ -1,6 +1,5 @@
 import React, { ComponentType } from "react";
 import { Route, Redirect, RouteProps } from "react-router-dom";
-import type { TPatternListContainerProps } from "../Containers/PatternListContainer";
 import { getPagePath } from "../Domain/Global";
 import { useGetUserQuery } from "../services/UserApi";
 import { EUserRoles } from "../Domain/User";
@@ -12,7 +11,7 @@ import styles from "../../local_modules/styles/mixins.less";
 const cn = classNames.bind(styles);
 
 type PrivateRouteProps = RouteProps & {
-    component: ComponentType<TPatternListContainerProps | object>;
+    component: ComponentType<object>;
     exact?: boolean;
     path: string;
 };
diff --git a/src/Providers/Providers.tsx b/src/Providers/Providers.tsx
index d0c853a2e..3225c3879 100644
--- a/src/Providers/Providers.tsx
+++ b/src/Providers/Providers.tsx
@@ -1,19 +1,13 @@
 import React from "react";
-import MoiraApi from "../Api/MoiraApi";
-import { ApiProvider } from "../Api/MoiraApiInjection";
 import { Provider } from "react-redux";
 import { LocaleContext } from "@skbkontur/react-ui/lib/locale/LocaleContext";
 import { LangCodes } from "@skbkontur/react-ui/lib/locale";
 import { store } from "../store/store";
 
-const moiraApi = new MoiraApi("/api");
-
 export const Providers = ({ children }: { children: React.ReactNode }) => {
     return (
         <LocaleContext.Provider value={{ langCode: LangCodes.en_GB }}>
-            <ApiProvider value={moiraApi}>
-                <Provider store={store}>{children}</Provider>
-            </ApiProvider>
+            <Provider store={store}>{children}</Provider>
         </LocaleContext.Provider>
     );
 };
diff --git a/src/desktop.bundle.tsx b/src/desktop.bundle.tsx
index 8c603df38..0b37773f4 100644
--- a/src/desktop.bundle.tsx
+++ b/src/desktop.bundle.tsx
@@ -1,7 +1,7 @@
 import React, { ComponentType } from "react";
 import { Switch, Route } from "react-router-dom";
 import { hot } from "react-hot-loader/root";
-import HeaderContainer from "./Containers/HeaderContainer";
+import { HeaderContainer } from "./Containers/HeaderContainer";
 import Footer from "./Components/Footer/Footer";
 import TriggerEditContainer from "./Containers/TriggerEditContainer";
 import TriggerDuplicateContainer from "./Containers/TriggerDuplicateContainer";
@@ -33,9 +33,7 @@ const cn = classNames.bind(styles);
 type ResponsiveRouteProps = {
     exact?: boolean;
     path: string;
-    container:
-        | ComponentType<Omit<TriggerListProps, "moiraApi">>
-        | ComponentType<Omit<TriggerProps, "moiraApi">>;
+    container: ComponentType<TriggerListProps> | ComponentType<TriggerProps>;
     view: ComponentType<TriggerListDesktopProps> | ComponentType<TriggerDesktopProps>;
 };
 
diff --git a/src/hooks/useCreateSubscription.tsx b/src/hooks/useCreateSubscription.tsx
index 1f3c71564..a8b2130dc 100644
--- a/src/hooks/useCreateSubscription.tsx
+++ b/src/hooks/useCreateSubscription.tsx
@@ -5,7 +5,7 @@ import {
     useTestSubscriptionMutation,
 } from "../services/SubscriptionsApi";
 import { useCreateTeamSubscriptionMutation } from "../services/TeamsApi";
-import type { SubscriptionCreateInfo } from "../Api/MoiraApi";
+import type { SubscriptionCreateInfo } from "../Domain/Subscription";
 import { useAppDispatch } from "../store/hooks";
 import { BaseApi } from "../services/BaseApi";
 
diff --git a/src/hooks/useLoadNotificationsData.tsx b/src/hooks/useLoadNotificationsData.tsx
index c3ef0f4d0..566ad8aa6 100644
--- a/src/hooks/useLoadNotificationsData.tsx
+++ b/src/hooks/useLoadNotificationsData.tsx
@@ -1,29 +1,14 @@
-import { useDispatch } from "react-redux";
-import { toggleLoading } from "../store/Reducers/UIReducer.slice";
-import MoiraApi from "../Api/MoiraApi";
-import {
-    setNotificationList,
-    setNotifierEnabled,
-} from "../store/Reducers/NotificationListContainerReducer.slice";
-import { setError } from "../store/Reducers/UIReducer.slice";
 import MoiraServiceStates from "../Domain/MoiraServiceStates";
+import { useGetNotificationsQuery } from "../services/NotificationsApi";
+import { useGetNotifierStateQuery } from "../services/NotifierApi";
 
-export const useLoadNotificationsData = (moiraApi: MoiraApi) => {
-    const dispatch = useDispatch();
+export const useLoadNotificationsData = () => {
+    const { data: notifier } = useGetNotifierStateQuery();
+    const { data: notifications } = useGetNotificationsQuery();
 
-    const loadNotificationsData = async () => {
-        dispatch(toggleLoading(true));
-        try {
-            const { list } = await moiraApi.getNotificationList();
-            dispatch(setNotificationList(list));
-            const notifier = await moiraApi.getNotifierState();
-            dispatch(setNotifierEnabled(notifier?.state === MoiraServiceStates.OK));
-        } catch (error) {
-            dispatch(setError(error.message));
-        } finally {
-            dispatch(toggleLoading(false));
-        }
+    return {
+        notifierEnabled: notifier?.state === MoiraServiceStates.OK,
+        notificationList: notifications?.list,
+        notificationAmount: notifications?.total ?? 0,
     };
-
-    return { loadNotificationsData };
 };
diff --git a/src/hooks/useSaveTrigger.tsx b/src/hooks/useSaveTrigger.tsx
index 4bca87e28..11b83c911 100644
--- a/src/hooks/useSaveTrigger.tsx
+++ b/src/hooks/useSaveTrigger.tsx
@@ -1,34 +1,30 @@
-import { Action, setError, setIsLoading } from "./useTriggerFormContainerReducer";
 import { getPageLink } from "../Domain/Global";
-import MoiraApi from "../Api/MoiraApi";
-import { Dispatch } from "react";
 import { History } from "history";
 import { Trigger, triggerClientToPayload } from "../Domain/Trigger";
+import { useAddTriggerMutation, useSetTriggerMutation } from "../services/TriggerApi";
 
-export const useSaveTrigger = (
-    moiraApi: MoiraApi,
-    dispatch: Dispatch<Action>,
-    history: History<unknown>
-) => {
-    return async (trigger?: Trigger | Partial<Trigger>) => {
+export const useSaveTrigger = (history: History<unknown>) => {
+    const [setTrigger] = useSetTriggerMutation();
+    const [addTrigger] = useAddTriggerMutation();
+
+    return async (trigger?: Partial<Trigger>) => {
         if (!trigger) {
             return;
         }
 
         const triggerPayload = triggerClientToPayload(trigger);
 
-        dispatch(setIsLoading(true));
+        const triggerID = triggerPayload.id;
+        const action = triggerID
+            ? () => setTrigger({ id: triggerID, data: triggerPayload }).unwrap()
+            : () => addTrigger(triggerPayload).unwrap();
+
         try {
-            const triggerID = triggerPayload.id;
-            const action = triggerID
-                ? () => moiraApi.setTrigger(triggerID, triggerPayload)
-                : () => moiraApi.addTrigger(triggerPayload);
-            const { id } = await action();
+            const result = await action();
+            const { id } = result;
             history.push(getPageLink("trigger", id));
-        } catch (error) {
-            dispatch(setError(error.message));
-        } finally {
-            dispatch(setIsLoading(false));
+        } catch {
+            return;
         }
     };
 };
diff --git a/src/hooks/useTriggerFormContainerReducer.tsx b/src/hooks/useTriggerFormContainerReducer.tsx
deleted file mode 100644
index d50a85d18..000000000
--- a/src/hooks/useTriggerFormContainerReducer.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import { useReducer } from "react";
-import { ValidateTargetsResult } from "../Domain/Trigger";
-
-export interface State {
-    isLoading: boolean;
-    isSaveModalVisible: boolean;
-    isSaveButtonDisabled: boolean;
-    validationResult?: ValidateTargetsResult;
-    error?: string | null;
-}
-
-export enum ActionType {
-    setIsLoading = "setIsLoading",
-    setIsSaveButtonDisabled = "setIsSaveButtonDisabled",
-    setIsSaveModalVisible = "setIsSaveModalVisible",
-    setValidationResult = "setValidationResult",
-    setError = "setError",
-}
-
-export const setIsLoading = (payload: boolean): Action => ({
-    type: ActionType.setIsLoading,
-    payload,
-});
-export const setIsSaveButtonDisabled = (payload: boolean): Action => ({
-    type: ActionType.setIsSaveButtonDisabled,
-    payload,
-});
-export const setIsSaveModalVisible = (payload: boolean): Action => ({
-    type: ActionType.setIsSaveModalVisible,
-    payload,
-});
-export const setValidationResult = (payload: ValidateTargetsResult): Action => ({
-    type: ActionType.setValidationResult,
-    payload,
-});
-export const setError = (payload: string | null): Action => ({
-    type: ActionType.setError,
-    payload,
-});
-
-export type Action =
-    | {
-          type: ActionType.setIsLoading;
-          payload: boolean;
-      }
-    | {
-          type: ActionType.setIsSaveButtonDisabled;
-          payload: boolean;
-      }
-    | {
-          type: ActionType.setIsSaveModalVisible;
-          payload: boolean;
-      }
-    | {
-          type: ActionType.setValidationResult;
-          payload: ValidateTargetsResult;
-      }
-    | {
-          type: ActionType.setError;
-          payload: string | null;
-      };
-
-const initialState: State = {
-    isLoading: false,
-    isSaveModalVisible: false,
-    isSaveButtonDisabled: false,
-    validationResult: undefined,
-    error: null,
-};
-
-const reducer = (state: State, action: Action) => {
-    switch (action.type) {
-        case ActionType.setIsLoading:
-            return { ...state, isLoading: action.payload };
-        case ActionType.setIsSaveButtonDisabled:
-            return { ...state, isSaveButtonDisabled: action.payload };
-        case ActionType.setIsSaveModalVisible:
-            return { ...state, isSaveModalVisible: action.payload };
-        case ActionType.setValidationResult:
-            return { ...state, validationResult: action.payload };
-        case ActionType.setError:
-            return { ...state, error: action.payload };
-        default:
-            throw new Error(`Unknown action: ${JSON.stringify(action)}`);
-    }
-};
-
-export const useTriggerFormContainerReducer = () => useReducer(reducer, initialState);
diff --git a/src/hooks/useValidateTarget.tsx b/src/hooks/useValidateTarget.tsx
index 0e9ceb1e2..88795bcf1 100644
--- a/src/hooks/useValidateTarget.tsx
+++ b/src/hooks/useValidateTarget.tsx
@@ -1,38 +1,33 @@
 import { Dispatch } from "react";
-import MoiraApi from "../Api/MoiraApi";
 import type { Trigger } from "../Domain/Trigger";
 import {
     checkTriggerTarget,
     triggerClientToPayload,
     TriggerTargetProblemType,
 } from "../Domain/Trigger";
+import { useSaveTrigger } from "./useSaveTrigger";
+import { History } from "history";
+import { useValidateTargetMutation } from "../services/TriggerApi";
 import {
-    Action,
-    setError,
-    setIsLoading,
     setIsSaveButtonDisabled,
-    setIsSaveModalVisible,
     setValidationResult,
-} from "./useTriggerFormContainerReducer";
-import { useSaveTrigger } from "./useSaveTrigger";
-import { History } from "history";
+    setIsSaveModalVisible,
+} from "../store/Reducers/TriggerFormReducer.slice";
+import { Action } from "@reduxjs/toolkit";
 
-export const useValidateTarget = (
-    moiraApi: MoiraApi,
-    dispatch: Dispatch<Action>,
-    history: History<unknown>
-) => {
-    const saveTrigger = useSaveTrigger(moiraApi, dispatch, history);
+export const useValidateTarget = (dispatch: Dispatch<Action>, history: History<unknown>) => {
+    const saveTrigger = useSaveTrigger(history);
+    const [validateTarget] = useValidateTargetMutation();
 
     return async (trigger?: Trigger | Partial<Trigger>) => {
         if (!trigger) {
             return;
         }
 
-        dispatch(setIsLoading(true));
+        const triggerPayload = triggerClientToPayload(trigger);
+
         try {
-            const triggerPayload = triggerClientToPayload(trigger);
-            const validationResult = await moiraApi.validateTarget(triggerPayload);
+            const validationResult = await validateTarget(triggerPayload).unwrap();
 
             const doAnyTargetsHaveError = validationResult.targets.some((target) =>
                 checkTriggerTarget(target, TriggerTargetProblemType.BAD)
@@ -56,10 +51,8 @@ export const useValidateTarget = (
             }
 
             await saveTrigger(triggerPayload);
-        } catch (error) {
-            dispatch(setError(error.message));
-        } finally {
-            dispatch(setIsLoading(false));
+        } catch {
+            return;
         }
     };
 };
diff --git a/src/mobile.bundle.tsx b/src/mobile.bundle.tsx
index 84eb6407e..e19f947ed 100644
--- a/src/mobile.bundle.tsx
+++ b/src/mobile.bundle.tsx
@@ -15,9 +15,7 @@ import { TeamSettingsPrivateRoute } from "./PrivateRoutes/TeamSettingsPrivateRou
 type ResponsiveRouteProps = {
     exact?: boolean;
     path: string;
-    container:
-        | ComponentType<Omit<TriggerListProps, "moiraApi">>
-        | ComponentType<Omit<TriggerProps, "moiraApi">>;
+    container: ComponentType<TriggerListProps> | ComponentType<TriggerProps>;
     view: ComponentType<TriggerListMobileProps> | ComponentType<TriggerMobileProps>;
 };
 
diff --git a/src/pages/trigger-list/trigger-list.desktop.tsx b/src/pages/trigger-list/trigger-list.desktop.tsx
index 14aff3bb7..88c6205e0 100644
--- a/src/pages/trigger-list/trigger-list.desktop.tsx
+++ b/src/pages/trigger-list/trigger-list.desktop.tsx
@@ -1,4 +1,4 @@
-import React, { ReactElement } from "react";
+import React from "react";
 import { History } from "history";
 import difference from "lodash/difference";
 import { Paging } from "@skbkontur/react-ui/components/Paging";
@@ -23,98 +23,94 @@ export type TriggerListDesktopProps = {
     onChange: (update: TriggerListUpdate) => void;
     searchText: string;
     loading: boolean;
-    error?: string;
+    error?: string | null;
     onSetMetricMaintenance: (triggerId: string, metric: string, maintenance: number) => void;
     onRemoveMetric: (triggerId: string, metric: string) => void;
     history: History;
 };
 
-export default class TriggerListDesktop extends React.Component<TriggerListDesktopProps> {
-    public render(): ReactElement {
-        const {
-            selectedTags,
-            subscribedTags,
-            allTags,
-            onlyProblems,
-            triggers,
-            activePage,
-            pageCount,
-            onChange,
-            searchText,
-            loading,
-            error,
-            onSetMetricMaintenance,
-            onRemoveMetric,
-            history,
-        } = this.props;
-
-        return (
-            <Layout loading={loading} error={error}>
-                <LayoutPlate>
-                    <RowStack verticalAlign="baseline" block gap={3}>
-                        <Fill>
-                            <SearchSelector
-                                search={searchText}
-                                allTags={this.props.allTags}
-                                loading={this.props.loading}
-                                selectedTokens={selectedTags}
-                                subscribedTokens={difference(subscribedTags, selectedTags)}
-                                remainingTokens={difference(allTags, selectedTags)}
-                                onChange={this.handleChange}
-                                onSearch={this.handleSearch}
-                            />
-                        </Fill>
-                        <Fit>
-                            <Toggle
-                                checked={onlyProblems}
-                                onValueChange={(value: boolean) =>
-                                    onChange({ onlyProblems: value })
-                                }
-                            />{" "}
-                            Only Problems
-                        </Fit>
-                    </RowStack>
-                </LayoutPlate>
-                <LayoutContent>
-                    <ColumnStack block gap={6} horizontalAlign="stretch">
-                        <AddingButton to={getPageLink("triggerAdd")} />
-                        <TriggerList
-                            searchMode={searchText !== ""}
-                            items={triggers}
-                            onChange={onSetMetricMaintenance}
-                            onRemove={onRemoveMetric}
-                            history={history}
-                        />
-                    </ColumnStack>
-                </LayoutContent>
-                {pageCount > 1 && (
-                    <LayoutFooter>
-                        <Paging
-                            caption="Next page"
-                            activePage={activePage}
-                            pagesCount={pageCount}
-                            onPageChange={this.handlePageChange}
-                            withoutNavigationHint
-                        />
-                    </LayoutFooter>
-                )}
-            </Layout>
-        );
-    }
-
-    handlePageChange = (page: number) => {
-        this.props.onChange({ page });
+const TriggerListDesktop: React.FC<TriggerListDesktopProps> = ({
+    selectedTags,
+    subscribedTags,
+    allTags,
+    onlyProblems,
+    triggers,
+    activePage,
+    pageCount,
+    onChange,
+    searchText,
+    loading,
+    error,
+    onSetMetricMaintenance,
+    onRemoveMetric,
+    history,
+}) => {
+    const handlePageChange = (page: number) => {
+        onChange({ page });
         window.scrollTo({
             top: 0,
             behavior: "smooth",
         });
     };
 
-    handleChange = (tags: string[], searchText: string): void => {
-        this.props.onChange({ tags, searchText });
+    const handleChange = (tags: string[], searchText: string) => {
+        onChange({ tags, searchText });
     };
 
-    handleSearch = (searchText: string): void => {
-        this.props.onChange({ searchText });
+    const handleSearch = (searchText: string) => {
+        onChange({ searchText });
     };
-}
+
+    return (
+        <Layout loading={loading} error={error}>
+            <LayoutPlate>
+                <RowStack verticalAlign="baseline" block gap={3}>
+                    <Fill>
+                        <SearchSelector
+                            search={searchText}
+                            allTags={allTags}
+                            loading={loading}
+                            selectedTokens={selectedTags}
+                            subscribedTokens={difference(subscribedTags, selectedTags)}
+                            remainingTokens={difference(allTags, selectedTags)}
+                            onChange={handleChange}
+                            onSearch={handleSearch}
+                        />
+                    </Fill>
+                    <Fit>
+                        <Toggle
+                            checked={onlyProblems}
+                            onValueChange={(value: boolean) => onChange({ onlyProblems: value })}
+                        />{" "}
+                        Only Problems
+                    </Fit>
+                </RowStack>
+            </LayoutPlate>
+            <LayoutContent>
+                <ColumnStack block gap={6} horizontalAlign="stretch">
+                    <AddingButton to={getPageLink("triggerAdd")} />
+                    <TriggerList
+                        searchMode={!!searchText}
+                        items={triggers}
+                        onChange={onSetMetricMaintenance}
+                        onRemove={onRemoveMetric}
+                        history={history}
+                    />
+                </ColumnStack>
+            </LayoutContent>
+            {pageCount > 1 && (
+                <LayoutFooter>
+                    <Paging
+                        caption="Next page"
+                        activePage={activePage}
+                        pagesCount={pageCount}
+                        onPageChange={handlePageChange}
+                        withoutNavigationHint
+                    />
+                </LayoutFooter>
+            )}
+        </Layout>
+    );
+};
+
+export default TriggerListDesktop;
diff --git a/src/pages/trigger-list/trigger-list.tsx b/src/pages/trigger-list/trigger-list.tsx
index e20f9ba36..568e801bd 100644
--- a/src/pages/trigger-list/trigger-list.tsx
+++ b/src/pages/trigger-list/trigger-list.tsx
@@ -1,22 +1,27 @@
-import React, { ComponentType, ReactElement } from "react";
+import React, { useEffect, useState, ComponentType } from "react";
 import { RouteComponentProps } from "react-router-dom";
-import isEqual from "lodash/isEqual";
 import flattenDeep from "lodash/flattenDeep";
 import uniq from "lodash/uniq";
-import queryString from "query-string";
-import { withMoiraApi } from "../../Api/MoiraApiInjection";
-import { Trigger, TriggerList } from "../../Domain/Trigger";
+import qs from "qs";
+import { TriggerList } from "../../Domain/Trigger";
 import { MoiraUrlParams } from "../../Domain/MoiraUrlParams";
-import { setMetricMaintenance } from "../../Domain/Maintenance";
 import transformPageFromHumanToProgrammer from "../../logic/transformPageFromHumanToProgrammer";
 import { TriggerListMobileProps } from "./trigger-list.mobile";
 import { TriggerListDesktopProps } from "./trigger-list.desktop";
-import MoiraApi from "../../Api/MoiraApi";
 import { clearInput } from "../../helpers/common";
 import { setDocumentTitle } from "../../helpers/setDocumentTitle";
+import { useAppSelector } from "../../store/hooks";
+import { UIState } from "../../store/selectors";
+import { useGetUserSettingsQuery } from "../../services/UserApi";
+import { useGetTagsQuery } from "../../services/TagsApi";
+import {
+    useDeleteMetricMutation,
+    useGetTriggerListQuery,
+    useSetMetricsMaintenanceMutation,
+} from "../../services/TriggerApi";
 
 export type TriggerListUpdate = {
-    tags?: Array<string>;
+    tags?: string[];
     page?: number;
     searchText?: string;
     onlyProblems?: boolean;
@@ -24,188 +29,148 @@ export type TriggerListUpdate = {
 
 export type TriggerListProps = RouteComponentProps & {
     view: ComponentType<TriggerListDesktopProps | TriggerListMobileProps>;
-    moiraApi: MoiraApi;
 };
 
-type State = {
-    loading: boolean;
-    error?: string;
-    subscribedTags: string[];
-    allTags: string[];
-    triggers: Trigger[];
-    activePage: number;
-    pageCount: number;
+const parseLocationSearch = (search: string): MoiraUrlParams => {
+    const START_PAGE = 1;
+    const { page, tags, onlyProblems, searchText } = qs.parse(search, {
+        ignoreQueryPrefix: true,
+    });
+
+    return {
+        page:
+            Number.isNaN(Number(page)) || typeof page !== "string"
+                ? START_PAGE
+                : Math.abs(parseInt(page, 10)),
+        tags: Array.isArray(tags) ? tags.map((value) => value.toString()) : [],
+        onlyProblems: onlyProblems === "false" ? false : Boolean(onlyProblems),
+        searchText: clearInput(typeof searchText === "string" ? searchText : ""),
+    };
 };
 
-class TriggerListPage extends React.Component<TriggerListProps, State> {
-    state: State = {
-        loading: true,
-        subscribedTags: [],
-        allTags: [],
-        triggers: [],
-        activePage: 1,
-        pageCount: 1,
-    };
+const changeLocationSearch = (
+    history: RouteComponentProps["history"],
+    locationSearch: MoiraUrlParams,
+    update: TriggerListUpdate
+) => {
+    const settings = { ...locationSearch, ...update };
+    localStorage.setItem("moiraSettings", JSON.stringify({ ...settings, searchText: "" }));
+    history.push(`?${qs.stringify(settings, { arrayFormat: "indices", encode: true })}`);
+};
 
-    componentDidMount() {
-        setDocumentTitle("Triggers");
-        this.loadData();
+const loadLocalSettingsAndRedirectIfNeed = (
+    history: RouteComponentProps["history"],
+    locationSearch: MoiraUrlParams,
+    tags: Array<string>,
+    onlyProblems: boolean
+) => {
+    const localDataString = localStorage.getItem("moiraSettings");
+    const { tags: localTags, onlyProblems: localOnlyProblems }: TriggerListUpdate =
+        typeof localDataString === "string" ? JSON.parse(localDataString) : {};
+
+    const searchToUpdate: TriggerListUpdate = {};
+    const isTagParamEnabled = tags.length === 0 && localTags?.length;
+    const isOnlyProblemsParamEnabled = !onlyProblems && localOnlyProblems;
+
+    if (isTagParamEnabled) {
+        searchToUpdate.tags = localTags;
     }
-
-    componentDidUpdate({ location: prevLocation }: TriggerListProps): void {
-        const { location: currentLocation } = this.props;
-        if (!isEqual(prevLocation, currentLocation)) {
-            this.loadData();
-        }
+    if (isOnlyProblemsParamEnabled) {
+        searchToUpdate.onlyProblems = localOnlyProblems;
     }
-
-    static parseLocationSearch(search: string): MoiraUrlParams {
-        const START_PAGE = 1;
-        const { page, tags, onlyProblems, searchText } = queryString.parse(search, {
-            arrayFormat: "index",
-        });
-
-        return {
-            page:
-                Number.isNaN(Number(page)) || typeof page !== "string"
-                    ? START_PAGE
-                    : Math.abs(parseInt(page, 10)),
-            tags: Array.isArray(tags) ? tags.map((value) => value.toString()) : [],
-            onlyProblems: onlyProblems === "false" ? false : Boolean(onlyProblems),
-            searchText: clearInput(searchText || ""),
-        };
+    if (isTagParamEnabled || isOnlyProblemsParamEnabled) {
+        changeLocationSearch(history, locationSearch, searchToUpdate);
+        return true;
     }
+    return false;
+};
 
-    public render(): ReactElement {
-        const { location } = this.props;
-        const locationSearch = TriggerListPage.parseLocationSearch(location.search);
-        const { onlyProblems, tags, searchText } = locationSearch;
-
-        const {
-            loading,
-            error,
-            subscribedTags,
-            allTags,
-            triggers,
-            activePage,
-            pageCount,
-        } = this.state;
-        const { view: TriggerListView } = this.props;
-
-        return (
-            <TriggerListView
-                searchText={searchText}
-                selectedTags={tags}
-                subscribedTags={subscribedTags}
-                allTags={allTags}
-                onlyProblems={onlyProblems}
-                triggers={triggers}
-                activePage={activePage}
-                pageCount={pageCount}
-                loading={loading}
-                error={error}
-                onChange={this.changeLocationSearch}
-                onSetMetricMaintenance={this.setMetricMaintenance}
-                onRemoveMetric={this.removeMetric}
-                history={this.props.history}
-            />
-        );
+const checkPageAndRedirectIfNeeded = (
+    triggerList: TriggerList,
+    page: number,
+    changeLocationSearch: (update: TriggerListUpdate) => void
+) => {
+    const pages = Math.ceil(triggerList.total / triggerList.size);
+    if (page > pages && triggerList.total !== 0) {
+        changeLocationSearch({ page: pages || 1 });
+        return true;
     }
+    return false;
+};
 
-    private async loadData() {
-        const { location, moiraApi } = this.props;
-        const locationSearch = TriggerListPage.parseLocationSearch(location.search);
-        const redirected = this.loadLocalSettingsAndRedirectIfNeed(
-            locationSearch.tags,
-            locationSearch.onlyProblems
-        );
+const TriggerListPage: React.FC<TriggerListProps> = ({
+    view: TriggerListView,
+    location,
+    history,
+}) => {
+    const { isLoading, error } = useAppSelector(UIState);
+    const [activePage, setActivePage] = useState(1);
+    const [pageCount, setPageCount] = useState(1);
 
-        if (redirected) return;
-
-        try {
-            const [settings, triggers, tags] = await Promise.all([
-                moiraApi.getSettings(),
-                moiraApi.getTriggerList(
-                    transformPageFromHumanToProgrammer(locationSearch.page),
-                    locationSearch.onlyProblems,
-                    locationSearch.tags,
-                    locationSearch.searchText
-                ),
-                moiraApi.getTagList(),
-            ]);
-
-            if (this.checkPageAndRedirectIfNeed(triggers, locationSearch.page)) return;
-
-            this.setState({
-                subscribedTags: uniq(flattenDeep(settings.subscriptions.map((item) => item.tags))),
-                allTags: tags.list,
-                // TODO: check getTriggerList always return trigger list?
-                triggers: triggers.list ?? [],
-                activePage: locationSearch.page,
-                pageCount: Math.ceil(triggers.total / triggers.size),
-                loading: false,
-            });
-        } catch (error) {
-            this.setState({ loading: false, error: error.message });
-        }
-    }
+    const { data: settings } = useGetUserSettingsQuery();
+    const { data: tags } = useGetTagsQuery();
+    const locationSearch = parseLocationSearch(location.search);
 
-    checkPageAndRedirectIfNeed(triggers: TriggerList, page: number): boolean {
-        const pages = Math.ceil(triggers.total / triggers.size);
-        if (page > pages && triggers.total !== 0) {
-            const rightLastPage = pages || 1;
-            this.changeLocationSearch({ page: rightLastPage });
-            return true;
-        }
-        return false;
-    }
+    const [setMetricMaintenance] = useSetMetricsMaintenanceMutation();
+    const [deleteMetric] = useDeleteMetricMutation();
 
-    loadLocalSettingsAndRedirectIfNeed(tags: Array<string>, onlyProblems: boolean) {
-        const localDataString = localStorage.getItem("moiraSettings");
-        const { tags: localTags, onlyProblems: localOnlyProblems }: TriggerListUpdate =
-            typeof localDataString === "string" ? JSON.parse(localDataString) : {};
-
-        const searchToUpdate: TriggerListUpdate = {};
-        const isTagParamEnabled = tags.length === 0 && localTags?.length;
-        const isOnlyProblemsParamEnabled = !onlyProblems && localOnlyProblems;
-
-        if (isTagParamEnabled) {
-            searchToUpdate.tags = localTags;
-        }
-        if (isOnlyProblemsParamEnabled) {
-            searchToUpdate.onlyProblems = localOnlyProblems;
-        }
-        if (isTagParamEnabled || isOnlyProblemsParamEnabled) {
-            this.changeLocationSearch(searchToUpdate);
-            return true;
-        }
-        return false;
-    }
+    const { data: triggerList } = useGetTriggerListQuery({
+        page: transformPageFromHumanToProgrammer(locationSearch.page),
+        onlyProblems: locationSearch.onlyProblems,
+        tags: locationSearch.tags,
+        searchText: locationSearch.searchText,
+    });
 
-    changeLocationSearch = (update: TriggerListUpdate) => {
-        const { location, history } = this.props;
-        const locationSearch = TriggerListPage.parseLocationSearch(location.search);
-        const settings = { ...locationSearch, ...update };
-        localStorage.setItem("moiraSettings", JSON.stringify({ ...settings, searchText: "" }));
-        history.push(
-            `?${queryString.stringify(settings, {
-                arrayFormat: "index",
-                encode: true,
-            })}`
-        );
-    };
+    const subscribedTags = uniq(flattenDeep(settings?.subscriptions.map((item) => item.tags)));
 
-    setMetricMaintenance = async (triggerId: string, metric: string, maintenance: number) => {
-        this.setState({ loading: true });
-        const { moiraApi } = this.props;
-        setMetricMaintenance(moiraApi, triggerId, metric, maintenance);
-    };
+    useEffect(() => {
+        setDocumentTitle("Triggers");
+        const redirected = loadLocalSettingsAndRedirectIfNeed(
+            history,
+            locationSearch,
+            locationSearch.tags,
+            locationSearch.onlyProblems
+        );
 
-    removeMetric = async (triggerId: string, metric: string): Promise<void> => {
-        this.setState({ loading: true });
-        const { moiraApi } = this.props;
-        await moiraApi.delMetric(triggerId, metric);
-    };
-}
+        if (redirected || !triggerList) return;
+
+        if (
+            checkPageAndRedirectIfNeeded(triggerList, locationSearch.page, (update) =>
+                changeLocationSearch(history, locationSearch, update)
+            )
+        )
+            return;
+
+        setActivePage(locationSearch.page);
+        setPageCount(Math.ceil(triggerList.total / triggerList.size));
+    }, [triggerList]);
+
+    return (
+        <TriggerListView
+            searchText={locationSearch.searchText}
+            selectedTags={locationSearch.tags}
+            subscribedTags={subscribedTags}
+            allTags={tags ?? []}
+            onlyProblems={locationSearch.onlyProblems}
+            triggers={triggerList?.list ?? []}
+            activePage={activePage}
+            pageCount={pageCount}
+            loading={isLoading}
+            error={error}
+            onChange={(update) => changeLocationSearch(history, locationSearch, update)}
+            onSetMetricMaintenance={(triggerId: string, metric: string, maintenance: number) =>
+                setMetricMaintenance({
+                    triggerId,
+                    metrics: { [metric]: maintenance },
+                    tagsToInvalidate: ["TriggerList"],
+                })
+            }
+            onRemoveMetric={(triggerId, metric) =>
+                deleteMetric({ triggerId, metric, tagsToInvalidate: ["TriggerList"] })
+            }
+            history={history}
+        />
+    );
+};
 
-export default withMoiraApi(TriggerListPage);
+export default TriggerListPage;
diff --git a/src/pages/trigger/trigger.tsx b/src/pages/trigger/trigger.tsx
index a46e6cf1b..367db3547 100644
--- a/src/pages/trigger/trigger.tsx
+++ b/src/pages/trigger/trigger.tsx
@@ -44,12 +44,21 @@ const TriggerPage: React.FC<TriggerProps> = ({ view: TriggerView }) => {
     const handleDisableThrottling = async () => await deleteTriggerThrottling(triggerId);
 
     const handleSetTriggerMaintenance = async (maintenance: number) =>
-        await setTriggerMaintenance({ triggerId, maintenance });
+        await setTriggerMaintenance({
+            triggerId,
+            maintenance,
+            tagsToInvalidate: ["TriggerState", "TriggerList"],
+        });
 
     const handleSetMetricMaintenance = async (metric: string, maintenance: number) =>
-        await setMetricMaintenance({ triggerId, metrics: { [metric]: maintenance } });
+        await setMetricMaintenance({
+            triggerId,
+            metrics: { [metric]: maintenance },
+            tagsToInvalidate: ["TriggerState", "TriggerList"],
+        });
 
-    const handleRemoveMetric = async (metric: string) => await deleteMetric({ triggerId, metric });
+    const handleRemoveMetric = async (metric: string) =>
+        await deleteMetric({ triggerId, metric, tagsToInvalidate: ["TriggerState"] });
 
     const handleRemoveNoDataMetric = async () => await deleteNoDataMetrics(triggerId);
 
diff --git a/src/services/BaseApi.ts b/src/services/BaseApi.ts
index 56bdf69cd..a970fc808 100644
--- a/src/services/BaseApi.ts
+++ b/src/services/BaseApi.ts
@@ -71,6 +71,9 @@ export type TApiInvalidateTags =
     | "Team"
     | "TriggerState"
     | "Trigger"
+    | "Notifications"
+    | "Patterns"
+    | "TriggerList"
     | "AllTeams";
 
 export const BaseApi = createApi({
@@ -86,6 +89,9 @@ export const BaseApi = createApi({
         "Team",
         "TriggerState",
         "Trigger",
+        "Notifications",
+        "Patterns",
+        "TriggerList",
         "AllTeams",
     ],
     baseQuery: customFetchBaseQuery,
diff --git a/src/services/NotificationsApi.ts b/src/services/NotificationsApi.ts
new file mode 100644
index 000000000..f80eb8056
--- /dev/null
+++ b/src/services/NotificationsApi.ts
@@ -0,0 +1,56 @@
+import { NotificationList } from "../Domain/Notification";
+import { BaseApi, CustomBaseQueryArgs } from "./BaseApi";
+
+export const NotificationsApi = BaseApi.injectEndpoints({
+    endpoints: (builder) => ({
+        getNotifications: builder.query<NotificationList, CustomBaseQueryArgs | void>({
+            query: () => ({
+                url: "notification?start=0&end=-1",
+                method: "GET",
+                credentials: "same-origin",
+            }),
+            providesTags: ["Notifications"],
+        }),
+
+        deleteNotification: builder.mutation<void, CustomBaseQueryArgs<{ id: string }>>({
+            query: ({ id }) => ({
+                url: `notification?id=${encodeURIComponent(id)}`,
+                credentials: "same-origin",
+                method: "DELETE",
+            }),
+            invalidatesTags: (_result, error) => {
+                if (error) {
+                    return [];
+                }
+                return ["Notifications"];
+            },
+        }),
+        deleteAllNotifications: builder.mutation<void, CustomBaseQueryArgs | void>({
+            query: () => ({
+                url: "notification/all",
+                credentials: "same-origin",
+                method: "DELETE",
+            }),
+            invalidatesTags: (_result, error) => {
+                if (error) {
+                    return [];
+                }
+                return ["Notifications"];
+            },
+        }),
+        deleteAllNotificationEvents: builder.mutation<void, CustomBaseQueryArgs | void>({
+            query: () => ({
+                url: "event/all",
+                credentials: "same-origin",
+                method: "DELETE",
+            }),
+        }),
+    }),
+});
+
+export const {
+    useGetNotificationsQuery,
+    useDeleteNotificationMutation,
+    useDeleteAllNotificationsMutation,
+    useDeleteAllNotificationEventsMutation,
+} = NotificationsApi;
diff --git a/src/services/NotifierApi.ts b/src/services/NotifierApi.ts
new file mode 100644
index 000000000..a4d2d60c1
--- /dev/null
+++ b/src/services/NotifierApi.ts
@@ -0,0 +1,30 @@
+import { NotifierState } from "../Domain/MoiraServiceStates";
+import { BaseApi, CustomBaseQueryArgs } from "./BaseApi";
+
+export const NotifierApi = BaseApi.injectEndpoints({
+    endpoints: (builder) => ({
+        getNotifierState: builder.query<NotifierState, CustomBaseQueryArgs | void>({
+            query: () => ({
+                url: "health/notifier",
+                method: "GET",
+                credentials: "same-origin",
+            }),
+        }),
+        setNotifierState: builder.mutation<NotifierState, CustomBaseQueryArgs<NotifierState>>({
+            query: (state) => ({
+                url: `/health/notifier`,
+                method: "PUT",
+                credentials: "same-origin",
+                body: JSON.stringify(state),
+            }),
+            invalidatesTags: (_result, error) => {
+                if (error) {
+                    return [];
+                }
+                return ["Notifications"];
+            },
+        }),
+    }),
+});
+
+export const { useGetNotifierStateQuery, useSetNotifierStateMutation } = NotifierApi;
diff --git a/src/services/PatternsApi.ts b/src/services/PatternsApi.ts
new file mode 100644
index 000000000..de76a95bd
--- /dev/null
+++ b/src/services/PatternsApi.ts
@@ -0,0 +1,32 @@
+import { Pattern, PatternList } from "../Domain/Pattern";
+import { BaseApi, CustomBaseQueryArgs } from "./BaseApi";
+
+export const PatternsApi = BaseApi.injectEndpoints({
+    endpoints: (builder) => ({
+        getPatterns: builder.query<Array<Pattern>, CustomBaseQueryArgs | void>({
+            query: () => ({
+                url: "pattern",
+                method: "GET",
+                credentials: "same-origin",
+            }),
+            transformResponse: (response: PatternList) => response.list,
+            providesTags: ["Patterns"],
+        }),
+
+        deletePattern: builder.mutation<void, CustomBaseQueryArgs<string>>({
+            query: (pattern) => ({
+                url: `pattern/${encodeURIComponent(pattern)}`,
+                credentials: "same-origin",
+                method: "DELETE",
+            }),
+            invalidatesTags: (_result, error) => {
+                if (error) {
+                    return [];
+                }
+                return ["Patterns"];
+            },
+        }),
+    }),
+});
+
+export const { useGetPatternsQuery, useDeletePatternMutation } = PatternsApi;
diff --git a/src/services/SubscriptionsApi.ts b/src/services/SubscriptionsApi.ts
index 462d69bad..a137e1f18 100644
--- a/src/services/SubscriptionsApi.ts
+++ b/src/services/SubscriptionsApi.ts
@@ -1,4 +1,4 @@
-import { SubscriptionCreateInfo } from "../Api/MoiraApi";
+import { SubscriptionCreateInfo } from "../Domain/Subscription";
 import { Subscription } from "../Domain/Subscription";
 import { BaseApi, CustomBaseQueryArgs, TApiInvalidateTags } from "./BaseApi";
 
diff --git a/src/services/TagsApi.ts b/src/services/TagsApi.ts
index 68391a7bc..405b9b74a 100644
--- a/src/services/TagsApi.ts
+++ b/src/services/TagsApi.ts
@@ -1,11 +1,11 @@
 import { BaseApi, CustomBaseQueryArgs } from "./BaseApi";
-import { TagList, TagStatList } from "../Api/MoiraApi";
-import { TagStat } from "../Domain/Tag";
+import { TagList, TagStatList, TagStat } from "../Domain/Tag";
 
 export const TagsApi = BaseApi.injectEndpoints({
     endpoints: (builder) => ({
-        getTags: builder.query<TagList, CustomBaseQueryArgs | void>({
+        getTags: builder.query<string[], CustomBaseQueryArgs | void>({
             query: () => ({ url: "tag", method: "GET", credentials: "same-origin" }),
+            transformResponse: (response: TagList) => response.list,
         }),
         getTagStats: builder.query<Array<TagStat>, CustomBaseQueryArgs | void>({
             query: () => ({ url: "tag/stats", method: "GET", credentials: "same-origin" }),
diff --git a/src/services/TeamsApi.ts b/src/services/TeamsApi.ts
index 21fbccb18..c8f1444b4 100644
--- a/src/services/TeamsApi.ts
+++ b/src/services/TeamsApi.ts
@@ -1,4 +1,4 @@
-import { SubscriptionCreateInfo } from "../Api/MoiraApi";
+import { SubscriptionCreateInfo } from "../Domain/Subscription";
 import { Contact, TeamContactCreateInfo } from "../Domain/Contact";
 import { Settings } from "../Domain/Settings";
 import { Subscription } from "../Domain/Subscription";
diff --git a/src/services/TriggerApi.ts b/src/services/TriggerApi.ts
index 980ae3d2a..513dea8b2 100644
--- a/src/services/TriggerApi.ts
+++ b/src/services/TriggerApi.ts
@@ -1,9 +1,11 @@
 import { EventList } from "../Domain/Event";
 import { Status } from "../Domain/Status";
-import { Trigger, TriggerState } from "../Domain/Trigger";
-import { BaseApi, CustomBaseQueryArgs } from "./BaseApi";
+import { Trigger, TriggerList, TriggerState, ValidateTargetsResult } from "../Domain/Trigger";
+import { BaseApi, CustomBaseQueryArgs, TApiInvalidateTags } from "./BaseApi";
+import qs from "qs";
 
 const eventHistoryPageSize = 100;
+const triggerListPageSize = 20;
 
 export const TriggerApi = BaseApi.injectEndpoints({
     endpoints: (builder) => ({
@@ -13,32 +15,26 @@ export const TriggerApi = BaseApi.injectEndpoints({
                 triggerId: string;
                 page: number;
                 states?: Status[];
-                metric?: string | null;
+                metric?: string;
                 from?: number | null;
                 to?: number | null;
             }>
         >({
             query: ({ triggerId, page, states, metric, from, to }) => {
-                const params = new URLSearchParams({
-                    p: String(page),
-                    size: String(eventHistoryPageSize),
-                });
-
-                if (states?.length) {
-                    params.append("states", states.join(","));
-                }
-                if (metric) {
-                    params.append("metric", metric);
-                }
-                if (from) {
-                    params.append("from", String(from));
-                }
-                if (to) {
-                    params.append("to", String(to));
-                }
+                const params = qs.stringify(
+                    {
+                        p: page,
+                        size: eventHistoryPageSize,
+                        states: states?.length ? states : null,
+                        metric,
+                        from,
+                        to,
+                    },
+                    { arrayFormat: "comma", skipNulls: true }
+                );
 
                 return {
-                    url: `event/${encodeURIComponent(triggerId)}?${params.toString()}`,
+                    url: `event/${encodeURIComponent(triggerId)}?${params}`,
                     method: "GET",
                     credentials: "same-origin",
                 };
@@ -87,7 +83,11 @@ export const TriggerApi = BaseApi.injectEndpoints({
         }),
         setTriggerMaintenance: builder.mutation<
             void,
-            CustomBaseQueryArgs<{ triggerId: string; maintenance: number }>
+            CustomBaseQueryArgs<{
+                triggerId: string;
+                maintenance: number;
+                tagsToInvalidate?: TApiInvalidateTags[];
+            }>
         >({
             query: ({ triggerId, maintenance }) => ({
                 url: `trigger/${encodeURIComponent(triggerId)}/setMaintenance`,
@@ -95,19 +95,19 @@ export const TriggerApi = BaseApi.injectEndpoints({
                 credentials: "same-origin",
                 body: JSON.stringify({ trigger: maintenance }),
             }),
-            invalidatesTags: (_result, error) => {
+            invalidatesTags: (_result, error, { tagsToInvalidate = [] }) => {
                 if (error) {
                     return [];
                 }
-                return ["TriggerState"];
+                return tagsToInvalidate;
             },
         }),
-
         setMetricsMaintenance: builder.mutation<
             void,
             CustomBaseQueryArgs<{
                 triggerId: string;
                 metrics: { [metric: string]: number };
+                tagsToInvalidate?: TApiInvalidateTags[];
             }>
         >({
             query: ({ triggerId, metrics }) => ({
@@ -116,16 +116,20 @@ export const TriggerApi = BaseApi.injectEndpoints({
                 credentials: "same-origin",
                 body: JSON.stringify({ metrics }),
             }),
-            invalidatesTags: (_result, error) => {
+            invalidatesTags: (_result, error, { tagsToInvalidate = [] }) => {
                 if (error) {
                     return [];
                 }
-                return ["TriggerState"];
+                return tagsToInvalidate;
             },
         }),
         deleteMetric: builder.mutation<
             void,
-            CustomBaseQueryArgs<{ triggerId: string; metric: string }>
+            CustomBaseQueryArgs<{
+                triggerId: string;
+                metric: string;
+                tagsToInvalidate?: TApiInvalidateTags[];
+            }>
         >({
             query: ({ triggerId, metric }) => ({
                 url: `trigger/${encodeURIComponent(triggerId)}/metrics?name=${encodeURIComponent(
@@ -134,11 +138,11 @@ export const TriggerApi = BaseApi.injectEndpoints({
                 method: "DELETE",
                 credentials: "same-origin",
             }),
-            invalidatesTags: (_result, error) => {
+            invalidatesTags: (_result, error, { tagsToInvalidate = [] }) => {
                 if (error) {
                     return [];
                 }
-                return ["TriggerState"];
+                return tagsToInvalidate;
             },
         }),
         deleteTrigger: builder.mutation<void, CustomBaseQueryArgs<string>>({
@@ -147,6 +151,12 @@ export const TriggerApi = BaseApi.injectEndpoints({
                 method: "DELETE",
                 credentials: "same-origin",
             }),
+            invalidatesTags: (_result, error) => {
+                if (error) {
+                    return [];
+                }
+                return ["TriggerList"];
+            },
         }),
         deleteNoDataMetric: builder.mutation<void, CustomBaseQueryArgs<string>>({
             query: (triggerId) => ({
@@ -161,6 +171,72 @@ export const TriggerApi = BaseApi.injectEndpoints({
                 return ["TriggerState"];
             },
         }),
+        validateTarget: builder.mutation<
+            ValidateTargetsResult,
+            CustomBaseQueryArgs<Partial<Trigger>>
+        >({
+            query: (trigger) => ({
+                url: "trigger/check",
+                method: "PUT",
+                body: JSON.stringify(trigger),
+                credentials: "same-origin",
+            }),
+        }),
+        setTrigger: builder.mutation<
+            {
+                [key: string]: string;
+            },
+            CustomBaseQueryArgs<{ id: string; data: Partial<Trigger> }>
+        >({
+            query: ({ id, data }) => ({
+                url: `trigger/${encodeURIComponent(id)}`,
+                method: "PUT",
+                body: JSON.stringify(data),
+                credentials: "same-origin",
+            }),
+        }),
+        addTrigger: builder.mutation<
+            {
+                [key: string]: string;
+            },
+            CustomBaseQueryArgs<Partial<Trigger>>
+        >({
+            query: (data) => ({
+                url: "trigger",
+                method: "PUT",
+                body: JSON.stringify(data),
+                credentials: "same-origin",
+            }),
+        }),
+        getTriggerList: builder.query<
+            TriggerList,
+            CustomBaseQueryArgs<{
+                page: number;
+                onlyProblems: boolean;
+                tags?: Array<string>;
+                searchText?: string;
+            }>
+        >({
+            query: ({ page, onlyProblems, tags = [], searchText = "" }) => {
+                const params = qs.stringify(
+                    {
+                        p: page,
+                        size: triggerListPageSize,
+                        tags,
+                        onlyProblems,
+                        text: searchText,
+                    },
+                    { arrayFormat: "indices", skipNulls: true, encode: true }
+                );
+
+                return {
+                    url: `/trigger/search?${params}`,
+                    method: "GET",
+                    credentials: "same-origin",
+                };
+            },
+            providesTags: ["TriggerList"],
+        }),
     }),
 });
 
@@ -174,4 +250,8 @@ export const {
     useGetTriggerStateQuery,
     useSetMetricsMaintenanceMutation,
     useSetTriggerMaintenanceMutation,
+    useValidateTargetMutation,
+    useSetTriggerMutation,
+    useAddTriggerMutation,
+    useGetTriggerListQuery,
 } = TriggerApi;
diff --git a/src/store/Reducers/NotificationListContainerReducer.slice.ts b/src/store/Reducers/NotificationListContainerReducer.slice.ts
deleted file mode 100644
index 76946710a..000000000
--- a/src/store/Reducers/NotificationListContainerReducer.slice.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { PayloadAction, createSlice } from "@reduxjs/toolkit";
-import { Notification } from "../../Domain/Notification";
-
-interface INotificationListContainerState {
-    notificationList: Notification[];
-    notifierEnabled: boolean;
-}
-
-const initialState: INotificationListContainerState = {
-    notificationList: [],
-    notifierEnabled: true,
-};
-
-const NotificationSlice = createSlice({
-    name: "notifications",
-    initialState,
-    reducers: {
-        setNotificationList: (state, action: PayloadAction<Notification[]>) => {
-            state.notificationList = action.payload;
-        },
-        setNotifierEnabled: (state, action: PayloadAction<boolean>) => {
-            state.notifierEnabled = action.payload;
-        },
-        deleteNotification: (state, action: PayloadAction<string>) => {
-            state.notificationList = state.notificationList.filter(
-                (item) => item.timestamp + item.contact.id + item.event.sub_id !== action.payload
-            );
-        },
-        deleteAllNotifications: (state) => {
-            state.notificationList = [];
-        },
-    },
-});
-
-export const {
-    setNotificationList,
-    setNotifierEnabled,
-    deleteNotification,
-    deleteAllNotifications,
-} = NotificationSlice.actions;
-
-export default NotificationSlice.reducer;
diff --git a/src/store/Reducers/SettingsContainerReducer.slice.ts b/src/store/Reducers/SettingsContainerReducer.slice.ts
deleted file mode 100644
index 71e1fd2ab..000000000
--- a/src/store/Reducers/SettingsContainerReducer.slice.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { PayloadAction, createSlice } from "@reduxjs/toolkit";
-import type { Settings } from "../../Domain/Settings";
-import { Team } from "../../Domain/Team";
-import { TagList } from "../../Api/MoiraApi";
-
-interface TeamsAndTags {
-    login: string;
-    teams: Team[];
-    tags: TagList;
-    team?: Team;
-}
-
-export interface ISettingsContainerState {
-    teamsAndTags?: TeamsAndTags;
-    settings?: Settings;
-}
-
-const initialState: ISettingsContainerState = {
-    teamsAndTags: undefined,
-    settings: undefined,
-};
-
-const SettingsSlice = createSlice({
-    name: "settings",
-    initialState,
-    reducers: {
-        setTeamsAndTags: (state, action: PayloadAction<Partial<TeamsAndTags>>) => {
-            state.teamsAndTags = { ...state.teamsAndTags, ...(action.payload as TeamsAndTags) };
-        },
-        setSettings: (state, action: PayloadAction<Partial<Settings>>) => {
-            state.settings = { ...state.settings, ...(action.payload as Settings) };
-        },
-    },
-});
-
-export const { setTeamsAndTags, setSettings } = SettingsSlice.actions;
-
-export default SettingsSlice.reducer;
diff --git a/src/store/Reducers/TriggerFormReducer.slice.ts b/src/store/Reducers/TriggerFormReducer.slice.ts
new file mode 100644
index 000000000..7110156b5
--- /dev/null
+++ b/src/store/Reducers/TriggerFormReducer.slice.ts
@@ -0,0 +1,38 @@
+import { createSlice, PayloadAction } from "@reduxjs/toolkit";
+import { ValidateTargetsResult } from "../../Domain/Trigger";
+
+interface State {
+    isSaveModalVisible: boolean;
+    isSaveButtonDisabled: boolean;
+    validationResult?: ValidateTargetsResult;
+}
+
+const initialState: State = {
+    isSaveModalVisible: false,
+    isSaveButtonDisabled: false,
+    validationResult: undefined,
+};
+
+export const triggerFormSlice = createSlice({
+    name: "triggerForm",
+    initialState,
+    reducers: {
+        setIsSaveButtonDisabled: (state, action: PayloadAction<boolean>) => {
+            state.isSaveButtonDisabled = action.payload;
+        },
+        setIsSaveModalVisible: (state, action: PayloadAction<boolean>) => {
+            state.isSaveModalVisible = action.payload;
+        },
+        setValidationResult: (state, action: PayloadAction<ValidateTargetsResult>) => {
+            state.validationResult = action.payload;
+        },
+    },
+});
+
+export const {
+    setIsSaveButtonDisabled,
+    setIsSaveModalVisible,
+    setValidationResult,
+} = triggerFormSlice.actions;
+
+export default triggerFormSlice.reducer;
diff --git a/src/store/selectors.ts b/src/store/selectors.ts
index 507026601..48a64f56b 100644
--- a/src/store/selectors.ts
+++ b/src/store/selectors.ts
@@ -1,5 +1,5 @@
 import { RootState } from "./store";
 
-export const NotificationsState = (state: RootState) => state.NotificationListContainerReducer;
 export const UIState = (state: RootState) => state.UIReducer;
 export const ConfigState = (state: RootState) => state.ConfigReducer;
+export const TriggerFormState = (state: RootState) => state.TriggerFormReducer;
diff --git a/src/store/store.ts b/src/store/store.ts
index f150b4040..1e869c9c0 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -1,16 +1,16 @@
 import { configureStore } from "@reduxjs/toolkit";
-import NotificationListContainerReducer from "./Reducers/NotificationListContainerReducer.slice";
 import UIReducer from "./Reducers/UIReducer.slice";
 import { BaseApi } from "../services/BaseApi";
 import ConfigReducer from "./Reducers/ConfigReducer.slice";
+import TriggerFormReducer from "./Reducers/TriggerFormReducer.slice";
 import { rtkQueryErrorAndLoadingHandler } from "../services/rtkQueryErrorAndLoadingHandler";
 
 export const store = configureStore({
     reducer: {
         [BaseApi.reducerPath]: BaseApi.reducer,
         ConfigReducer,
-        NotificationListContainerReducer,
         UIReducer,
+        TriggerFormReducer,
     },
 
     middleware: (getDefaultMiddleware) =>