From 9d24f8aea5cb1825ac3e64cb1593b01d02e5b18c Mon Sep 17 00:00:00 2001
From: Laura Hausmann <laura@hausmann.dev>
Date: Sat, 7 Oct 2023 22:16:17 +0200
Subject: [PATCH] [mastodon-client] Remove ID conversion

BREAKING: Please log out and log back in of any clients in use, as their cache is now invalid
---
 packages/backend/src/misc/convert-id.ts       |  27 ----
 packages/backend/src/server/api/index.ts      |   4 -
 .../src/server/api/mastodon/converters.ts     | 117 ---------------
 .../server/api/mastodon/endpoints/account.ts  | 120 ++++++---------
 .../src/server/api/mastodon/endpoints/auth.ts |   3 +-
 .../src/server/api/mastodon/endpoints/list.ts |  44 ++----
 .../server/api/mastodon/endpoints/media.ts    |  17 +--
 .../src/server/api/mastodon/endpoints/misc.ts |  14 +-
 .../api/mastodon/endpoints/notifications.ts   |  19 +--
 .../server/api/mastodon/endpoints/search.ts   |  11 +-
 .../server/api/mastodon/endpoints/status.ts   | 140 ++++++------------
 .../server/api/mastodon/endpoints/timeline.ts |  45 ++----
 .../src/server/api/mastodon/helpers/misc.ts   |   4 +-
 .../src/server/api/mastodon/helpers/note.ts   |   5 +-
 .../api/mastodon/middleware/pagination.ts     |   5 +-
 15 files changed, 139 insertions(+), 436 deletions(-)
 delete mode 100644 packages/backend/src/misc/convert-id.ts
 delete mode 100644 packages/backend/src/server/api/mastodon/converters.ts

diff --git a/packages/backend/src/misc/convert-id.ts b/packages/backend/src/misc/convert-id.ts
deleted file mode 100644
index c82a9b86a..000000000
--- a/packages/backend/src/misc/convert-id.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-export enum IdType {
-    IceshrimpId,
-    MastodonId
-}
-
-const chars = '0123456789abcdefghijklmnopqrstuvwxyz';
-
-//FIXME: This implementation breaks for IceshrimpIDs with leading zeroes
-//FIXME: Make this idempotent
-export function convertId(id: string, target: IdType): string {
-    if (target == IdType.IceshrimpId) {
-        return BigInt(id).toString(36);
-    }
-    else if (target == IdType.MastodonId) {
-        let result = 0n;
-        const iter = id.toLowerCase();
-
-        for (let i = 0; i < iter.length; i++){
-            const char = iter[i];
-            if (!chars.includes(char)) throw new Error('Invalid ID');
-            result = result * 36n + BigInt(chars.indexOf(char));
-        }
-
-        return result.toString();
-    }
-		throw new Error('Unknown ID type');
-}
diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts
index 976191599..e3535ae39 100644
--- a/packages/backend/src/server/api/index.ts
+++ b/packages/backend/src/server/api/index.ts
@@ -20,10 +20,6 @@ import verifyEmail from "./private/verify-email.js";
 import discord from "./service/discord.js";
 import github from "./service/github.js";
 import twitter from "./service/twitter.js";
-import { convertId, IdType } from "@/misc/convert-id.js";
-
-// re-export native rust id conversion (function and enum)
-export { IdType, convertId };
 
 // Init app
 const app = new Koa();
diff --git a/packages/backend/src/server/api/mastodon/converters.ts b/packages/backend/src/server/api/mastodon/converters.ts
deleted file mode 100644
index 0f4e7fab1..000000000
--- a/packages/backend/src/server/api/mastodon/converters.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import { convertId, IdType } from "../index.js";
-
-// It's *very* important to put `param = structuredClone(param)` at the top of each function that doesn't simply call simpleConvertId on the param object directly.
-
-function simpleConvertId(data: any) {
-    // copy the object to bypass weird pass by reference bugs
-    data = structuredClone(data);
-    data.id = convertId(data.id, IdType.MastodonId);
-    return data;
-}
-
-export function convertAccountId(account: MastodonEntity.Account | MastodonEntity.MutedAccount) {
-    return simpleConvertId(account);
-}
-
-export function convertAnnouncementId(announcement: MastodonEntity.Announcement) {
-    return simpleConvertId(announcement);
-}
-
-export function convertAttachmentId(attachment: MastodonEntity.Attachment) {
-    return simpleConvertId(attachment);
-}
-
-export function convertListId(list: MastodonEntity.List) {
-    return simpleConvertId(list);
-}
-
-export function convertPollId(poll: MastodonEntity.Poll) {
-    return simpleConvertId(poll);
-}
-
-export function convertRelationshipId(relationship: MastodonEntity.Relationship) {
-    return simpleConvertId(relationship);
-}
-
-export function convertStatusSourceId(statusSource: MastodonEntity.StatusSource) {
-    return simpleConvertId(statusSource);
-}
-
-export function convertSuggestionIds(suggestion: MastodonEntity.SuggestedAccount) {
-    suggestion = structuredClone(suggestion);
-    suggestion.account = convertAccountId(suggestion.account)
-    return suggestion
-}
-
-export function convertNotificationIds(notification: MastodonEntity.Notification) {
-    notification = structuredClone(notification);
-    notification.account = convertAccountId(notification.account);
-    notification.id = convertId(notification.id, IdType.MastodonId);
-    if (notification.status)
-        notification.status = convertStatusIds(notification.status);
-    if (notification.reaction)
-        notification.reaction = convertReactionIds(notification.reaction);
-    return notification;
-}
-
-export function convertReactionIds(reaction: MastodonEntity.Reaction) {
-    reaction = structuredClone(reaction);
-    if (reaction.accounts) {
-        reaction.accounts = reaction.accounts.map(convertAccountId);
-    }
-    return reaction;
-}
-
-export function convertSearchIds(search: MastodonEntity.Search) {
-    search = structuredClone(search);
-    search.accounts = search.accounts.map(p => convertAccountId(p));
-    search.statuses = search.statuses.map(p => convertStatusIds(p));
-    return search;
-}
-
-export function convertStatusIds(status: MastodonEntity.Status) {
-    status = structuredClone(status);
-    status.account = convertAccountId(status.account);
-    status.id = convertId(status.id, IdType.MastodonId);
-    if (status.in_reply_to_account_id)
-        status.in_reply_to_account_id = convertId(
-            status.in_reply_to_account_id,
-            IdType.MastodonId,
-        );
-    if (status.in_reply_to_id)
-        status.in_reply_to_id = convertId(status.in_reply_to_id, IdType.MastodonId);
-    status.media_attachments = status.media_attachments.map((attachment) =>
-        convertAttachmentId(attachment),
-    );
-    status.mentions = status.mentions.map((mention) => ({
-        ...mention,
-        id: convertId(mention.id, IdType.MastodonId),
-    }));
-    if (status.poll) status.poll = convertPollId(status.poll);
-    if (status.reblog) status.reblog = convertStatusIds(status.reblog);
-    if (status.quote) status.quote = convertStatusIds(status.quote);
-    status.reactions = status.reactions.map(convertReactionIds);
-
-    return status;
-}
-
-export function convertStatusEditIds(edit: MastodonEntity.StatusEdit) {
-    edit = structuredClone(edit);
-    edit.account = convertAccountId(edit.account);
-    edit.media_attachments = edit.media_attachments.map((attachment) =>
-        convertAttachmentId(attachment),
-    );
-    if (edit.poll) edit.poll = convertPollId(edit.poll);
-    return edit;
-}
-
-export function convertConversationIds(conversation: MastodonEntity.Conversation) {
-    conversation = structuredClone(conversation);
-    conversation.id = convertId(conversation.id, IdType.MastodonId);
-    conversation.accounts = conversation.accounts.map(convertAccountId);
-    if (conversation.last_status) {
-        conversation.last_status = convertStatusIds(conversation.last_status);
-    }
-
-    return conversation;
-}
diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts
index afb54e710..dc111bfc6 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/account.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts
@@ -1,7 +1,5 @@
 import Router from "@koa/router";
