diff --git a/package.json b/package.json
index 0a3026e17e..ae90d21301 100644
--- a/package.json
+++ b/package.json
@@ -88,7 +88,7 @@
 		"autwh": "0.1.0",
 		"bcryptjs": "2.4.3",
 		"bootstrap-vue": "2.0.0-rc.6",
-		"cafy": "3.2.1",
+		"cafy": "5.1.0",
 		"chai": "4.1.2",
 		"chai-http": "4.0.0",
 		"chalk": "2.4.0",
diff --git a/src/cafy-id.ts b/src/cafy-id.ts
new file mode 100644
index 0000000000..1109d42d8f
--- /dev/null
+++ b/src/cafy-id.ts
@@ -0,0 +1,29 @@
+import * as mongo from 'mongodb';
+import { Query } from 'cafy';
+
+export const isAnId = x => mongo.ObjectID.isValid(x);
+export const isNotAnId = x => !isAnId(x);
+
+/**
+ * ID
+ */
+export default class ID extends Query<mongo.ObjectID> {
+	constructor(...args) {
+		super(...args);
+
+		this.transform = v => {
+			if (isAnId(v) && !mongo.ObjectID.prototype.isPrototypeOf(v)) {
+				return new mongo.ObjectID(v);
+			} else {
+				return v;
+			}
+		};
+
+		this.pushFirstTimeValidator(v => {
+			if (!mongo.ObjectID.prototype.isPrototypeOf(v) && isNotAnId(v)) {
+				return new Error('must-be-an-id');
+			}
+			return true;
+		});
+	}
+}
diff --git a/src/models/note-reaction.ts b/src/models/note-reaction.ts
index 7891ebdf17..f78b0d9d01 100644
--- a/src/models/note-reaction.ts
+++ b/src/models/note-reaction.ts
@@ -1,5 +1,5 @@
 import * as mongo from 'mongodb';
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import deepcopy = require('deepcopy');
 import db from '../db/mongodb';
 import Reaction from './note-reaction';
diff --git a/src/models/user-list.ts b/src/models/user-list.ts
index 66e2afe213..7100fced7e 100644
--- a/src/models/user-list.ts
+++ b/src/models/user-list.ts
@@ -1,4 +1,5 @@
 import * as mongo from 'mongodb';
+import deepcopy = require('deepcopy');
 import db from '../db/mongodb';
 
 const UserList = db.get<IUserList>('userList');
@@ -38,3 +39,29 @@ export async function deleteUserList(userList: string | mongo.ObjectID | IUserLi
 		_id: u._id
 	});
 }
+
+export const pack = (
+	userList: string | mongo.ObjectID | IUserList
+) => new Promise<any>(async (resolve, reject) => {
+	let _userList: any;
+
+	if (mongo.ObjectID.prototype.isPrototypeOf(userList)) {
+		_userList = await UserList.findOne({
+			_id: userList
+		});
+	} else if (typeof userList === 'string') {
+		_userList = await UserList.findOne({
+			_id: new mongo.ObjectID(userList)
+		});
+	} else {
+		_userList = deepcopy(userList);
+	}
+
+	if (!_userList) throw `invalid userList arg ${userList}`;
+
+	// Rename _id to id
+	_userList.id = _userList._id;
+	delete _userList._id;
+
+	resolve(_userList);
+});
diff --git a/src/server/api/endpoints/aggregation/posts.ts b/src/server/api/endpoints/aggregation/posts.ts
index cc2a48b53d..17bead2808 100644
--- a/src/server/api/endpoints/aggregation/posts.ts
+++ b/src/server/api/endpoints/aggregation/posts.ts
@@ -6,9 +6,6 @@ import Note from '../../../../models/note';
 
 /**
  * Aggregate notes
- *
- * @param {any} params
- * @return {Promise<any>}
  */
 module.exports = params => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/aggregation/users.ts b/src/server/api/endpoints/aggregation/users.ts
index 19776ed297..b0a7632f24 100644
--- a/src/server/api/endpoints/aggregation/users.ts
+++ b/src/server/api/endpoints/aggregation/users.ts
@@ -6,9 +6,6 @@ import User from '../../../../models/user';
 
 /**
  * Aggregate users
- *
- * @param {any} params
- * @return {Promise<any>}
  */
 module.exports = params => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/aggregation/users/activity.ts b/src/server/api/endpoints/aggregation/users/activity.ts
index 318cce77a5..d36e07a441 100644
--- a/src/server/api/endpoints/aggregation/users/activity.ts
+++ b/src/server/api/endpoints/aggregation/users/activity.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import User from '../../../../../models/user';
 import Note from '../../../../../models/note';
 
@@ -9,9 +9,6 @@ import Note from '../../../../../models/note';
 
 /**
  * Aggregate activity of a user
- *
- * @param {any} params
- * @return {Promise<any>}
  */
 module.exports = (params) => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
@@ -19,7 +16,7 @@ module.exports = (params) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Lookup user
diff --git a/src/server/api/endpoints/aggregation/users/followers.ts b/src/server/api/endpoints/aggregation/users/followers.ts
index 7ccb2a3066..a6dd29e735 100644
--- a/src/server/api/endpoints/aggregation/users/followers.ts
+++ b/src/server/api/endpoints/aggregation/users/followers.ts
@@ -1,19 +1,16 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import User from '../../../../../models/user';
 import FollowedLog from '../../../../../models/followed-log';
 
 /**
  * Aggregate followers of a user
- *
- * @param {any} params
- * @return {Promise<any>}
  */
 module.exports = (params) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Lookup user
diff --git a/src/server/api/endpoints/aggregation/users/following.ts b/src/server/api/endpoints/aggregation/users/following.ts
index 45e246495b..7336f392fe 100644
--- a/src/server/api/endpoints/aggregation/users/following.ts
+++ b/src/server/api/endpoints/aggregation/users/following.ts
@@ -1,19 +1,16 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import User from '../../../../../models/user';
 import FollowingLog from '../../../../../models/following-log';
 
 /**
  * Aggregate following of a user
- *
- * @param {any} params
- * @return {Promise<any>}
  */
 module.exports = (params) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Lookup user
diff --git a/src/server/api/endpoints/aggregation/users/post.ts b/src/server/api/endpoints/aggregation/users/post.ts
index e6170d83e2..c5a5e5ffca 100644
--- a/src/server/api/endpoints/aggregation/users/post.ts
+++ b/src/server/api/endpoints/aggregation/users/post.ts
@@ -1,19 +1,16 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import User from '../../../../../models/user';
 import Note from '../../../../../models/note';
 
 /**
  * Aggregate note of a user
- *
- * @param {any} params
- * @return {Promise<any>}
  */
 module.exports = (params) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Lookup user
diff --git a/src/server/api/endpoints/aggregation/users/reaction.ts b/src/server/api/endpoints/aggregation/users/reaction.ts
index 881c7ea693..f1664823cd 100644
--- a/src/server/api/endpoints/aggregation/users/reaction.ts
+++ b/src/server/api/endpoints/aggregation/users/reaction.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import User from '../../../../../models/user';
 import Reaction from '../../../../../models/note-reaction';
 
@@ -13,7 +13,7 @@ import Reaction from '../../../../../models/note-reaction';
  */
 module.exports = (params) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Lookup user
diff --git a/src/server/api/endpoints/app/create.ts b/src/server/api/endpoints/app/create.ts
index 4a55d33f2d..f403429261 100644
--- a/src/server/api/endpoints/app/create.ts
+++ b/src/server/api/endpoints/app/create.ts
@@ -79,7 +79,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 	if (descriptionErr) return rej('invalid description param');
 
 	// Get 'permission' parameter
