diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts
index 366dc08c02e1d2a166bf86fa96888cb4c930cc65..c44dddea412cdd119461fd28282fb19844d96b49 100644
--- a/packages/backend/src/core/NotificationService.ts
+++ b/packages/backend/src/core/NotificationService.ts
@@ -43,6 +43,7 @@ export class NotificationService implements OnApplicationShutdown {
 	@bindThis
 	public async readAllNotification(
 		userId: User['id'],
+		force = false,
 	) {
 		const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${userId}`);
 		
@@ -57,7 +58,7 @@ export class NotificationService implements OnApplicationShutdown {
 
 		this.redisClient.set(`latestReadNotification:${userId}`, latestNotificationId);
 
-		if (latestReadNotificationId == null || (latestReadNotificationId < latestNotificationId)) {
+		if (force || latestReadNotificationId == null || (latestReadNotificationId < latestNotificationId)) {
 			return this.postReadAllNotifications(userId);
 		}
 	}
@@ -95,7 +96,7 @@ export class NotificationService implements OnApplicationShutdown {
 			...data,
 		} as Notification;
 
-		this.redisClient.xadd(
+		const redisIdPromise = this.redisClient.xadd(
 			`notificationTimeline:${notifieeId}`,
 			'MAXLEN', '~', '300',
 			`${this.idService.parse(notification.id).date.getTime()}-*`,
@@ -109,7 +110,7 @@ export class NotificationService implements OnApplicationShutdown {
 		// 2秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する
 		setTimeout(2000, 'unread notification', { signal: this.#shutdownController.signal }).then(async () => {
 			const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${notifieeId}`);
-			if (latestReadNotificationId && (latestReadNotificationId >= notification.id)) return;
+			if (latestReadNotificationId && (latestReadNotificationId >= await redisIdPromise)) return;
 
 			this.globalEventService.publishMainStream(notifieeId, 'unreadNotification', packed);
 			this.pushNotificationService.pushNotification(notifieeId, 'notification', packed);
diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts
index 9ba6079189adecf6870368c345fbf2521a9d2580..e601bf9d5b0dd338be73e5bdcd47590e4b979026 100644
--- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts
+++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts
@@ -24,7 +24,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 		private notificationService: NotificationService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			this.notificationService.readAllNotification(me.id);
+			this.notificationService.readAllNotification(me.id, true);
 		});
 	}
 }
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index e57484be6b806e1b1ae9aeb82bba63d7650ec55e..2511f14dcc57ab2bdff8c45a8617513543ac12b0 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -166,8 +166,6 @@ export type Channels = {
             readAllAntennas: () => void;
             unreadAntenna: (payload: Antenna) => void;
             readAllAnnouncements: () => void;
-            readAllChannels: () => void;
-            unreadChannel: (payload: Note['id']) => void;
             myTokenRegenerated: () => void;
             reversiNoInvites: () => void;
             reversiInvited: (payload: FIXME) => void;
@@ -1857,12 +1855,6 @@ export type Endpoints = {
         req: NoParams;
         res: null;
     };
-    'notifications/read': {
-        req: {
-            notificationId: Notification_2['id'];
-        };
-        res: null;
-    };
     'page-push': {
         req: {
             pageId: Page['id'];
@@ -2361,7 +2353,6 @@ type MeDetailed = UserDetailed & {
     hasPendingReceivedFollowRequest: boolean;
     hasUnreadAnnouncement: boolean;
     hasUnreadAntenna: boolean;
-    hasUnreadChannel: boolean;
     hasUnreadMentions: boolean;
     hasUnreadMessagingMessage: boolean;
     hasUnreadNotification: boolean;
@@ -2618,7 +2609,11 @@ export class Stream extends EventEmitter<StreamEvents> {
     // (undocumented)
     removeSharedConnectionPool(pool: Pool): void;
     // (undocumented)
-    send(typeOrPayload: any, payload?: any): void;
+    send(typeOrPayload: string): void;
+    // (undocumented)
+    send(typeOrPayload: string, payload: any): void;
+    // (undocumented)
+    send(typeOrPayload: Record<string, any> | any[]): void;
     // (undocumented)
     state: 'initializing' | 'reconnecting' | 'connected';
     // (undocumented)
@@ -2714,8 +2709,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u
 //
 // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts
 // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
-// src/api.types.ts:595:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
-// src/streaming.types.ts:35:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts
+// src/api.types.ts:594:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
+// src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts
 
 // (No @packageDocumentation comment for this package)
 
diff --git a/packages/misskey-js/src/streaming.ts b/packages/misskey-js/src/streaming.ts
index 12f386ddbfc928d3a7500177c6acda4649633f5d..0218b40a03cd6f4acf05b7d4d61c2129daaad225 100644
--- a/packages/misskey-js/src/streaming.ts
+++ b/packages/misskey-js/src/streaming.ts
@@ -169,14 +169,21 @@ export default class Stream extends EventEmitter<StreamEvents> {
 
 	/**
 	 * Send a message to connection
+	 * ! ストリーム上のやり取りはすべてJSONで行われます !
 	 */
-	public send(typeOrPayload: any, payload?: any): void {
-		const data = payload === undefined ? typeOrPayload : {
-			type: typeOrPayload,
-			body: payload,
-		};
+	public send(typeOrPayload: string): void
+	public send(typeOrPayload: string, payload: any): void
+	public send(typeOrPayload: Record<string, any> | any[]): void
+	public send(typeOrPayload: string | Record<string, any> | any[], payload?: any): void {
+		if (typeof typeOrPayload === 'string') {
+			this.stream.send(JSON.stringify({
+				type: typeOrPayload,
+				...(payload === undefined ? {} : { body: payload }),
+			}));
+			return;
+		}
 
-		this.stream.send(JSON.stringify(data));
+		this.stream.send(JSON.stringify(typeOrPayload));
 	}
 
 	/**