-import { argsToBools, convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "./timeline.js";
-import { convertId, IdType } from "../../index.js";
-import { convertAccountId, convertListId, convertRelationshipId, convertStatusIds, } from "../converters.js";
+import { argsToBools, limitToInt, normalizeUrlQuery } from "./timeline.js";
 import { UserConverter } from "@/server/api/mastodon/converters/user.js";
 import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
 import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
@@ -12,53 +10,43 @@ export function setupEndpointsAccount(router: Router): void {
     router.get("/v1/accounts/verify_credentials",
         auth(true, ['read:accounts']),
         async (ctx) => {
-            const acct = await UserHelpers.verifyCredentials(ctx);
-            ctx.body = convertAccountId(acct);
+            ctx.body = await UserHelpers.verifyCredentials(ctx);
         }
     );
     router.patch("/v1/accounts/update_credentials",
         auth(true, ['write:accounts']),
         async (ctx) => {
-            const acct = await UserHelpers.updateCredentials(ctx);
-            ctx.body = convertAccountId(acct)
+            ctx.body = await UserHelpers.updateCredentials(ctx)
         }
     );
     router.get("/v1/accounts/lookup",
         async (ctx) => {
             const args = normalizeUrlQuery(ctx.query);
             const user = await UserHelpers.getUserFromAcct(args.acct);
-            const account = await UserConverter.encode(user, ctx);
-            ctx.body = convertAccountId(account);
+            ctx.body = await UserConverter.encode(user, ctx);
         }
     );
     router.get("/v1/accounts/relationships",
         auth(true, ['read:follows']),
         async (ctx) => {
-            const ids = (normalizeUrlQuery(ctx.query, ['id[]'])['id[]'] ?? [])
-                .map((id: string) => convertId(id, IdType.IceshrimpId));
-            const result = await UserHelpers.getUserRelationhipToMany(ids, ctx.user.id);
-            ctx.body = result.map(rel => convertRelationshipId(rel));
+            const ids = (normalizeUrlQuery(ctx.query, ['id[]'])['id[]'] ?? []);
+            ctx.body = await UserHelpers.getUserRelationhipToMany(ids, ctx.user.id);
         }
     );
     router.get<{ Params: { id: string } }>("/v1/accounts/:id",
         auth(false),
         async (ctx) => {
-            const userId = convertId(ctx.params.id, IdType.IceshrimpId);
-            const account = await UserConverter.encode(await UserHelpers.getUserOr404(userId), ctx);
-            ctx.body = convertAccountId(account);
+            ctx.body = await UserConverter.encode(await UserHelpers.getUserOr404(ctx.params.id), ctx);
         }
     );
     router.get<{ Params: { id: string } }>(
         "/v1/accounts/:id/statuses",
         auth(false, ["read:statuses"]),
         async (ctx) => {
-            const userId = convertId(ctx.params.id, IdType.IceshrimpId);
-            const query = await UserHelpers.getUserCachedOr404(userId, ctx);
-            const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query))));
+            const query = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query)));
             const res = await UserHelpers.getUserStatuses(query, args.max_id, args.since_id, args.min_id, args.limit, args['only_media'], args['exclude_replies'], args['exclude_reblogs'], args.pinned, args.tagged, ctx);