-	const [permission, permissionErr] = $(params.permission).array('string').unique().$;
+	const [permission, permissionErr] = $(params.permission).array($().string()).unique().$;
 	if (permissionErr) return rej('invalid permission param');
 
 	// Get 'callbackUrl' parameter
diff --git a/src/server/api/endpoints/app/show.ts b/src/server/api/endpoints/app/show.ts
index 99a2093b68..92a03b9838 100644
--- a/src/server/api/endpoints/app/show.ts
+++ b/src/server/api/endpoints/app/show.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import App, { pack } from '../../../../models/app';
 
 /**
@@ -41,7 +41,7 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => {
 	const isSecure = user != null && app == null;
 
 	// Get 'appId' parameter
-	const [appId, appIdErr] = $(params.appId).optional.id().$;
+	const [appId, appIdErr] = $(params.appId).optional.type(ID).$;
 	if (appIdErr) return rej('invalid appId param');
 
 	// Get 'nameId' parameter
diff --git a/src/server/api/endpoints/auth/accept.ts b/src/server/api/endpoints/auth/accept.ts
index b6297d663d..e0073b31e6 100644
--- a/src/server/api/endpoints/auth/accept.ts
+++ b/src/server/api/endpoints/auth/accept.ts
@@ -3,7 +3,7 @@
  */
 import rndstr from 'rndstr';
 const crypto = require('crypto');
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import App from '../../../../models/app';
 import AuthSess from '../../../../models/auth-session';
 import AccessToken from '../../../../models/access-token';
diff --git a/src/server/api/endpoints/channels.ts b/src/server/api/endpoints/channels.ts
index 582e6ba43b..b68107ed7d 100644
--- a/src/server/api/endpoints/channels.ts
+++ b/src/server/api/endpoints/channels.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../cafy-id';
 import Channel, { pack } from '../../../models/channel';
 
 /**
@@ -17,11 +17,11 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
diff --git a/src/server/api/endpoints/channels/create.ts b/src/server/api/endpoints/channels/create.ts
index 0f0f558c8a..a737fcb152 100644
--- a/src/server/api/endpoints/channels/create.ts
+++ b/src/server/api/endpoints/channels/create.ts
@@ -8,10 +8,6 @@ import { pack } from '../../../../models/channel';
 
 /**
  * Create a channel
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = async (params, user) => new Promise(async (res, rej) => {
 	// Get 'title' parameter
diff --git a/src/server/api/endpoints/channels/notes.ts b/src/server/api/endpoints/channels/notes.ts
index d636aa0d10..73a69c6d2a 100644
--- a/src/server/api/endpoints/channels/notes.ts
+++ b/src/server/api/endpoints/channels/notes.ts
@@ -1,16 +1,12 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import { default as Channel, IChannel } from '../../../../models/channel';
 import Note, { pack } from '../../../../models/note';
 
 /**
  * Show a notes of a channel
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
@@ -18,11 +14,11 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
@@ -31,7 +27,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	}
 
 	// Get 'channelId' parameter
-	const [channelId, channelIdErr] = $(params.channelId).id().$;
+	const [channelId, channelIdErr] = $(params.channelId).type(ID).$;
 	if (channelIdErr) return rej('invalid channelId param');
 
 	// Fetch channel
diff --git a/src/server/api/endpoints/channels/show.ts b/src/server/api/endpoints/channels/show.ts
index 3ce9ce4745..3f468937ed 100644
--- a/src/server/api/endpoints/channels/show.ts
+++ b/src/server/api/endpoints/channels/show.ts
@@ -1,19 +1,15 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Channel, { IChannel, pack } from '../../../../models/channel';
 
 /**
  * Show a channel
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'channelId' parameter
-	const [channelId, channelIdErr] = $(params.channelId).id().$;
+	const [channelId, channelIdErr] = $(params.channelId).type(ID).$;
 	if (channelIdErr) return rej('invalid channelId param');
 
 	// Fetch channel
diff --git a/src/server/api/endpoints/channels/unwatch.ts b/src/server/api/endpoints/channels/unwatch.ts
index 8220b90b68..6ada3c9e1b 100644
--- a/src/server/api/endpoints/channels/unwatch.ts
+++ b/src/server/api/endpoints/channels/unwatch.ts
@@ -1,20 +1,16 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Channel from '../../../../models/channel';
 import Watching from '../../../../models/channel-watching';
 
 /**
  * Unwatch a channel
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'channelId' parameter
-	const [channelId, channelIdErr] = $(params.channelId).id().$;
+	const [channelId, channelIdErr] = $(params.channelId).type(ID).$;
 	if (channelIdErr) return rej('invalid channelId param');
 
 	//#region Fetch channel
diff --git a/src/server/api/endpoints/channels/watch.ts b/src/server/api/endpoints/channels/watch.ts
index 6906282a54..7880c34652 100644
--- a/src/server/api/endpoints/channels/watch.ts
+++ b/src/server/api/endpoints/channels/watch.ts
@@ -1,20 +1,16 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Channel from '../../../../models/channel';
 import Watching from '../../../../models/channel-watching';
 
 /**
  * Watch a channel
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'channelId' parameter
-	const [channelId, channelIdErr] = $(params.channelId).id().$;
+	const [channelId, channelIdErr] = $(params.channelId).type(ID).$;
 	if (channelIdErr) return rej('invalid channelId param');
 
 	//#region Fetch channel
diff --git a/src/server/api/endpoints/drive/files.ts b/src/server/api/endpoints/drive/files.ts
index 63d69d145a..7f78ef9daa 100644
--- a/src/server/api/endpoints/drive/files.ts
+++ b/src/server/api/endpoints/drive/files.ts
@@ -1,16 +1,11 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import DriveFile, { pack } from '../../../../models/drive-file';
 
 /**
  * Get drive files
- *
- * @param {any} params
- * @param {any} user
- * @param {any} app
- * @return {Promise<any>}
  */
 module.exports = async (params, user, app) => {
 	// Get 'limit' parameter
@@ -18,11 +13,11 @@ module.exports = async (params, user, app) => {
 	if (limitErr) throw 'invalid limit param';
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) throw 'invalid sinceId param';
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) throw 'invalid untilId param';
 
 	// Check if both of sinceId and untilId is specified
@@ -31,7 +26,7 @@ module.exports = async (params, user, app) => {
 	}
 
 	// Get 'folderId' parameter
-	const [folderId = null, folderIdErr] = $(params.folderId).optional.nullable.id().$;
+	const [folderId = null, folderIdErr] = $(params.folderId).optional.nullable.type(ID).$;
 	if (folderIdErr) throw 'invalid folderId param';
 
 	// Get 'type' parameter
diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts
index df0bd0a0d3..3d5048732d 100644
--- a/src/server/api/endpoints/drive/files/create.ts
+++ b/src/server/api/endpoints/drive/files/create.ts
@@ -1,17 +1,12 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import { validateFileName, pack } from '../../../../../models/drive-file';
 import create from '../../../../../services/drive/add-file';
 
 /**
  * Create a file
- *
- * @param {any} file
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = async (file, params, user): Promise<any> => {
 	if (file == null) {
@@ -34,7 +29,7 @@ module.exports = async (file, params, user): Promise<any> => {
 	}
 
 	// Get 'folderId' parameter
-	const [folderId = null, folderIdErr] = $(params.folderId).optional.nullable.id().$;
+	const [folderId = null, folderIdErr] = $(params.folderId).optional.nullable.type(ID).$;
 	if (folderIdErr) throw 'invalid folderId param';
 
 	try {
diff --git a/src/server/api/endpoints/drive/files/find.ts b/src/server/api/endpoints/drive/files/find.ts
index 0ab6e5d3e3..5d49577983 100644
--- a/src/server/api/endpoints/drive/files/find.ts
+++ b/src/server/api/endpoints/drive/files/find.ts
@@ -1,15 +1,11 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import DriveFile, { pack } from '../../../../../models/drive-file';
 
 /**
  * Find a file(s)
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'name' parameter
@@ -17,7 +13,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (nameErr) return rej('invalid name param');
 
 	// Get 'folderId' parameter
-	const [folderId = null, folderIdErr] = $(params.folderId).optional.nullable.id().$;
+	const [folderId = null, folderIdErr] = $(params.folderId).optional.nullable.type(ID).$;
 	if (folderIdErr) return rej('invalid folderId param');
 
 	// Issue query
diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts
index 3398f24541..93c3a63031 100644
--- a/src/server/api/endpoints/drive/files/show.ts
+++ b/src/server/api/endpoints/drive/files/show.ts
@@ -1,19 +1,15 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import DriveFile, { pack } from '../../../../../models/drive-file';
 
 /**
  * Show a file
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = async (params, user) => {
 	// Get 'fileId' parameter
-	const [fileId, fileIdErr] = $(params.fileId).id().$;
+	const [fileId, fileIdErr] = $(params.fileId).type(ID).$;
 	if (fileIdErr) throw 'invalid fileId param';
 
 	// Fetch file
diff --git a/src/server/api/endpoints/drive/files/update.ts b/src/server/api/endpoints/drive/files/update.ts
index c783ad8b3b..3ac157b530 100644
--- a/src/server/api/endpoints/drive/files/update.ts
+++ b/src/server/api/endpoints/drive/files/update.ts
@@ -1,21 +1,17 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import DriveFolder from '../../../../../models/drive-folder';
 import DriveFile, { validateFileName, pack } from '../../../../../models/drive-file';
 import { publishDriveStream } from '../../../../../publishers/stream';
 
 /**
  * Update a file
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'fileId' parameter
-	const [fileId, fileIdErr] = $(params.fileId).id().$;
+	const [fileId, fileIdErr] = $(params.fileId).type(ID).$;
 	if (fileIdErr) return rej('invalid fileId param');
 
 	// Fetch file
@@ -35,7 +31,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (name) file.filename = name;
 
 	// Get 'folderId' parameter
-	const [folderId, folderIdErr] = $(params.folderId).optional.nullable.id().$;
+	const [folderId, folderIdErr] = $(params.folderId).optional.nullable.type(ID).$;
 	if (folderIdErr) return rej('invalid folderId param');
 
 	if (folderId !== undefined) {
diff --git a/src/server/api/endpoints/drive/files/upload_from_url.ts b/src/server/api/endpoints/drive/files/upload_from_url.ts
index 8a426c0efc..cfae1ae192 100644
--- a/src/server/api/endpoints/drive/files/upload_from_url.ts
+++ b/src/server/api/endpoints/drive/files/upload_from_url.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import { pack } from '../../../../../models/drive-file';
 import uploadFromUrl from '../../../../../services/drive/upload-from-url';
 
@@ -15,7 +15,7 @@ module.exports = async (params, user): Promise<any> => {
 	if (urlErr) throw 'invalid url param';
 
 	// Get 'folderId' parameter
-	const [folderId = null, folderIdErr] = $(params.folderId).optional.nullable.id().$;
+	const [folderId = null, folderIdErr] = $(params.folderId).optional.nullable.type(ID).$;
 	if (folderIdErr) throw 'invalid folderId param';
 
 	return pack(await uploadFromUrl(url, user, folderId));
diff --git a/src/server/api/endpoints/drive/folders.ts b/src/server/api/endpoints/drive/folders.ts
index 489e47912e..cba33c4286 100644
--- a/src/server/api/endpoints/drive/folders.ts
+++ b/src/server/api/endpoints/drive/folders.ts
@@ -1,16 +1,11 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import DriveFolder, { pack } from '../../../../models/drive-folder';
 
 /**
  * Get drive folders
- *
- * @param {any} params
- * @param {any} user
- * @param {any} app
- * @return {Promise<any>}
  */
 module.exports = (params, user, app) => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
@@ -18,11 +13,11 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
@@ -31,7 +26,7 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => {
 	}
 
 	// Get 'folderId' parameter
-	const [folderId = null, folderIdErr] = $(params.folderId).optional.nullable.id().$;
+	const [folderId = null, folderIdErr] = $(params.folderId).optional.nullable.type(ID).$;
 	if (folderIdErr) return rej('invalid folderId param');
 
 	// Construct query