-            const tl = await NoteConverter.encodeMany(res, ctx);
-
-            ctx.body = tl.map(s => convertStatusIds(s));
+            ctx.body = await NoteConverter.encodeMany(res, ctx);
         },
     );
     router.get<{ Params: { id: string } }>(
@@ -71,72 +59,61 @@ export function setupEndpointsAccount(router: Router): void {
         "/v1/accounts/:id/followers",
         auth(false),
         async (ctx) => {
-            const userId = convertId(ctx.params.id, IdType.IceshrimpId);
-            const query = await UserHelpers.getUserCachedOr404(userId, ctx);
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
+            const query = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            const args = normalizeUrlQuery(limitToInt(ctx.query as any));
             const res = await UserHelpers.getUserFollowers(query, args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const followers = await UserConverter.encodeMany(res, ctx);
-
-            ctx.body = followers.map((account) => convertAccountId(account));
+            ctx.body = await UserConverter.encodeMany(res, ctx);
         },
     );
     router.get<{ Params: { id: string } }>(
         "/v1/accounts/:id/following",
         auth(false),
         async (ctx) => {
-            const userId = convertId(ctx.params.id, IdType.IceshrimpId);
-            const query = await UserHelpers.getUserCachedOr404(userId, ctx);
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
+            const query = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            const args = normalizeUrlQuery(limitToInt(ctx.query as any));
             const res = await UserHelpers.getUserFollowing(query, args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const following = await UserConverter.encodeMany(res, ctx);
-
-            ctx.body = following.map((account) => convertAccountId(account));
+            ctx.body = await UserConverter.encodeMany(res, ctx);
         },
     );
     router.get<{ Params: { id: string } }>(
         "/v1/accounts/:id/lists",
         auth(true, ["read:lists"]),
         async (ctx) => {
-            const member = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
-            const results = await ListHelpers.getListsByMember(member, ctx);
-            ctx.body = results.map(p => convertListId(p));
+            const member = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            ctx.body = await ListHelpers.getListsByMember(member, ctx);
         },
     );
     router.post<{ Params: { id: string } }>(
         "/v1/accounts/:id/follow",
         auth(true, ["write:follows"]),
         async (ctx) => {
-            const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
+            const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
             //FIXME: Parse form data
-            const result = await UserHelpers.followUser(target, true, false, ctx);
-            ctx.body = convertRelationshipId(result);
+            ctx.body = await UserHelpers.followUser(target, true, false, ctx);
         },
     );
     router.post<{ Params: { id: string } }>(
         "/v1/accounts/:id/unfollow",
         auth(true, ["write:follows"]),
         async (ctx) => {
-            const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
-            const result = await UserHelpers.unfollowUser(target, ctx);
-            ctx.body = convertRelationshipId(result);
+            const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            ctx.body = await UserHelpers.unfollowUser(target, ctx);
         },
     );
     router.post<{ Params: { id: string } }>(
         "/v1/accounts/:id/block",
         auth(true, ["write:blocks"]),
         async (ctx) => {
-            const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
-            const result = await UserHelpers.blockUser(target, ctx);
-            ctx.body = convertRelationshipId(result);
+            const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            ctx.body = await UserHelpers.blockUser(target, ctx);
         },
     );
     router.post<{ Params: { id: string } }>(
         "/v1/accounts/:id/unblock",
         auth(true, ["write:blocks"]),
         async (ctx) => {
-            const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
-            const result = await UserHelpers.unblockUser(target, ctx);
-            ctx.body = convertRelationshipId(result)
+            const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            ctx.body = await UserHelpers.unblockUser(target, ctx);
         },
     );
     router.post<{ Params: { id: string } }>(
@@ -145,18 +122,16 @@ export function setupEndpointsAccount(router: Router): void {
         async (ctx) => {
             //FIXME: parse form data
             const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query, ['duration']), ['notifications']));
-            const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
-            const result = await UserHelpers.muteUser(target, args.notifications, args.duration, ctx);
-            ctx.body = convertRelationshipId(result)
+            const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            ctx.body = await UserHelpers.muteUser(target, args.notifications, args.duration, ctx);
         },
     );
     router.post<{ Params: { id: string } }>(
         "/v1/accounts/:id/unmute",
         auth(true, ["write:mutes"]),
         async (ctx) => {
-            const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
-            const result = await UserHelpers.unmuteUser(target, ctx);
-            ctx.body = convertRelationshipId(result)
+            const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            ctx.body = await UserHelpers.unmuteUser(target, ctx);
         },
     );
     router.get("/v1/featured_tags",
@@ -172,63 +147,56 @@ export function setupEndpointsAccount(router: Router): void {
     router.get("/v1/bookmarks",
         auth(true, ["read:bookmarks"]),
         async (ctx) => {
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
+            const args = normalizeUrlQuery(limitToInt(ctx.query as any));
             const res = await UserHelpers.getUserBookmarks(args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const bookmarks = await NoteConverter.encodeMany(res, ctx);
-            ctx.body = bookmarks.map(s => convertStatusIds(s));
+            ctx.body = await NoteConverter.encodeMany(res, ctx);
         }
     );
     router.get("/v1/favourites",
         auth(true, ["read:favourites"]),
         async (ctx) => {
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
+            const args = normalizeUrlQuery(limitToInt(ctx.query as any));
             const res = await UserHelpers.getUserFavorites(args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const favorites = await NoteConverter.encodeMany(res, ctx);
-            ctx.body = favorites.map(s => convertStatusIds(s));
+            ctx.body = await NoteConverter.encodeMany(res, ctx);
         }
     );
     router.get("/v1/mutes",
         auth(true, ["read:mutes"]),
         async (ctx) => {
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
-            const res = await UserHelpers.getUserMutes(args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            ctx.body = res.map(m => convertAccountId(m));
+            const args = normalizeUrlQuery(limitToInt(ctx.query as any));
+            ctx.body = await UserHelpers.getUserMutes(args.max_id, args.since_id, args.min_id, args.limit, ctx);
         }
     );
     router.get("/v1/blocks",
         auth(true, ["read:blocks"]),
         async (ctx) => {
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
+            const args = normalizeUrlQuery(limitToInt(ctx.query as any));
             const res = await UserHelpers.getUserBlocks(args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const blocks = await UserConverter.encodeMany(res, ctx);
-            ctx.body = blocks.map(b => convertAccountId(b));
+            ctx.body = await UserConverter.encodeMany(res, ctx);
         }
     );
     router.get("/v1/follow_requests",
         auth(true, ["read:follows"]),
         async (ctx) => {
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
+            const args = normalizeUrlQuery(limitToInt(ctx.query as any));
             const res = await UserHelpers.getUserFollowRequests(args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const requests = await UserConverter.encodeMany(res, ctx);
-            ctx.body = requests.map(b => convertAccountId(b));
+            ctx.body = await UserConverter.encodeMany(res, ctx);
         }
     );
     router.post<{ Params: { id: string } }>(
         "/v1/follow_requests/:id/authorize",
         auth(true, ["write:follows"]),
         async (ctx) => {
-            const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
-            const result = await UserHelpers.acceptFollowRequest(target, ctx);
-            ctx.body = convertRelationshipId(result);
+            const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            ctx.body = await UserHelpers.acceptFollowRequest(target, ctx);
         },
     );
     router.post<{ Params: { id: string } }>(
         "/v1/follow_requests/:id/reject",
         auth(true, ["write:follows"]),
         async (ctx) => {
-            const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
-            const result = await UserHelpers.rejectFollowRequest(target, ctx);
-            ctx.body = convertRelationshipId(result);
+            const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
+            ctx.body = await UserHelpers.rejectFollowRequest(target, ctx);
         },
     );
 }
diff --git a/packages/backend/src/server/api/mastodon/endpoints/auth.ts b/packages/backend/src/server/api/mastodon/endpoints/auth.ts
index 98e2ad9e8..c0ab5dab5 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/auth.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/auth.ts
@@ -1,6 +1,5 @@
 import Router from "@koa/router";
 import { AuthHelpers } from "@/server/api/mastodon/helpers/auth.js";
-import { convertId, IdType } from "@/misc/convert-id.js";
 import { AuthConverter } from "@/server/api/mastodon/converters/auth.js";
 import { v4 as uuid } from "uuid";
 import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js";
@@ -15,7 +14,7 @@ export function setupEndpointsAuth(router: Router): void {
         const red = body.redirect_uris;
         const appData = await AuthHelpers.registerApp(body['client_name'], scopeArr, red, body['website']);
         ctx.body = {
-            id: convertId(appData.id, IdType.MastodonId),
+            id: appData.id,
             name: appData.name,
             website: body.website,
             redirect_uri: red,
diff --git a/packages/backend/src/server/api/mastodon/endpoints/list.ts b/packages/backend/src/server/api/mastodon/endpoints/list.ts
index 516aa822e..1d7fb15d1 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/list.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/list.ts
@@ -1,7 +1,5 @@
 import Router from "@koa/router";
-import { convertAccountId, convertListId, } from "../converters.js";
-import { convertId, IdType } from "../../index.js";
-import { convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "@/server/api/mastodon/endpoints/timeline.js";
+import { limitToInt, normalizeUrlQuery } from "@/server/api/mastodon/endpoints/timeline.js";
 import { ListHelpers } from "@/server/api/mastodon/helpers/list.js";
 import { UserConverter } from "@/server/api/mastodon/converters/user.js";
 import { UserLists } from "@/models/index.js";
@@ -14,18 +12,14 @@ export function setupEndpointsList(router: Router): void {
     router.get("/v1/lists",
         auth(true, ['read:lists']),
         async (ctx, reply) => {
-            ctx.body = await ListHelpers.getLists(ctx)
-                .then(p => p.map(list => convertListId(list)));
+            ctx.body = await ListHelpers.getLists(ctx);
         }
     );
     router.get<{ Params: { id: string } }>(
         "/v1/lists/:id",
         auth(true, ['read:lists']),
         async (ctx, reply) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-
-            ctx.body = await ListHelpers.getListOr404(id, ctx)
-                .then(p => convertListId(p));
+            ctx.body = await ListHelpers.getListOr404(ctx.params.id, ctx);
         },
     );
     router.post("/v1/lists",
@@ -33,31 +27,26 @@ export function setupEndpointsList(router: Router): void {
         async (ctx, reply) => {
             const body = ctx.request.body as any;
             const title = (body.title ?? '').trim();
-
-            ctx.body = await ListHelpers.createList(title, ctx)
-                .then(p => convertListId(p));
+            ctx.body = await ListHelpers.createList(title, ctx);
         }
     );
     router.put<{ Params: { id: string } }>(
         "/v1/lists/:id",
         auth(true, ['write:lists']),
         async (ctx, reply) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const list = await UserLists.findOneBy({ userId: ctx.user.id, id: id });
+            const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.id });
             if (!list) throw new MastoApiError(404);
 
             const body = ctx.request.body as any;
             const title = (body.title ?? '').trim();
-            ctx.body = await ListHelpers.updateList(list, title, ctx)
-                .then(p => convertListId(p));
+            ctx.body = await ListHelpers.updateList(list, title, ctx);
         },
     );
     router.delete<{ Params: { id: string } }>(
         "/v1/lists/:id",
         auth(true, ['write:lists']),
         async (ctx, reply) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const list = await UserLists.findOneBy({ userId: ctx.user.id, id: id });
+            const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.id });
             if (!list) throw new MastoApiError(404);
 
             await ListHelpers.deleteList(list, ctx);
@@ -68,26 +57,22 @@ export function setupEndpointsList(router: Router): void {
         "/v1/lists/:id/accounts",
         auth(true, ['read:lists']),
         async (ctx, reply) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
-            const res = await ListHelpers.getListUsers(id, args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const accounts = await UserConverter.encodeMany(res, ctx);
-
-            ctx.body = accounts.map(account => convertAccountId(account));
+            const args = normalizeUrlQuery(limitToInt(ctx.query));
+            const res = await ListHelpers.getListUsers(ctx.params.id, args.max_id, args.since_id, args.min_id, args.limit, ctx);
+            ctx.body = await UserConverter.encodeMany(res, ctx);
         },
     );
     router.post<{ Params: { id: string } }>(
         "/v1/lists/:id/accounts",
         auth(true, ['write:lists']),
         async (ctx, reply) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const list = await UserLists.findOneBy({ userId: ctx.user.id, id: id });
+            const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.id });
             if (!list) throw new MastoApiError(404);
 
             const body = ctx.request.body as any;
             if (!body['account_ids']) throw new MastoApiError(400, "Missing account_ids[] field");
 
-            const ids = toArray(body['account_ids']).map(p => convertId(p, IdType.IceshrimpId));
+            const ids = toArray(body['account_ids']);
             const targets = await Promise.all(ids.map(p => getUser(p)));
             await ListHelpers.addToList(list, targets, ctx);
             ctx.body = {}
@@ -97,14 +82,13 @@ export function setupEndpointsList(router: Router): void {
         "/v1/lists/:id/accounts",
         auth(true, ['write:lists']),
         async (ctx, reply) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const list = await UserLists.findOneBy({ userId: ctx.user.id, id: id });
+            const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.id });
             if (!list) throw new MastoApiError(404);
 
             const body = ctx.request.body as any;
             if (!body['account_ids']) throw new MastoApiError(400, "Missing account_ids[] field");
 
-            const ids = toArray(body['account_ids']).map(p => convertId(p, IdType.IceshrimpId));
+            const ids = toArray(body['account_ids']);
             const targets = await Promise.all(ids.map(p => getUser(p)));
             await ListHelpers.removeFromList(list, targets, ctx);
             ctx.body = {}
diff --git a/packages/backend/src/server/api/mastodon/endpoints/media.ts b/packages/backend/src/server/api/mastodon/endpoints/media.ts
index a37869788..b47da0bfb 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/media.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/media.ts
@@ -1,6 +1,4 @@
 import Router from "@koa/router";
-import { convertId, IdType } from "@/misc/convert-id.js";
-import { convertAttachmentId } from "@/server/api/mastodon/converters.js";
 import { MediaHelpers } from "@/server/api/mastodon/helpers/media.js";
 import { FileConverter } from "@/server/api/mastodon/converters/file.js";
 import { auth } from "@/server/api/mastodon/middleware/auth.js";
@@ -9,28 +7,23 @@ export function setupEndpointsMedia(router: Router): void {
     router.get<{ Params: { id: string } }>("/v1/media/:id",
         auth(true, ['write:media']),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const file = await MediaHelpers.getMediaPackedOr404(id, ctx);
-            const attachment = FileConverter.encode(file);
-            ctx.body = convertAttachmentId(attachment);
+            const file = await MediaHelpers.getMediaPackedOr404(ctx.params.id, ctx);
+            ctx.body = FileConverter.encode(file);
         }
     );
     router.put<{ Params: { id: string } }>("/v1/media/:id",
         auth(true, ['write:media']),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const file = await MediaHelpers.getMediaOr404(id, ctx);
-            const result = await MediaHelpers.updateMedia(file, ctx)
+            const file = await MediaHelpers.getMediaOr404(ctx.params.id, ctx);
+            ctx.body = await MediaHelpers.updateMedia(file, ctx)
                 .then(p => FileConverter.encode(p));
-            ctx.body = convertAttachmentId(result);
         }
     );
     router.post(["/v2/media", "/v1/media"],
         auth(true, ['write:media']),
         async (ctx) => {
-            const result = await MediaHelpers.uploadMedia(ctx)
+            ctx.body = await MediaHelpers.uploadMedia(ctx)
                 .then(p => FileConverter.encode(p));
-            ctx.body = convertAttachmentId(result);
         }
     );
 }
diff --git a/packages/backend/src/server/api/mastodon/endpoints/misc.ts b/packages/backend/src/server/api/mastodon/endpoints/misc.ts
index 0077ab2fa..d141b8f65 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/misc.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/misc.ts
@@ -2,8 +2,6 @@ import Router from "@koa/router";
 import { MiscHelpers } from "@/server/api/mastodon/helpers/misc.js";
 import { argsToBools, limitToInt } from "@/server/api/mastodon/endpoints/timeline.js";
 import { Announcements } from "@/models/index.js";
-import { convertAnnouncementId, convertStatusIds, convertSuggestionIds } from "@/server/api/mastodon/converters.js";
-import { convertId, IdType } from "@/misc/convert-id.js";
 import { auth } from "@/server/api/mastodon/middleware/auth.js";
 import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js";
 
@@ -24,8 +22,7 @@ export function setupEndpointsMisc(router: Router): void {
         auth(true),
         async (ctx) => {
             const args = argsToBools(ctx.query, ['with_dismissed']);
-            ctx.body = await MiscHelpers.getAnnouncements(args['with_dismissed'], ctx)
-                .then(p => p.map(x => convertAnnouncementId(x)));
+            ctx.body = await MiscHelpers.getAnnouncements(args['with_dismissed'], ctx);
         }
     );
 
@@ -33,8 +30,7 @@ export function setupEndpointsMisc(router: Router): void {
         "/v1/announcements/:id/dismiss",
         auth(true, ['write:accounts']),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const announcement = await Announcements.findOneBy({ id: id });
+            const announcement = await Announcements.findOneBy({ id: ctx.params.id });
             if (!announcement) throw new MastoApiError(404);
 
             await MiscHelpers.dismissAnnouncement(announcement, ctx);
@@ -54,8 +50,7 @@ export function setupEndpointsMisc(router: Router): void {
     router.get("/v1/trends/statuses",
         async (ctx) => {
             const args = limitToInt(ctx.query);
-            ctx.body = await MiscHelpers.getTrendingStatuses(args.limit, args.offset, ctx)
-                .then(p => p.map(x => convertStatusIds(x)));
+            ctx.body = await MiscHelpers.getTrendingStatuses(args.limit, args.offset, ctx);
         }
     );
 
@@ -76,8 +71,7 @@ export function setupEndpointsMisc(router: Router): void {
         auth(true, ['read']),
         async (ctx) => {
             const args = limitToInt(ctx.query);
-            ctx.body = await MiscHelpers.getFollowSuggestions(args.limit, ctx)
-                .then(p => p.map(x => convertSuggestionIds(x)));
+            ctx.body = await MiscHelpers.getFollowSuggestions(args.limit, ctx);
         }
     );
 }
diff --git a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts
index 78959236e..21aa3b95f 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts
@@ -1,7 +1,5 @@
 import Router from "@koa/router";
-import { convertId, IdType } from "../../index.js";
-import { convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "./timeline.js";
-import { convertNotificationIds } from "../converters.js";
+import { limitToInt, normalizeUrlQuery } from "./timeline.js";
 import { NotificationHelpers } from "@/server/api/mastodon/helpers/notification.js";
 import { NotificationConverter } from "@/server/api/mastodon/converters/notification.js";
 import { auth } from "@/server/api/mastodon/middleware/auth.js";
@@ -10,19 +8,17 @@ export function setupEndpointsNotifications(router: Router): void {
     router.get("/v1/notifications",
         auth(true, ['read:notifications']),
         async (ctx) => {
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)), ['types[]', 'exclude_types[]']);
+            const args = normalizeUrlQuery(limitToInt(ctx.query), ['types[]', 'exclude_types[]']);
             const res = await NotificationHelpers.getNotifications(args.max_id, args.since_id, args.min_id, args.limit, args['types[]'], args['exclude_types[]'], args.account_id, ctx);
-            const data = await NotificationConverter.encodeMany(res, ctx);
-
-            ctx.body = data.map(n => convertNotificationIds(n));
+            ctx.body = await NotificationConverter.encodeMany(res, ctx);
         }
     );
 
     router.get("/v1/notifications/:id",
         auth(true, ['read:notifications']),
         async (ctx) => {
-            const notification = await NotificationHelpers.getNotificationOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
-            ctx.body = convertNotificationIds(await NotificationConverter.encode(notification, ctx));
+            const notification = await NotificationHelpers.getNotificationOr404(ctx.params.id, ctx);
+            ctx.body = await NotificationConverter.encode(notification, ctx);
         }
     );
 
@@ -37,7 +33,7 @@ export function setupEndpointsNotifications(router: Router): void {
     router.post("/v1/notifications/:id/dismiss",
         auth(true, ['write:notifications']),
         async (ctx) => {
-            const notification = await NotificationHelpers.getNotificationOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
+            const notification = await NotificationHelpers.getNotificationOr404(ctx.params.id, ctx);
             await NotificationHelpers.dismissNotification(notification.id, ctx);
             ctx.body = {};
         }
@@ -46,8 +42,7 @@ export function setupEndpointsNotifications(router: Router): void {
     router.post("/v1/conversations/:id/read",
         auth(true, ['write:conversations']),
         async (ctx, reply) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            await NotificationHelpers.markConversationAsRead(id, ctx);
+            await NotificationHelpers.markConversationAsRead(ctx.params.id, ctx);
             ctx.body = {};
         }
     );
diff --git a/packages/backend/src/server/api/mastodon/endpoints/search.ts b/packages/backend/src/server/api/mastodon/endpoints/search.ts
index c5f3f5905..6dc080334 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/search.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/search.ts
@@ -1,6 +1,5 @@
 import Router from "@koa/router";
-import { argsToBools, convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "./timeline.js";
-import { convertSearchIds } from "../converters.js";
+import { argsToBools, limitToInt, normalizeUrlQuery } from "./timeline.js";
 import { SearchHelpers } from "@/server/api/mastodon/helpers/search.js";
 import { auth } from "@/server/api/mastodon/middleware/auth.js";
 
@@ -8,15 +7,13 @@ export function setupEndpointsSearch(router: Router): void {
     router.get(["/v1/search", "/v2/search"],
         auth(true, ['read:search']),
         async (ctx) => {
-            const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query), ['resolve', 'following', 'exclude_unreviewed'])));
-            const result = await SearchHelpers.search(args.q, args.type, args.resolve, args.following, args.account_id, args['exclude_unreviewed'], args.max_id, args.min_id, args.limit, args.offset, ctx);
-
-            ctx.body = convertSearchIds(result);
+            const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query), ['resolve', 'following', 'exclude_unreviewed']));
+            ctx.body = await SearchHelpers.search(args.q, args.type, args.resolve, args.following, args.account_id, args['exclude_unreviewed'], args.max_id, args.min_id, args.limit, args.offset, ctx);
 
             if (ctx.path === "/v1/search") {
                 ctx.body = {
                     ...ctx.body,
-                    hashtags: result.hashtags.map(p => p.name),
+                    hashtags: ctx.body.hashtags.map((p: MastodonEntity.Tag) => p.name),
                 };
             }
         }
diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts
index 0acc46e15..b0adf55b6 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/status.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts
@@ -1,15 +1,7 @@
 import Router from "@koa/router";
-import { convertId, IdType } from "../../index.js";
-import {
-    convertAccountId,
-    convertPollId,
-    convertStatusEditIds,
-    convertStatusIds,
-    convertStatusSourceId,
-} from "../converters.js";
 import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
 import { NoteHelpers } from "@/server/api/mastodon/helpers/note.js";
-import { convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "@/server/api/mastodon/endpoints/timeline.js";
+import { limitToInt, normalizeUrlQuery } from "@/server/api/mastodon/endpoints/timeline.js";
 import { UserConverter } from "@/server/api/mastodon/converters/user.js";
 import { PollHelpers } from "@/server/api/mastodon/helpers/poll.js";
 import { toArray } from "@/prelude/array.js";
@@ -32,8 +24,7 @@ export function setupEndpointsStatus(router: Router): void {
 
             let request = NoteHelpers.normalizeComposeOptions(ctx.request.body);
             ctx.body = await NoteHelpers.createNote(request, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
 
             if (key !== null) NoteHelpers.postIdempotencyCache.set(key, { status: ctx.body });
         }
@@ -41,31 +32,25 @@ export function setupEndpointsStatus(router: Router): void {
     router.put("/v1/statuses/:id",
         auth(true, ['write:statuses']),
         async (ctx) => {
-            const noteId = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(noteId, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
             let request = NoteHelpers.normalizeEditOptions(ctx.request.body);
             ctx.body = await NoteHelpers.editNote(request, note, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         }
     );
     router.get<{ Params: { id: string } }>("/v1/statuses/:id",
         auth(false, ["read:statuses"]),
         async (ctx) => {
-            const noteId = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(noteId, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
 
-            const status = await NoteConverter.encode(note, ctx);
-            ctx.body = convertStatusIds(status);
+            ctx.body = await NoteConverter.encode(note, ctx);
         }
     );
     router.delete<{ Params: { id: string } }>("/v1/statuses/:id",
         auth(true, ['write:statuses']),
         async (ctx) => {
-            const noteId = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(noteId, ctx);
-            ctx.body = await NoteHelpers.deleteNote(note, ctx)
-                .then(p => convertStatusIds(p));
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
+            ctx.body = await NoteHelpers.deleteNote(note, ctx);
         }
     );
 
@@ -74,14 +59,11 @@ export function setupEndpointsStatus(router: Router): void {
         auth(false, ["read:statuses"]),
         async (ctx) => {
             //FIXME: determine final limits within helper functions instead of here
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
             const ancestors = await NoteHelpers.getNoteAncestors(note, ctx.user ? 4096 : 60, ctx)
-                .then(n => NoteConverter.encodeMany(n, ctx))
-                .then(n => n.map(s => convertStatusIds(s)));
+                .then(n => NoteConverter.encodeMany(n, ctx));
             const descendants = await NoteHelpers.getNoteDescendants(note, ctx.user ? 4096 : 40, ctx.user ? 4096 : 20, ctx)
-                .then(n => NoteConverter.encodeMany(n, ctx))
-                .then(n => n.map(s => convertStatusIds(s)));
+                .then(n => NoteConverter.encodeMany(n, ctx));
 
             ctx.body = {
                 ancestors,
@@ -93,69 +75,57 @@ export function setupEndpointsStatus(router: Router): void {
         "/v1/statuses/:id/history",
         auth(false, ["read:statuses"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
-            const res = await NoteHelpers.getNoteEditHistory(note, ctx);
-            ctx.body = res.map(p => convertStatusEditIds(p));
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
+            ctx.body = await NoteHelpers.getNoteEditHistory(note, ctx);
         }
     );
     router.get<{ Params: { id: string } }>(
         "/v1/statuses/:id/source",
         auth(true, ["read:statuses"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
-            const src = NoteHelpers.getNoteSource(note);
-            ctx.body = convertStatusSourceId(src);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
+            ctx.body = NoteHelpers.getNoteSource(note);
         }
     );
     router.get<{ Params: { id: string } }>(
         "/v1/statuses/:id/reblogged_by",
         auth(false, ["read:statuses"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
+            const args = normalizeUrlQuery(limitToInt(ctx.query as any));
             const res = await NoteHelpers.getNoteRebloggedBy(note, args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const users = await UserConverter.encodeMany(res, ctx);
-            ctx.body = users.map(m => convertAccountId(m));
+            ctx.body = await UserConverter.encodeMany(res, ctx);
         }
     );
     router.get<{ Params: { id: string } }>(
         "/v1/statuses/:id/favourited_by",
         auth(false, ["read:statuses"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
+            const args = normalizeUrlQuery(limitToInt(ctx.query as any));
             const res = await NoteHelpers.getNoteFavoritedBy(note, args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const users = await UserConverter.encodeMany(res, ctx);
-            ctx.body = users.map(m => convertAccountId(m));
+            ctx.body = await UserConverter.encodeMany(res, ctx);
         }
     );
     router.post<{ Params: { id: string } }>(
         "/v1/statuses/:id/favourite",
         auth(true, ["write:favourites"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
             const reaction = await NoteHelpers.getDefaultReaction();
 
             ctx.body = await NoteHelpers.reactToNote(note, reaction, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         }
     );
     router.post<{ Params: { id: string } }>(
         "/v1/statuses/:id/unfavourite",
         auth(true, ["write:favourites"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
 
             ctx.body = await NoteHelpers.removeReactFromNote(note, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         },
     );
 
@@ -163,12 +133,10 @@ export function setupEndpointsStatus(router: Router): void {
         "/v1/statuses/:id/reblog",
         auth(true, ["write:statuses"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
 
             ctx.body = await NoteHelpers.reblogNote(note, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         },
     );
 
@@ -176,12 +144,10 @@ export function setupEndpointsStatus(router: Router): void {
         "/v1/statuses/:id/unreblog",
         auth(true, ["write:statuses"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
 
             ctx.body = await NoteHelpers.unreblogNote(note, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         },
     );
 
@@ -189,12 +155,10 @@ export function setupEndpointsStatus(router: Router): void {
         "/v1/statuses/:id/bookmark",
         auth(true, ["write:bookmarks"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
 
             ctx.body = await NoteHelpers.bookmarkNote(note, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         },
     );
 
@@ -202,12 +166,10 @@ export function setupEndpointsStatus(router: Router): void {
         "/v1/statuses/:id/unbookmark",
         auth(true, ["write:bookmarks"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
 
             ctx.body = await NoteHelpers.unbookmarkNote(note, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         },
     );
 
@@ -215,12 +177,10 @@ export function setupEndpointsStatus(router: Router): void {
         "/v1/statuses/:id/pin",
         auth(true, ["write:accounts"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
 
             ctx.body = await NoteHelpers.pinNote(note, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         },
     );
 
@@ -228,12 +188,10 @@ export function setupEndpointsStatus(router: Router): void {
         "/v1/statuses/:id/unpin",
         auth(true, ["write:accounts"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
 
             ctx.body = await NoteHelpers.unpinNote(note, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         },
     );
 
@@ -241,12 +199,10 @@ export function setupEndpointsStatus(router: Router): void {
         "/v1/statuses/:id/react/:name",
         auth(true, ["write:favourites"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
 
             ctx.body = await NoteHelpers.reactToNote(note, ctx.params.name, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         },
     );
 
@@ -254,35 +210,29 @@ export function setupEndpointsStatus(router: Router): void {
         "/v1/statuses/:id/unreact/:name",
         auth(true, ["write:favourites"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.name, ctx);
 
             ctx.body = await NoteHelpers.removeReactFromNote(note, ctx)
-                .then(p => NoteConverter.encode(p, ctx))
-                .then(p => convertStatusIds(p));
+                .then(p => NoteConverter.encode(p, ctx));
         },
     );
     router.get<{ Params: { id: string } }>("/v1/polls/:id",
         auth(false, ["read:statuses"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
-            const data = await PollHelpers.getPoll(note, ctx);
-            ctx.body = convertPollId(data);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.name, ctx);
+            ctx.body = await PollHelpers.getPoll(note, ctx);
         });
     router.post<{ Params: { id: string } }>(
         "/v1/polls/:id/votes",
         auth(true, ["write:statuses"]),
         async (ctx) => {
-            const id = convertId(ctx.params.id, IdType.IceshrimpId);
-            const note = await NoteHelpers.getNoteOr404(id, ctx);
+            const note = await NoteHelpers.getNoteOr404(ctx.params.name, ctx);
 
             const body: any = ctx.request.body;
             const choices = toArray(body.choices ?? []).map(p => parseInt(p));
             if (choices.length < 1) throw new MastoApiError(400, "Must vote for at least one option");
 
-            const data = await PollHelpers.voteInPoll(choices, note, ctx);
-            ctx.body = convertPollId(data);
+            ctx.body = await PollHelpers.voteInPoll(choices, note, ctx);
         },
     );
 }
diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts
index 3395bcf5a..dc93c8b58 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts
@@ -1,7 +1,5 @@
 import Router from "@koa/router";
 import { ParsedUrlQuery } from "querystring";
-import { convertConversationIds, convertStatusIds, } from "../converters.js";
-import { convertId, IdType } from "../../index.js";
 import { TimelineHelpers } from "@/server/api/mastodon/helpers/timeline.js";
 import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
 import { UserLists } from "@/models/index.js";
@@ -39,16 +37,6 @@ export function argsToBools(q: ParsedUrlQuery, additional: string[] = []) {
     return object;
 }
 
-export function convertPaginationArgsIds(q: ParsedUrlQuery) {
-    if (typeof q.min_id === "string")
-        q.min_id = convertId(q.min_id, IdType.IceshrimpId);
-    if (typeof q.max_id === "string")
-        q.max_id = convertId(q.max_id, IdType.IceshrimpId);
-    if (typeof q.since_id === "string")
-        q.since_id = convertId(q.since_id, IdType.IceshrimpId);
-    return q;
-}
-
 export function normalizeUrlQuery(q: ParsedUrlQuery, arrayKeys: string[] = []): any {
     const dict: any = {};
 
@@ -66,55 +54,44 @@ export function setupEndpointsTimeline(router: Router): void {
     router.get("/v1/timelines/public",
         auth(true, ['read:statuses']),
         async (ctx, reply) => {
-            const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query))));
+            const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query)));
             const res = await TimelineHelpers.getPublicTimeline(args.max_id, args.since_id, args.min_id, args.limit, args.only_media, args.local, args.remote, ctx);
-            const tl = await NoteConverter.encodeMany(res, ctx);
-
-            ctx.body = tl.map(s => convertStatusIds(s));
+            ctx.body = await NoteConverter.encodeMany(res, ctx);
         });
     router.get<{ Params: { hashtag: string } }>(
         "/v1/timelines/tag/:hashtag",
         auth(false, ['read:statuses']),
         async (ctx, reply) => {
             const tag = (ctx.params.hashtag ?? '').trim();
-            const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query))), ['any[]', 'all[]', 'none[]']);
+            const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query)), ['any[]', 'all[]', 'none[]']);
             const res = await TimelineHelpers.getTagTimeline(tag, args.max_id, args.since_id, args.min_id, args.limit, args['any[]'] ?? [], args['all[]'] ?? [], args['none[]'] ?? [], args.only_media, args.local, args.remote, ctx);
-            const tl = await NoteConverter.encodeMany(res, ctx);
-
-            ctx.body = tl.map(s => convertStatusIds(s));
+            ctx.body = await NoteConverter.encodeMany(res, ctx);
         },
     );
     router.get("/v1/timelines/home",
         auth(true, ['read:statuses']),
         async (ctx, reply) => {
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
+            const args = normalizeUrlQuery(limitToInt(ctx.query));
             const res = await TimelineHelpers.getHomeTimeline(args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const tl = await NoteConverter.encodeMany(res, ctx);
-
-            ctx.body = tl.map(s => convertStatusIds(s));
+            ctx.body = await NoteConverter.encodeMany(res, ctx);
         });
     router.get<{ Params: { listId: string } }>(
         "/v1/timelines/list/:listId",
         auth(true, ['read:lists']),
         async (ctx, reply) => {
-            const listId = convertId(ctx.params.listId, IdType.IceshrimpId);
-            const list = await UserLists.findOneBy({ userId: ctx.user.id, id: listId });
+            const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.listId });
             if (!list) throw new MastoApiError(404);
 
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
+            const args = normalizeUrlQuery(limitToInt(ctx.query));
             const res = await TimelineHelpers.getListTimeline(list, args.max_id, args.since_id, args.min_id, args.limit, ctx);
-            const tl = await NoteConverter.encodeMany(res, ctx);
-
-            ctx.body = tl.map(s => convertStatusIds(s));
+            ctx.body = await NoteConverter.encodeMany(res, ctx);
         },
     );
     router.get("/v1/conversations",
         auth(true, ['read:statuses']),
         async (ctx, reply) => {
-            const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
-            const res = await TimelineHelpers.getConversations(args.max_id, args.since_id, args.min_id, args.limit, ctx);
-
-            ctx.body = res.map(c => convertConversationIds(c));
+            const args = normalizeUrlQuery(limitToInt(ctx.query));
+            ctx.body = await TimelineHelpers.getConversations(args.max_id, args.since_id, args.min_id, args.limit, ctx);
         }
     );
 }
diff --git a/packages/backend/src/server/api/mastodon/helpers/misc.ts b/packages/backend/src/server/api/mastodon/helpers/misc.ts
index 39a620ce8..5ff385c8f 100644
--- a/packages/backend/src/server/api/mastodon/helpers/misc.ts
+++ b/packages/backend/src/server/api/mastodon/helpers/misc.ts
@@ -5,7 +5,6 @@ import { AnnouncementReads, Announcements, Emojis, Instances, Notes, UserProfile
 import { IsNull } from "typeorm";
 import { awaitAll } from "@/prelude/await-all.js";
 import { UserConverter } from "@/server/api/mastodon/converters/user.js";
-import { convertAccountId } from "@/server/api/mastodon/converters.js";
 import { Announcement } from "@/models/entities/announcement.js";
 import { ILocalUser, User } from "@/models/entities/user.js";
 import { AnnouncementConverter } from "@/server/api/mastodon/converters/announcement.js";
@@ -35,8 +34,7 @@ export class MiscHelpers {
             },
             order: { id: "ASC" },
         })
-            .then(p => p ? UserConverter.encode(p, ctx) : null)
-            .then(p => p ? convertAccountId(p) : null);
+            .then(p => p ? UserConverter.encode(p, ctx) : null);
         const meta = await fetchMeta(true);
 
         const res = {
diff --git a/packages/backend/src/server/api/mastodon/helpers/note.ts b/packages/backend/src/server/api/mastodon/helpers/note.ts
index a035bda35..cf33c866f 100644
--- a/packages/backend/src/server/api/mastodon/helpers/note.ts
+++ b/packages/backend/src/server/api/mastodon/helpers/note.ts
@@ -18,7 +18,6 @@ import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
 import { generatePaginationData } from "@/server/api/mastodon/middleware/pagination.js"
 import { addPinned, removePinned } from "@/services/i/pin.js";
 import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
-import { convertId, IdType } from "@/misc/convert-id.js";
 import { awaitAll } from "@/prelude/await-all.js";
 import { VisibilityConverter } from "@/server/api/mastodon/converters/visibility.js";
 import mfm from "mfm-js";
@@ -360,11 +359,10 @@ export class NoteHelpers {
         if (body.scheduled_at != null)
             result.scheduled_at = new Date(Date.parse(body.scheduled_at));
         if (body.in_reply_to_id)
-            result.in_reply_to_id = convertId(body.in_reply_to_id, IdType.IceshrimpId);
+            result.in_reply_to_id = body.in_reply_to_id;
         if (body.media_ids)
             result.media_ids = body.media_ids && body.media_ids.length > 0
                 ? toArray(body.media_ids)
-                    .map(p => convertId(p, IdType.IceshrimpId))
                 : undefined;
 
         if (body.poll) {
@@ -392,7 +390,6 @@ export class NoteHelpers {
         if (body.media_ids)
             result.media_ids = body.media_ids && body.media_ids.length > 0
                 ? toArray(body.media_ids)
-                    .map(p => convertId(p, IdType.IceshrimpId))
                 : undefined;
 
         if (body.poll) {
diff --git a/packages/backend/src/server/api/mastodon/middleware/pagination.ts b/packages/backend/src/server/api/mastodon/middleware/pagination.ts
index 988b71015..25c44476b 100644
--- a/packages/backend/src/server/api/mastodon/middleware/pagination.ts
+++ b/packages/backend/src/server/api/mastodon/middleware/pagination.ts
@@ -1,6 +1,5 @@
 import { MastoContext } from "@/server/api/mastodon/index.js";
 import config from "@/config/index.js";
-import { convertId, IdType } from "@/misc/convert-id.js";
 
 type PaginationData = {
     limit: number;
@@ -15,11 +14,11 @@ export async function PaginationMiddleware(ctx: MastoContext, next: () => Promis
     const link: string[] = [];
     const limit = ctx.pagination.limit;
     if (ctx.pagination.maxId) {
-        const l = `<${config.url}/api${ctx.path}?limit=${limit}&max_id=${convertId(ctx.pagination.maxId, IdType.MastodonId)}>; rel="next"`;
+        const l = `<${config.url}/api${ctx.path}?limit=${limit}&max_id=${ctx.pagination.maxId}>; rel="next"`;
         link.push(l);
     }
     if (ctx.pagination.minId) {
-        const l = `<${config.url}/api${ctx.path}?limit=${limit}&min_id=${convertId(ctx.pagination.maxId, IdType.MastodonId)}>; rel="prev"`;
+        const l = `<${config.url}/api${ctx.path}?limit=${limit}&min_id=${ctx.pagination.maxId}>; rel="prev"`;
         link.push(l);
     }
     if (link.length > 0) {