diff --git a/src/server/api/endpoints/drive/folders/create.ts b/src/server/api/endpoints/drive/folders/create.ts
index f34d0019d7..65425537a2 100644
--- a/src/server/api/endpoints/drive/folders/create.ts
+++ b/src/server/api/endpoints/drive/folders/create.ts
@@ -1,16 +1,12 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder';
 import { publishDriveStream } from '../../../../../publishers/stream';
 
 /**
  * Create drive folder
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'name' parameter
@@ -18,7 +14,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (nameErr) return rej('invalid name param');
 
 	// Get 'parentId' parameter
-	const [parentId = null, parentIdErr] = $(params.parentId).optional.nullable.id().$;
+	const [parentId = null, parentIdErr] = $(params.parentId).optional.nullable.type(ID).$;
 	if (parentIdErr) return rej('invalid parentId param');
 
 	// If the parent folder is specified
diff --git a/src/server/api/endpoints/drive/folders/find.ts b/src/server/api/endpoints/drive/folders/find.ts
index 04dc38f87f..d6277f1978 100644
--- a/src/server/api/endpoints/drive/folders/find.ts
+++ b/src/server/api/endpoints/drive/folders/find.ts
@@ -1,15 +1,11 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import DriveFolder, { pack } from '../../../../../models/drive-folder';
 
 /**
  * Find a folder(s)
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'name' parameter
@@ -17,7 +13,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (nameErr) return rej('invalid name param');
 
 	// Get 'parentId' parameter
-	const [parentId = null, parentIdErr] = $(params.parentId).optional.nullable.id().$;
+	const [parentId = null, parentIdErr] = $(params.parentId).optional.nullable.type(ID).$;
 	if (parentIdErr) return rej('invalid parentId param');
 
 	// Issue query
diff --git a/src/server/api/endpoints/drive/folders/show.ts b/src/server/api/endpoints/drive/folders/show.ts
index b432f5a50a..c703209fef 100644
--- a/src/server/api/endpoints/drive/folders/show.ts
+++ b/src/server/api/endpoints/drive/folders/show.ts
@@ -1,19 +1,15 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import DriveFolder, { pack } from '../../../../../models/drive-folder';
 
 /**
  * Show a folder
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'folderId' parameter
-	const [folderId, folderIdErr] = $(params.folderId).id().$;
+	const [folderId, folderIdErr] = $(params.folderId).type(ID).$;
 	if (folderIdErr) return rej('invalid folderId param');
 
 	// Get folder
diff --git a/src/server/api/endpoints/drive/folders/update.ts b/src/server/api/endpoints/drive/folders/update.ts
index dd7e8f5c86..d8da67fac8 100644
--- a/src/server/api/endpoints/drive/folders/update.ts
+++ b/src/server/api/endpoints/drive/folders/update.ts
@@ -1,20 +1,16 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder';
 import { publishDriveStream } from '../../../../../publishers/stream';
 
 /**
  * Update a folder
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'folderId' parameter
-	const [folderId, folderIdErr] = $(params.folderId).id().$;
+	const [folderId, folderIdErr] = $(params.folderId).type(ID).$;
 	if (folderIdErr) return rej('invalid folderId param');
 
 	// Fetch folder
@@ -34,7 +30,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (name) folder.name = name;
 
 	// Get 'parentId' parameter
-	const [parentId, parentIdErr] = $(params.parentId).optional.nullable.id().$;
+	const [parentId, parentIdErr] = $(params.parentId).optional.nullable.type(ID).$;
 	if (parentIdErr) return rej('invalid parentId param');
 	if (parentId !== undefined) {
 		if (parentId === null) {
diff --git a/src/server/api/endpoints/drive/stream.ts b/src/server/api/endpoints/drive/stream.ts
index 02313aa37b..00d89582b6 100644
--- a/src/server/api/endpoints/drive/stream.ts
+++ b/src/server/api/endpoints/drive/stream.ts
@@ -1,15 +1,11 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import DriveFile, { pack } from '../../../../models/drive-file';
 
 /**
  * Get drive stream
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
@@ -17,11 +13,11 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
diff --git a/src/server/api/endpoints/following/create.ts b/src/server/api/endpoints/following/create.ts
index 27e5eb31db..43f902852e 100644
--- a/src/server/api/endpoints/following/create.ts
+++ b/src/server/api/endpoints/following/create.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User from '../../../../models/user';
 import Following from '../../../../models/following';
 import create from '../../../../services/following/create';
@@ -13,7 +13,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	const follower = user;
 
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// 自分自身
diff --git a/src/server/api/endpoints/following/delete.ts b/src/server/api/endpoints/following/delete.ts
index ca0703ca22..99722ccf91 100644
--- a/src/server/api/endpoints/following/delete.ts
+++ b/src/server/api/endpoints/following/delete.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User from '../../../../models/user';
 import Following from '../../../../models/following';
 import deleteFollowing from '../../../../services/following/delete';
@@ -13,7 +13,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	const follower = user;
 
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Check if the followee is yourself
diff --git a/src/server/api/endpoints/following/stalk.ts b/src/server/api/endpoints/following/stalk.ts
index fc8be4924d..1dfbc4df98 100644
--- a/src/server/api/endpoints/following/stalk.ts
+++ b/src/server/api/endpoints/following/stalk.ts
@@ -1,6 +1,5 @@
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Following from '../../../../models/following';
-import { isLocalUser } from '../../../../models/user';
 
 /**
  * Stalk a user
@@ -9,7 +8,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	const follower = user;
 
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Fetch following
diff --git a/src/server/api/endpoints/following/unstalk.ts b/src/server/api/endpoints/following/unstalk.ts
index d7593bcd00..0d91ffeac8 100644
--- a/src/server/api/endpoints/following/unstalk.ts
+++ b/src/server/api/endpoints/following/unstalk.ts
@@ -1,4 +1,4 @@
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Following from '../../../../models/following';
 
 /**
@@ -8,7 +8,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	const follower = user;
 
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Fetch following
diff --git a/src/server/api/endpoints/i/authorized_apps.ts b/src/server/api/endpoints/i/authorized_apps.ts
index 82fd2d2516..fd12b3dec0 100644
--- a/src/server/api/endpoints/i/authorized_apps.ts
+++ b/src/server/api/endpoints/i/authorized_apps.ts
@@ -7,10 +7,6 @@ import { pack } from '../../../../models/app';
 
 /**
  * Get authorized apps of my account
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/i/change_password.ts b/src/server/api/endpoints/i/change_password.ts
index 57415083f1..a24e9f0be1 100644
--- a/src/server/api/endpoints/i/change_password.ts
+++ b/src/server/api/endpoints/i/change_password.ts
@@ -7,10 +7,6 @@ import User from '../../../../models/user';
 
 /**
  * Change password
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = async (params, user) => new Promise(async (res, rej) => {
 	// Get 'currentPasword' parameter
diff --git a/src/server/api/endpoints/i/favorites.ts b/src/server/api/endpoints/i/favorites.ts
index f390ef9ec7..a2c472ad17 100644
--- a/src/server/api/endpoints/i/favorites.ts
+++ b/src/server/api/endpoints/i/favorites.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Favorite, { pack } from '../../../../models/favorite';
 
 /**
@@ -13,11 +13,11 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts
index 69a8910898..14ade7b023 100644
--- a/src/server/api/endpoints/i/notifications.ts
+++ b/src/server/api/endpoints/i/notifications.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Notification from '../../../../models/notification';
 import Mute from '../../../../models/mute';
 import { pack } from '../../../../models/notification';
@@ -22,7 +22,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (markAsReadErr) return rej('invalid markAsRead param');
 
 	// Get 'type' parameter
-	const [type, typeErr] = $(params.type).optional.array('string').unique().$;
+	const [type, typeErr] = $(params.type).optional.array($().string()).unique().$;
 	if (typeErr) return rej('invalid type param');
 
 	// Get 'limit' parameter
@@ -30,11 +30,11 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
diff --git a/src/server/api/endpoints/i/pin.ts b/src/server/api/endpoints/i/pin.ts
index 909a6fdbde..761e41bbea 100644
--- a/src/server/api/endpoints/i/pin.ts
+++ b/src/server/api/endpoints/i/pin.ts
@@ -1,21 +1,17 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User from '../../../../models/user';
 import Note from '../../../../models/note';
 import { pack } from '../../../../models/user';
 
 /**
  * Pin note
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = async (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Fetch pinee
diff --git a/src/server/api/endpoints/i/regenerate_token.ts b/src/server/api/endpoints/i/regenerate_token.ts
index f9e92c1797..945ddbdee4 100644
--- a/src/server/api/endpoints/i/regenerate_token.ts
+++ b/src/server/api/endpoints/i/regenerate_token.ts
@@ -9,10 +9,6 @@ import generateUserToken from '../../common/generate-native-user-token';
 
 /**
  * Regenerate native token
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = async (params, user) => new Promise(async (res, rej) => {
 	// Get 'password' parameter
diff --git a/src/server/api/endpoints/i/signin_history.ts b/src/server/api/endpoints/i/signin_history.ts
index 931b9e2252..77beca9fd6 100644
--- a/src/server/api/endpoints/i/signin_history.ts
+++ b/src/server/api/endpoints/i/signin_history.ts
@@ -1,15 +1,11 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Signin, { pack } from '../../../../models/signin';
 
 /**
  * Get signin history of my account
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
@@ -17,11 +13,11 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts
index f3c9d777b5..7505e73387 100644
--- a/src/server/api/endpoints/i/update.ts
+++ b/src/server/api/endpoints/i/update.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User, { isValidName, isValidDescription, isValidLocation, isValidBirthday, pack } from '../../../../models/user';
 import event from '../../../../publishers/stream';
 
@@ -32,12 +32,12 @@ module.exports = async (params, user, app) => new Promise(async (res, rej) => {
 	if (birthday !== undefined) user.profile.birthday = birthday;
 
 	// Get 'avatarId' parameter
-	const [avatarId, avatarIdErr] = $(params.avatarId).optional.id().$;
+	const [avatarId, avatarIdErr] = $(params.avatarId).optional.type(ID).$;
 	if (avatarIdErr) return rej('invalid avatarId param');
 	if (avatarId) user.avatarId = avatarId;
 
 	// Get 'bannerId' parameter
-	const [bannerId, bannerIdErr] = $(params.bannerId).optional.id().$;
+	const [bannerId, bannerIdErr] = $(params.bannerId).optional.type(ID).$;
 	if (bannerIdErr) return rej('invalid bannerId param');
 	if (bannerId) user.bannerId = bannerId;
 
diff --git a/src/server/api/endpoints/i/update_client_setting.ts b/src/server/api/endpoints/i/update_client_setting.ts
index b0d5db5ec2..f753c8bcc4 100644
--- a/src/server/api/endpoints/i/update_client_setting.ts
+++ b/src/server/api/endpoints/i/update_client_setting.ts
@@ -7,10 +7,6 @@ import event from '../../../../publishers/stream';
 
 /**
  * Update myself
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = async (params, user) => new Promise(async (res, rej) => {
 	// Get 'name' parameter
diff --git a/src/server/api/endpoints/i/update_home.ts b/src/server/api/endpoints/i/update_home.ts
index ce7661ede0..4b8ba25069 100644
--- a/src/server/api/endpoints/i/update_home.ts
+++ b/src/server/api/endpoints/i/update_home.ts
@@ -8,7 +8,7 @@ import event from '../../../../publishers/stream';
 module.exports = async (params, user) => new Promise(async (res, rej) => {
 	// Get 'home' parameter
 	const [home, homeErr] = $(params.home).optional.array().each(
-		$().strict.object()
+		$().object(true)
 			.have('name', $().string())
 			.have('id', $().string())
 			.have('place', $().string())
diff --git a/src/server/api/endpoints/i/update_mobile_home.ts b/src/server/api/endpoints/i/update_mobile_home.ts
index b710e2f330..c3ecea7178 100644
--- a/src/server/api/endpoints/i/update_mobile_home.ts
+++ b/src/server/api/endpoints/i/update_mobile_home.ts
@@ -8,7 +8,7 @@ import event from '../../../../publishers/stream';
 module.exports = async (params, user) => new Promise(async (res, rej) => {
 	// Get 'home' parameter
 	const [home, homeErr] = $(params.home).optional.array().each(
-		$().strict.object()
+		$().object(true)
 			.have('name', $().string())
 			.have('id', $().string())
 			.have('data', $().object())).$;
diff --git a/src/server/api/endpoints/messaging/history.ts b/src/server/api/endpoints/messaging/history.ts
index e42d34f21a..654bf5c198 100644
--- a/src/server/api/endpoints/messaging/history.ts
+++ b/src/server/api/endpoints/messaging/history.ts
@@ -8,10 +8,6 @@ import { pack } from '../../../../models/messaging-message';
 
 /**
  * Show messaging history
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/messaging/messages.ts b/src/server/api/endpoints/messaging/messages.ts
index 092eab0562..f28699cb88 100644
--- a/src/server/api/endpoints/messaging/messages.ts
+++ b/src/server/api/endpoints/messaging/messages.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Message from '../../../../models/messaging-message';
 import User from '../../../../models/user';
 import { pack } from '../../../../models/messaging-message';
@@ -16,7 +16,7 @@ import read from '../../common/read-messaging-message';
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [recipientId, recipientIdErr] = $(params.userId).id().$;
+	const [recipientId, recipientIdErr] = $(params.userId).type(ID).$;
 	if (recipientIdErr) return rej('invalid userId param');
 
 	// Fetch recipient
@@ -41,11 +41,11 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
diff --git a/src/server/api/endpoints/messaging/messages/create.ts b/src/server/api/endpoints/messaging/messages/create.ts
index 0483b602b2..cce326be6e 100644
--- a/src/server/api/endpoints/messaging/messages/create.ts
+++ b/src/server/api/endpoints/messaging/messages/create.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import Message from '../../../../../models/messaging-message';
 import { isValidText } from '../../../../../models/messaging-message';
 import History from '../../../../../models/messaging-history';
@@ -16,14 +16,10 @@ import config from '../../../../../config';
 
 /**
  * Create a message
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [recipientId, recipientIdErr] = $(params.userId).id().$;
+	const [recipientId, recipientIdErr] = $(params.userId).type(ID).$;
 	if (recipientIdErr) return rej('invalid userId param');
 
 	// Myself
@@ -49,7 +45,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (textErr) return rej('invalid text');
 
 	// Get 'fileId' parameter
-	const [fileId, fileIdErr] = $(params.fileId).optional.id().$;
+	const [fileId, fileIdErr] = $(params.fileId).optional.type(ID).$;
 	if (fileIdErr) return rej('invalid fileId param');
 
 	let file = null;
diff --git a/src/server/api/endpoints/messaging/unread.ts b/src/server/api/endpoints/messaging/unread.ts
index 30d59dd8bd..1d83af501d 100644
--- a/src/server/api/endpoints/messaging/unread.ts
+++ b/src/server/api/endpoints/messaging/unread.ts
@@ -6,10 +6,6 @@ import Mute from '../../../../models/mute';
 
 /**
  * Get count of unread messages
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	const mute = await Mute.find({
diff --git a/src/server/api/endpoints/mute/create.ts b/src/server/api/endpoints/mute/create.ts
index 26ae612cab..0d59ecc118 100644
--- a/src/server/api/endpoints/mute/create.ts
+++ b/src/server/api/endpoints/mute/create.ts
@@ -1,22 +1,18 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User from '../../../../models/user';
 import Mute from '../../../../models/mute';
 
 /**
  * Mute a user
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	const muter = user;
 
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// 自分自身
diff --git a/src/server/api/endpoints/mute/delete.ts b/src/server/api/endpoints/mute/delete.ts
index 6f617416c8..3a37de9a21 100644
--- a/src/server/api/endpoints/mute/delete.ts
+++ b/src/server/api/endpoints/mute/delete.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User from '../../../../models/user';
 import Mute from '../../../../models/mute';
 
@@ -12,7 +12,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	const muter = user;
 
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Check if the mutee is yourself
diff --git a/src/server/api/endpoints/mute/list.ts b/src/server/api/endpoints/mute/list.ts
index 0b8262d6c5..f35bf7d168 100644
--- a/src/server/api/endpoints/mute/list.ts
+++ b/src/server/api/endpoints/mute/list.ts
@@ -1,17 +1,13 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Mute from '../../../../models/mute';
 import { pack } from '../../../../models/user';
 import { getFriendIds } from '../../common/get-friends';
 
 /**
  * Get muted users of a user
- *
- * @param {any} params
- * @param {any} me
- * @return {Promise<any>}
  */
 module.exports = (params, me) => new Promise(async (res, rej) => {
 	// Get 'iknow' parameter
@@ -23,7 +19,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'cursor' parameter
-	const [cursor = null, cursorErr] = $(params.cursor).optional.id().$;
+	const [cursor = null, cursorErr] = $(params.cursor).optional.type(ID).$;
 	if (cursorErr) return rej('invalid cursor param');
 
 	// Construct query
diff --git a/src/server/api/endpoints/my/apps.ts b/src/server/api/endpoints/my/apps.ts
index 2a3f8bcd7a..eb7ece70e9 100644
--- a/src/server/api/endpoints/my/apps.ts
+++ b/src/server/api/endpoints/my/apps.ts
@@ -6,10 +6,6 @@ import App, { pack } from '../../../../models/app';
 
 /**
  * Get my apps
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts
index a70ac0588f..bf4d5bc66f 100644
--- a/src/server/api/endpoints/notes.ts
+++ b/src/server/api/endpoints/notes.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../cafy-id';
 import Note, { pack } from '../../../models/note';
 
 /**
@@ -33,11 +33,11 @@ module.exports = (params) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
diff --git a/src/server/api/endpoints/notes/context.ts b/src/server/api/endpoints/notes/context.ts
index 2caf742d26..309fc26447 100644
--- a/src/server/api/endpoints/notes/context.ts
+++ b/src/server/api/endpoints/notes/context.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note, { pack } from '../../../../models/note';
 
 /**
@@ -13,7 +13,7 @@ import Note, { pack } from '../../../../models/note';
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts
index ea1f41aae2..1824a16c24 100644
--- a/src/server/api/endpoints/notes/create.ts
+++ b/src/server/api/endpoints/notes/create.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note, { INote, isValidText, isValidCw, pack } from '../../../../models/note';
 import { ILocalUser } from '../../../../models/user';
 import Channel, { IChannel } from '../../../../models/channel';
@@ -11,11 +11,6 @@ import { IApp } from '../../../../models/app';
 
 /**
  * Create a note
- *
- * @param {any} params
- * @param {any} user
- * @param {any} app
- * @return {Promise<any>}
  */
 module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => {
 	// Get 'visibility' parameter
@@ -35,11 +30,11 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res
 	if (viaMobileErr) return rej('invalid viaMobile');
 
 	// Get 'tags' parameter
-	const [tags = [], tagsErr] = $(params.tags).optional.array('string').unique().eachQ(t => t.range(1, 32)).$;
+	const [tags = [], tagsErr] = $(params.tags).optional.array($().string().range(1, 32)).unique().$;
 	if (tagsErr) return rej('invalid tags');
 
 	// Get 'geo' parameter
-	const [geo, geoErr] = $(params.geo).optional.nullable.strict.object()
+	const [geo, geoErr] = $(params.geo).optional.nullable.object(true)
 		.have('coordinates', $().array().length(2)
 			.item(0, $().number().range(-180, 180))
 			.item(1, $().number().range(-90, 90)))
@@ -52,7 +47,7 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res
 	if (geoErr) return rej('invalid geo');
 
 	// Get 'mediaIds' parameter
-	const [mediaIds, mediaIdsErr] = $(params.mediaIds).optional.array('id').unique().range(1, 4).$;
+	const [mediaIds, mediaIdsErr] = $(params.mediaIds).optional.array($().type(ID)).unique().range(1, 4).$;
 	if (mediaIdsErr) return rej('invalid mediaIds');
 
 	let files = [];
@@ -79,7 +74,7 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res
 	}
 
 	// Get 'renoteId' parameter
-	const [renoteId, renoteIdErr] = $(params.renoteId).optional.id().$;
+	const [renoteId, renoteIdErr] = $(params.renoteId).optional.type(ID).$;
 	if (renoteIdErr) return rej('invalid renoteId');
 
 	let renote: INote = null;
@@ -100,7 +95,7 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res
 	}
 
 	// Get 'replyId' parameter
-	const [replyId, replyIdErr] = $(params.replyId).optional.id().$;
+	const [replyId, replyIdErr] = $(params.replyId).optional.type(ID).$;
 	if (replyIdErr) return rej('invalid replyId');
 
 	let reply: INote = null;
@@ -121,7 +116,7 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res
 	}
 
 	// Get 'channelId' parameter
-	const [channelId, channelIdErr] = $(params.channelId).optional.id().$;
+	const [channelId, channelIdErr] = $(params.channelId).optional.type(ID).$;
 	if (channelIdErr) return rej('invalid channelId');
 
 	let channel: IChannel = null;
@@ -162,8 +157,8 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res
 	}
 
 	// Get 'poll' parameter
-	const [poll, pollErr] = $(params.poll).optional.strict.object()
-		.have('choices', $().array('string')
+	const [poll, pollErr] = $(params.poll).optional.object(true)
+		.have('choices', $().array($().string())
 			.unique()
 			.range(2, 10)
 			.each(c => c.length > 0 && c.length < 50))
diff --git a/src/server/api/endpoints/notes/favorites/create.ts b/src/server/api/endpoints/notes/favorites/create.ts
index c8e7f52426..e4c4adb9bb 100644
--- a/src/server/api/endpoints/notes/favorites/create.ts
+++ b/src/server/api/endpoints/notes/favorites/create.ts
@@ -1,20 +1,16 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import Favorite from '../../../../../models/favorite';
 import Note from '../../../../../models/note';
 
 /**
  * Favorite a note
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Get favoritee
diff --git a/src/server/api/endpoints/notes/favorites/delete.ts b/src/server/api/endpoints/notes/favorites/delete.ts
index 92aceb343b..3c4d9a1111 100644
--- a/src/server/api/endpoints/notes/favorites/delete.ts
+++ b/src/server/api/endpoints/notes/favorites/delete.ts
@@ -1,20 +1,16 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import Favorite from '../../../../../models/favorite';
 import Note from '../../../../../models/note';
 
 /**
  * Unfavorite a note
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Get favoritee
diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts
index 07e138ec54..e2a94d8a3e 100644
--- a/src/server/api/endpoints/notes/global-timeline.ts
+++ b/src/server/api/endpoints/notes/global-timeline.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note from '../../../../models/note';
 import Mute from '../../../../models/mute';
 import { pack } from '../../../../models/note';
@@ -15,11 +15,11 @@ module.exports = async (params, user, app) => {
 	if (limitErr) throw 'invalid limit param';
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) throw 'invalid sinceId param';
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) throw 'invalid untilId param';
 
 	// Get 'sinceDate' parameter
diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts
index d63528c3cd..dda83311ac 100644
--- a/src/server/api/endpoints/notes/local-timeline.ts
+++ b/src/server/api/endpoints/notes/local-timeline.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note from '../../../../models/note';
 import Mute from '../../../../models/mute';
 import { pack } from '../../../../models/note';
@@ -15,11 +15,11 @@ module.exports = async (params, user, app) => {
 	if (limitErr) throw 'invalid limit param';
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) throw 'invalid sinceId param';
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) throw 'invalid untilId param';
 
 	// Get 'sinceDate' parameter
diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts
index 2d95606b3f..815cf271a2 100644
--- a/src/server/api/endpoints/notes/mentions.ts
+++ b/src/server/api/endpoints/notes/mentions.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note from '../../../../models/note';
 import { getFriendIds } from '../../common/get-friends';
 import { pack } from '../../../../models/note';
@@ -24,11 +24,11 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
diff --git a/src/server/api/endpoints/notes/polls/recommendation.ts b/src/server/api/endpoints/notes/polls/recommendation.ts
index cb530ea2cf..24b0a4c803 100644
--- a/src/server/api/endpoints/notes/polls/recommendation.ts
+++ b/src/server/api/endpoints/notes/polls/recommendation.ts
@@ -7,10 +7,6 @@ import Note, { pack } from '../../../../../models/note';
 
 /**
  * Get recommended polls
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts
index 03d94da60d..2669c39085 100644
--- a/src/server/api/endpoints/notes/polls/vote.ts
+++ b/src/server/api/endpoints/notes/polls/vote.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import Vote from '../../../../../models/poll-vote';
 import Note from '../../../../../models/note';
 import Watching from '../../../../../models/note-watching';
@@ -11,14 +11,10 @@ import notify from '../../../../../publishers/notify';
 
 /**
  * Vote poll of a note
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Get votee
diff --git a/src/server/api/endpoints/notes/reactions.ts b/src/server/api/endpoints/notes/reactions.ts
index bbff97bb0a..68ffbacd46 100644
--- a/src/server/api/endpoints/notes/reactions.ts
+++ b/src/server/api/endpoints/notes/reactions.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note from '../../../../models/note';
 import Reaction, { pack } from '../../../../models/note-reaction';
 
@@ -14,7 +14,7 @@ import Reaction, { pack } from '../../../../models/note-reaction';
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/notes/reactions/create.ts b/src/server/api/endpoints/notes/reactions/create.ts
index 9e217cc3e0..1c21252604 100644
--- a/src/server/api/endpoints/notes/reactions/create.ts
+++ b/src/server/api/endpoints/notes/reactions/create.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import Note from '../../../../../models/note';
 import create from '../../../../../services/note/reaction/create';
 import { validateReaction } from '../../../../../models/note-reaction';
@@ -11,7 +11,7 @@ import { validateReaction } from '../../../../../models/note-reaction';
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Get 'reaction' parameter
diff --git a/src/server/api/endpoints/notes/reactions/delete.ts b/src/server/api/endpoints/notes/reactions/delete.ts
index b5d738b8ff..be3c1b214d 100644
--- a/src/server/api/endpoints/notes/reactions/delete.ts
+++ b/src/server/api/endpoints/notes/reactions/delete.ts
@@ -1,21 +1,16 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import Reaction from '../../../../../models/note-reaction';
 import Note from '../../../../../models/note';
-// import event from '../../../publishers/stream';
 
 /**
  * Unreact to a note
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Fetch unreactee
diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts
index 88d9ff329a..31f1bb941a 100644
--- a/src/server/api/endpoints/notes/replies.ts
+++ b/src/server/api/endpoints/notes/replies.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note, { pack } from '../../../../models/note';
 
 /**
@@ -13,7 +13,7 @@ import Note, { pack } from '../../../../models/note';
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/notes/reposts.ts b/src/server/api/endpoints/notes/reposts.ts
index 9dfc2c3cb5..fe98931380 100644
--- a/src/server/api/endpoints/notes/reposts.ts
+++ b/src/server/api/endpoints/notes/reposts.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note, { pack } from '../../../../models/note';
 
 /**
@@ -13,7 +13,7 @@ import Note, { pack } from '../../../../models/note';
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Get 'limit' parameter
@@ -21,11 +21,11 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts
index 3ff3fbbafa..021f620aa2 100644
--- a/src/server/api/endpoints/notes/search.ts
+++ b/src/server/api/endpoints/notes/search.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 const escapeRegexp = require('escape-regexp');
 import Note from '../../../../models/note';
 import User from '../../../../models/user';
@@ -22,19 +22,19 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 	if (textError) return rej('invalid text param');
 
 	// Get 'includeUserIds' parameter
-	const [includeUserIds = [], includeUserIdsErr] = $(params.includeUserIds).optional.array('id').$;
+	const [includeUserIds = [], includeUserIdsErr] = $(params.includeUserIds).optional.array($().type(ID)).$;
 	if (includeUserIdsErr) return rej('invalid includeUserIds param');
 
 	// Get 'excludeUserIds' parameter
-	const [excludeUserIds = [], excludeUserIdsErr] = $(params.excludeUserIds).optional.array('id').$;
+	const [excludeUserIds = [], excludeUserIdsErr] = $(params.excludeUserIds).optional.array($().type(ID)).$;
 	if (excludeUserIdsErr) return rej('invalid excludeUserIds param');
 
 	// Get 'includeUserUsernames' parameter
-	const [includeUserUsernames = [], includeUserUsernamesErr] = $(params.includeUserUsernames).optional.array('string').$;
+	const [includeUserUsernames = [], includeUserUsernamesErr] = $(params.includeUserUsernames).optional.array($().string()).$;
 	if (includeUserUsernamesErr) return rej('invalid includeUserUsernames param');
 
 	// Get 'excludeUserUsernames' parameter
-	const [excludeUserUsernames = [], excludeUserUsernamesErr] = $(params.excludeUserUsernames).optional.array('string').$;
+	const [excludeUserUsernames = [], excludeUserUsernamesErr] = $(params.excludeUserUsernames).optional.array($().string()).$;
 	if (excludeUserUsernamesErr) return rej('invalid excludeUserUsernames param');
 
 	// Get 'following' parameter
diff --git a/src/server/api/endpoints/notes/show.ts b/src/server/api/endpoints/notes/show.ts
index 67cdc3038b..266e0687e9 100644
--- a/src/server/api/endpoints/notes/show.ts
+++ b/src/server/api/endpoints/notes/show.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note, { pack } from '../../../../models/note';
 
 /**
@@ -13,7 +13,7 @@ import Note, { pack } from '../../../../models/note';
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'noteId' parameter
-	const [noteId, noteIdErr] = $(params.noteId).id().$;
+	const [noteId, noteIdErr] = $(params.noteId).type(ID).$;
 	if (noteIdErr) return rej('invalid noteId param');
 
 	// Get note
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index de30afea57..476d64158c 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note from '../../../../models/note';
 import Mute from '../../../../models/mute';
 import ChannelWatching from '../../../../models/channel-watching';
@@ -17,11 +17,11 @@ module.exports = async (params, user, app) => {
 	if (limitErr) throw 'invalid limit param';
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) throw 'invalid sinceId param';
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) throw 'invalid untilId param';
 
 	// Get 'sinceDate' parameter
diff --git a/src/server/api/endpoints/notes/trend.ts b/src/server/api/endpoints/notes/trend.ts
index 48ecd5b843..6c220fc922 100644
--- a/src/server/api/endpoints/notes/trend.ts
+++ b/src/server/api/endpoints/notes/trend.ts
@@ -2,7 +2,7 @@
  * Module dependencies
  */
 const ms = require('ms');
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note, { pack } from '../../../../models/note';
 
 /**
diff --git a/src/server/api/endpoints/notifications/get_unread_count.ts b/src/server/api/endpoints/notifications/get_unread_count.ts
index 283ecd63b1..600a80d194 100644
--- a/src/server/api/endpoints/notifications/get_unread_count.ts
+++ b/src/server/api/endpoints/notifications/get_unread_count.ts
@@ -6,10 +6,6 @@ import Mute from '../../../../models/mute';
 
 /**
  * Get count of unread notifications
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	const mute = await Mute.find({
diff --git a/src/server/api/endpoints/notifications/mark_as_read_all.ts b/src/server/api/endpoints/notifications/mark_as_read_all.ts
index 01c9145837..dce3cb4663 100644
--- a/src/server/api/endpoints/notifications/mark_as_read_all.ts
+++ b/src/server/api/endpoints/notifications/mark_as_read_all.ts
@@ -6,10 +6,6 @@ import event from '../../../../publishers/stream';
 
 /**
  * Mark as read all notifications
- *
- * @param {any} params
- * @param {any} user
- * @return {Promise<any>}
  */
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Update documents
diff --git a/src/server/api/endpoints/othello/games.ts b/src/server/api/endpoints/othello/games.ts
index d05c1c2585..3b23b60637 100644
--- a/src/server/api/endpoints/othello/games.ts
+++ b/src/server/api/endpoints/othello/games.ts
@@ -1,4 +1,4 @@
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import OthelloGame, { pack } from '../../../../models/othello-game';
 
 module.exports = (params, user) => new Promise(async (res, rej) => {
@@ -11,11 +11,11 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Check if both of sinceId and untilId is specified
diff --git a/src/server/api/endpoints/othello/games/show.ts b/src/server/api/endpoints/othello/games/show.ts
index dd886936d4..d76c6556a2 100644
--- a/src/server/api/endpoints/othello/games/show.ts
+++ b/src/server/api/endpoints/othello/games/show.ts
@@ -1,10 +1,10 @@
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../../cafy-id';
 import OthelloGame, { pack } from '../../../../../models/othello-game';
 import Othello from '../../../../../othello/core';
 
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'gameId' parameter
-	const [gameId, gameIdErr] = $(params.gameId).id().$;
+	const [gameId, gameIdErr] = $(params.gameId).type(ID).$;
 	if (gameIdErr) return rej('invalid gameId param');
 
 	const game = await OthelloGame.findOne({ _id: gameId });
diff --git a/src/server/api/endpoints/othello/match.ts b/src/server/api/endpoints/othello/match.ts
index d9274f8f9c..b73b64437b 100644
--- a/src/server/api/endpoints/othello/match.ts
+++ b/src/server/api/endpoints/othello/match.ts
@@ -1,4 +1,4 @@
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Matching, { pack as packMatching } from '../../../../models/othello-matching';
 import OthelloGame, { pack as packGame } from '../../../../models/othello-game';
 import User from '../../../../models/user';
@@ -7,7 +7,7 @@ import { eighteight } from '../../../../othello/maps';
 
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [childId, childIdErr] = $(params.userId).id().$;
+	const [childId, childIdErr] = $(params.userId).type(ID).$;
 	if (childIdErr) return rej('invalid userId param');
 
 	// Myself
diff --git a/src/server/api/endpoints/users.ts b/src/server/api/endpoints/users.ts
index ae33e8af0c..5b389d452f 100644
--- a/src/server/api/endpoints/users.ts
+++ b/src/server/api/endpoints/users.ts
@@ -6,10 +6,6 @@ import User, { pack } from '../../../models/user';
 
 /**
  * Lists all users
- *
- * @param {any} params
- * @param {any} me
- * @return {Promise<any>}
  */
 module.exports = (params, me) => new Promise(async (res, rej) => {
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts
index 5f03326be8..940b5ed9bc 100644
--- a/src/server/api/endpoints/users/followers.ts
+++ b/src/server/api/endpoints/users/followers.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User from '../../../../models/user';
 import Following from '../../../../models/following';
 import { pack } from '../../../../models/user';
@@ -9,14 +9,10 @@ import { getFriendIds } from '../../common/get-friends';
 
 /**
  * Get followers of a user
- *
- * @param {any} params
- * @param {any} me
- * @return {Promise<any>}
  */
 module.exports = (params, me) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Get 'iknow' parameter
@@ -28,7 +24,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'cursor' parameter
-	const [cursor = null, cursorErr] = $(params.cursor).optional.id().$;
+	const [cursor = null, cursorErr] = $(params.cursor).optional.type(ID).$;
 	if (cursorErr) return rej('invalid cursor param');
 
 	// Lookup user
diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts
index 9fb135b24d..63a73a2e27 100644
--- a/src/server/api/endpoints/users/following.ts
+++ b/src/server/api/endpoints/users/following.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User from '../../../../models/user';
 import Following from '../../../../models/following';
 import { pack } from '../../../../models/user';
@@ -16,7 +16,7 @@ import { getFriendIds } from '../../common/get-friends';
  */
 module.exports = (params, me) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Get 'iknow' parameter
@@ -28,7 +28,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'cursor' parameter
-	const [cursor = null, cursorErr] = $(params.cursor).optional.id().$;
+	const [cursor = null, cursorErr] = $(params.cursor).optional.type(ID).$;
 	if (cursorErr) return rej('invalid cursor param');
 
 	// Lookup user
diff --git a/src/server/api/endpoints/users/get_frequently_replied_users.ts b/src/server/api/endpoints/users/get_frequently_replied_users.ts
index 7a98f44e98..4c00620a52 100644
--- a/src/server/api/endpoints/users/get_frequently_replied_users.ts
+++ b/src/server/api/endpoints/users/get_frequently_replied_users.ts
@@ -1,13 +1,13 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import Note from '../../../../models/note';
 import User, { pack } from '../../../../models/user';
 
 module.exports = (params, me) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).id().$;
+	const [userId, userIdErr] = $(params.userId).type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Get 'limit' parameter
diff --git a/src/server/api/endpoints/users/list/create.ts b/src/server/api/endpoints/users/list/create.ts
new file mode 100644
index 0000000000..6ae510f52b
--- /dev/null
+++ b/src/server/api/endpoints/users/list/create.ts
@@ -0,0 +1,25 @@
+/**
+ * Module dependencies
+ */
+import $ from 'cafy';
+import UserList, { pack } from '../../../../../models/user-list';
+
+/**
+ * Create a user list
+ */
+module.exports = async (params, user) => new Promise(async (res, rej) => {
+	// Get 'title' parameter
+	const [title, titleErr] = $(params.title).string().range(1, 100).$;
+	if (titleErr) return rej('invalid title param');
+
+	// insert
+	const userList = await UserList.insert({
+		createdAt: new Date(),
+		userId: user._id,
+		title: title,
+		userIds: []
+	});
+
+	// Response
+	res(await pack(userList));
+});
diff --git a/src/server/api/endpoints/users/list/push.ts b/src/server/api/endpoints/users/list/push.ts
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts
index bd4247c79c..dafa18bcc9 100644
--- a/src/server/api/endpoints/users/notes.ts
+++ b/src/server/api/endpoints/users/notes.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import getHostLower from '../../common/get-host-lower';
 import Note, { pack } from '../../../../models/note';
 import User from '../../../../models/user';
@@ -11,7 +11,7 @@ import User from '../../../../models/user';
  */
 module.exports = (params, me) => new Promise(async (res, rej) => {
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).optional.id().$;
+	const [userId, userIdErr] = $(params.userId).optional.type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Get 'username' parameter
@@ -43,11 +43,11 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 	if (limitErr) return rej('invalid limit param');
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $(params.sinceId).optional.id().$;
+	const [sinceId, sinceIdErr] = $(params.sinceId).optional.type(ID).$;
 	if (sinceIdErr) return rej('invalid sinceId param');
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $(params.untilId).optional.id().$;
+	const [untilId, untilIdErr] = $(params.untilId).optional.type(ID).$;
 	if (untilIdErr) return rej('invalid untilId param');
 
 	// Get 'sinceDate' parameter
diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts
index f72bb04bf1..1e8ef83432 100644
--- a/src/server/api/endpoints/users/recommendation.ts
+++ b/src/server/api/endpoints/users/recommendation.ts
@@ -2,7 +2,7 @@
  * Module dependencies
  */
 const ms = require('ms');
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User, { pack } from '../../../../models/user';
 import { getFriendIds } from '../../common/get-friends';
 import Mute from '../../../../models/mute';
diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts
index da30f47c2a..faf9b901d1 100644
--- a/src/server/api/endpoints/users/search.ts
+++ b/src/server/api/endpoints/users/search.ts
@@ -2,7 +2,7 @@
  * Module dependencies
  */
 import * as mongo from 'mongodb';
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User, { pack } from '../../../../models/user';
 import config from '../../../../config';
 const escapeRegexp = require('escape-regexp');
diff --git a/src/server/api/endpoints/users/search_by_username.ts b/src/server/api/endpoints/users/search_by_username.ts
index 5f6ececff9..41a12d5332 100644
--- a/src/server/api/endpoints/users/search_by_username.ts
+++ b/src/server/api/endpoints/users/search_by_username.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User, { pack } from '../../../../models/user';
 
 /**
diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts
index 7e7f5dc488..64adb5963b 100644
--- a/src/server/api/endpoints/users/show.ts
+++ b/src/server/api/endpoints/users/show.ts
@@ -1,7 +1,7 @@
 /**
  * Module dependencies
  */
-import $ from 'cafy';
+import $ from 'cafy'; import ID from '../../../../cafy-id';
 import User, { pack } from '../../../../models/user';
 import resolveRemoteUser from '../../../../remote/resolve-user';
 
@@ -14,7 +14,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 	let user;
 
 	// Get 'userId' parameter
-	const [userId, userIdErr] = $(params.userId).optional.id().$;
+	const [userId, userIdErr] = $(params.userId).optional.type(ID).$;
 	if (userIdErr) return rej('invalid userId param');
 
 	// Get 'username' parameter