Repository: hikari-py/hikari
Pull request #971 (2022-01-19 22:23:58)

Add ability to send attachments in an interaction initial response
### Summary
Adds the ability to pass attachments with `create_initial_response`

### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [ ] I have run `nox` and all the pipelines have passed.
- [ ] I have made unittests according to the code I have added/modified/deleted.

### Related issues
<!--
To mention an issue use `#issue-id` and to mention a merge request use `!merge-request-id`
To close/fix an issue use `Close #issue-id` or `Fix #issue-id` (depending on the merge request)
-->


FasterSpeeding: I'ma see if i can work on the rest server equivalent logic for this today
FasterSpeeding: Btw I do have a prototype impl of sending attachments on rest but i'm going to be waiting for discord to push a change they said they'd be pushing soon:tm: so if you remove the interaction server from this pr and switch out to handling de-duplicating the logic by moving out the payload building logic to a separate function instead of making the create_message logic work for create initial response this can be merged without the server logic

(and i'll do the server logic in another pr)
FasterSpeeding: Sry for the delay
FasterSpeeding: For a bit more specific detail on how OI think this should be handled: instead of modifying `_create_message` to also handle initial response specific logic I think we would be better off taking the logic for building the message response payload and doing something like
```py
def _build_message(self, message_body: JSONObjectBuilder, ...) -> Optional[URLEncodedFormBuilder]:
```
where _build_message should cover both edit and create semantics, and if it returns a `URLEncodedFormBuilder` then the calling logic should handle inserting the `"payload_json"` field before making the request
tandemdude: Tests are still a TODO but a pre-emptive review would be welcomed @FasterSpeeding 😉 
FasterSpeeding: Meow
HyperGH: Woof

Patch:
diff --git a/changes/924.feature.md b/changes/924.feature.md
new file mode 100644
index 00000000..e6a5d266
--- /dev/null
+++ b/changes/924.feature.md
@@ -0,0 +1,1 @@
+Add new command types and application autocomplete option
diff --git a/changes/971.feature.md b/changes/971.feature.md
new file mode 100644
index 00000000..f4e3516d
--- /dev/null
+++ b/changes/971.feature.md
@@ -0,0 +1,1 @@
+Add ability to send attachments in an interaction initial response
diff --git a/hikari/api/entity_factory.py b/hikari/api/entity_factory.py
index 245dc660..87c0b18a 100644
--- a/hikari/api/entity_factory.py
+++ b/hikari/api/entity_factory.py
@@ -964,7 +964,7 @@ def deserialize_command(
         payload: data_binding.JSONObject,
         *,
         guild_id: undefined.UndefinedNoneOr[snowflakes.Snowflake] = undefined.UNDEFINED,
-    ) -> commands.Command:
+    ) -> commands.PartialCommand:
         """Parse a raw payload from Discord into a command object.
 
         Parameters
@@ -980,7 +980,7 @@ def deserialize_command(
 
         Returns
         -------
-        hikari.commands.Command
+        hikari.commands.PartialCommand
             The deserialized command object.
 
         Raises
diff --git a/hikari/api/interaction_server.py b/hikari/api/interaction_server.py
index 689311f7..2898b9c4 100644
--- a/hikari/api/interaction_server.py
+++ b/hikari/api/interaction_server.py
@@ -37,7 +37,8 @@
     _InteractionT_co = typing.TypeVar("_InteractionT_co", bound=base_interactions.PartialInteraction, covariant=True)
     _ResponseT_co = typing.TypeVar("_ResponseT_co", bound=special_endpoints.InteractionResponseBuilder, covariant=True)
     _MessageResponseBuilderT = typing.Union[
-        special_endpoints.InteractionDeferredBuilder, special_endpoints.InteractionMessageBuilder
+        special_endpoints.InteractionDeferredBuilder,
+        special_endpoints.InteractionMessageBuilder,
     ]
 
 
@@ -146,6 +147,15 @@ def get_listener(
     ) -> typing.Optional[ListenerT[component_interactions.ComponentInteraction, _MessageResponseBuilderT]]:
         ...
 
+    @typing.overload
+    @abc.abstractmethod
+    def get_listener(
+        self, interaction_type: typing.Type[command_interactions.AutocompleteInteraction], /
+    ) -> typing.Optional[
+        ListenerT[command_interactions.AutocompleteInteraction, special_endpoints.InteractionAutocompleteBuilder]
+    ]:
+        ...
+
     @typing.overload
     @abc.abstractmethod
     def get_listener(
@@ -195,6 +205,20 @@ def set_listener(
     ) -> None:
         ...
 
+    @typing.overload
+    @abc.abstractmethod
+    def set_listener(
+        self,
+        interaction_type: typing.Type[command_interactions.AutocompleteInteraction],
+        listener: typing.Optional[
+            ListenerT[command_interactions.AutocompleteInteraction, special_endpoints.InteractionAutocompleteBuilder]
+        ],
+        /,
+        *,
+        replace: bool = False,
+    ) -> None:
+        ...
+
     @abc.abstractmethod
     def set_listener(
         self,
diff --git a/hikari/api/rest.py b/hikari/api/rest.py
index bab30110..a30dd2e0 100644
--- a/hikari/api/rest.py
+++ b/hikari/api/rest.py
@@ -6637,9 +6637,12 @@ async def sync_guild_template(
         """
 
     @abc.abstractmethod
-    def command_builder(self, name: str, description: str, /) -> special_endpoints.CommandBuilder:
+    def command_builder(self, name: str, description: str) -> special_endpoints.SlashCommandBuilder:
         r"""Create a command builder for use in `RESTClient.set_application_commands`.
 
+        .. deprecated:: 2.0.0.dev106
+            Use `RESTClient.slash_command_builder` or `RESTClient.context_menu_command_builder` instead.
+
         Parameters
         ----------
         name : builtins.str
@@ -6651,7 +6654,51 @@ def command_builder(self, name: str, description: str, /) -> special_endpoints.C
 
         Returns
         -------
-        hikari.api.special_endpoints.CommandBuilder
+        hikari.api.special_endpoints.SlashCommandBuilder
+            The created command builder object.
+        """
+
+    @abc.abstractmethod
+    def slash_command_builder(
+        self,
+        name: str,
+        description: str,
+    ) -> special_endpoints.SlashCommandBuilder:
+        r"""Create a command builder for use in `RESTClient.set_application_commands`.
+
+        Parameters
+        ----------
+        name : builtins.str
+            The command's name. This should match the regex `^[\w-]{1,32}$` in
+            Unicode mode and be lowercase.
+        description : builtins.str
+            The description to set for the command if this is a slash command.
+            This should be inclusively between 1-100 characters in length.
+
+        Returns
+        -------
+        hikari.api.special_endpoints.SlashCommandBuilder
+            The created command builder object.
+        """
+
+    @abc.abstractmethod
+    def context_menu_command_builder(
+        self,
+        type: typing.Union[commands.CommandType, int],
+        name: str,
+    ) -> special_endpoints.ContextMenuCommandBuilder:
+        r"""Create a command builder for use in `RESTClient.set_application_commands`.
+
+        Parameters
+        ----------
+        type : commands.CommandType
+            The commands's type.
+        name : builtins.str
+            The command's name.
+
+        Returns
+        -------
+        hikari.api.special_endpoints.ContextMenuCommandBuilder
             The created command builder object.
         """
 
@@ -6659,16 +6706,16 @@ def command_builder(self, name: str, description: str, /) -> special_endpoints.C
     async def fetch_application_command(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
-        command: snowflakes.SnowflakeishOr[commands.Command],
+        command: snowflakes.SnowflakeishOr[commands.PartialCommand],
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
-    ) -> commands.Command:
+    ) -> commands.PartialCommand:
         """Fetch a command set for an application.
 
         Parameters
         ----------
         application: hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialApplication]
             Object or ID of the application to fetch a command for.
-        command: hikari.snowflakes.SnowflakeishOr[hikari.commands.Command]
+        command: hikari.snowflakes.SnowflakeishOr[hikari.commands.PartialCommand]
             Object or ID of the command to fetch.
 
         Other Parameters
@@ -6680,7 +6727,7 @@ async def fetch_application_command(
 
         Returns
         -------
-        hikari.commands.Command
+        hikari.commands.PartialCommand
             Object of the fetched command.
 
         Raises
@@ -6711,7 +6758,7 @@ async def fetch_application_commands(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
-    ) -> typing.Sequence[commands.Command]:
+    ) -> typing.Sequence[commands.PartialCommand]:
         """Fetch the commands set for an application.
 
         Parameters
@@ -6729,7 +6776,7 @@ async def fetch_application_commands(
 
         Returns
         -------
-        typing.Sequence[hikari.commands.Command]
+        typing.Sequence[hikari.commands.PartialCommand]
             A sequence of the commands declared for the provided application.
             This will exclusively either contain the commands set for a specific
             guild if `guild` is provided or the global commands if not.
@@ -6767,7 +6814,78 @@ async def create_application_command(
         *,
         options: undefined.UndefinedOr[typing.Sequence[commands.CommandOption]] = undefined.UNDEFINED,
         default_permission: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
-    ) -> commands.Command:
+    ) -> commands.SlashCommand:
+        r"""Create an application command.
+
+        .. deprecated:: 2.0.0.dev106
+            Use `RESTClient.create_slash_command` or `RESTClient.create_context_menu_command` instead.
+
+        Parameters
+        ----------
+        application: hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialApplication]
+            Object or ID of the application to create a command for.
+        name : builtins.str
+            The command's name. This should match the regex `^[\w-]{1,32}$` in
+            Unicode mode and be lowercase.
+        description : builtins.str
+            The description to set for the command.
+            This should be inclusively between 1-100 characters in length.
+        guild : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialGuild]
+            Object or ID of the specific guild this should be made for.
+            If left as `hikari.undefined.UNDEFINED` then this call will create
+            a global command rather than a guild specific one.
+
+        Other Parameters
+        ----------------
+        options : hikari.undefined.UndefinedOr[typing.Sequence[hikari.commands.CommandOption]]
+            A sequence of up to 10 options for this command.
+        default_permission : hikari.undefined.UndefinedOr[builtins.bool]
+            Whether this command should be enabled by default (without any
+            permissions) when added to a guild.
+
+            Defaults to `builtins.True`.
+
+        Returns
+        -------
+        hikari.commands.SlashCommand
+            Object of the created command.
+
+        Raises
+        ------
+        hikari.errors.ForbiddenError
+            If you cannot access the provided application's commands.
+        hikari.errors.NotFoundError
+            If the provided application isn't found.
+        hikari.errors.BadRequestError
+            If any of the fields that are passed have an invalid value.
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """
+
+    @abc.abstractmethod
+    async def create_slash_command(
+        self,
+        application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
+        name: str,
+        description: str,
+        *,
+        guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
+        options: undefined.UndefinedOr[typing.Sequence[commands.CommandOption]] = undefined.UNDEFINED,
+        default_permission: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+    ) -> commands.SlashCommand:
         r"""Create an application command.
 
         Parameters
@@ -6797,7 +6915,69 @@ async def create_application_command(
 
         Returns
         -------
-        hikari.commands.Command
+        hikari.commands.SlashCommand
+            Object of the created command.
+
+        Raises
+        ------
+        hikari.errors.ForbiddenError
+            If you cannot access the provided application's commands.
+        hikari.errors.NotFoundError
+            If the provided application isn't found.
+        hikari.errors.BadRequestError
+            If any of the fields that are passed have an invalid value.
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """
+
+    @abc.abstractmethod
+    async def create_context_menu_command(
+        self,
+        application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
+        type: typing.Literal[commands.CommandType.USER, commands.CommandType.MESSAGE, 2, 3],
+        name: str,
+        *,
+        guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
+        default_permission: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+    ) -> commands.ContextMenuCommand:
+        r"""Create an application command.
+
+        Parameters
+        ----------
+        application: hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialApplication]
+            Object or ID of the application to create a command for.
+        name : builtins.str
+            The command's name. This should match the regex `^[\w-]{1,32}$` in
+            Unicode mode and be lowercase.
+
+        Other Parameters
+        ----------------
+        guild : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialGuild]
+            Object or ID of the specific guild this should be made for.
+            If left as `hikari.undefined.UNDEFINED` then this call will create
+            a global command rather than a guild specific one.
+        default_permission : hikari.undefined.UndefinedOr[builtins.bool]
+            Whether this command should be enabled by default (without any
+            permissions) when added to a guild.
+
+            Defaults to `builtins.True`.
+
+        Returns
+        -------
+        hikari.commands.ContextMenuCommand
             Object of the created command.
 
         Raises
@@ -6831,7 +7011,7 @@ async def set_application_commands(
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
         commands: typing.Sequence[special_endpoints.CommandBuilder],
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
-    ) -> typing.Sequence[commands.Command]:
+    ) -> typing.Sequence[commands.PartialCommand]:
         """Set the commands for an application.
 
         !!! warning
@@ -6855,7 +7035,7 @@ async def set_application_commands(
 
         Returns
         -------
-        typing.Sequence[hikari.commands.Command]
+        typing.Sequence[hikari.commands.PartialCommand]
             A sequence of the set command objects.
 
         Raises
@@ -6887,20 +7067,20 @@ async def set_application_commands(
     async def edit_application_command(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
-        command: snowflakes.SnowflakeishOr[commands.Command],
+        command: snowflakes.SnowflakeishOr[commands.PartialCommand],
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
         *,
         name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         description: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         options: undefined.UndefinedOr[typing.Sequence[commands.CommandOption]] = undefined.UNDEFINED,
-    ) -> commands.Command:
+    ) -> commands.PartialCommand:
         """Edit a registered application command.
 
         Parameters
         ----------
         application: hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialApplication]
             Object or ID of the application to edit a command for.
-        command : hikari.snowflakes.SnowflakeishOr[hikari.commands.Command]
+        command : hikari.snowflakes.SnowflakeishOr[hikari.commands.PartialCommand]
             Object or ID of the command to modify.
 
         Other Parameters
@@ -6921,7 +7101,7 @@ async def edit_application_command(
 
         Returns
         -------
-        hikari.commands.Command
+        hikari.commands.PartialCommand
             The edited command object.
 
         Raises
@@ -6953,7 +7133,7 @@ async def edit_application_command(
     async def delete_application_command(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
-        command: snowflakes.SnowflakeishOr[commands.Command],
+        command: snowflakes.SnowflakeishOr[commands.PartialCommand],
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
     ) -> None:
         """Delete a registered application command.
@@ -6962,7 +7142,7 @@ async def delete_application_command(
         ----------
         application: hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialApplication]
             Object or ID of the application to delete a command for.
-        command : hikari.snowflakes.SnowflakeishOr[hikari.commands.Command]
+        command : hikari.snowflakes.SnowflakeishOr[hikari.commands.PartialCommand]
             Object or ID of the command to delete.
 
         Other Parameters
@@ -7043,7 +7223,7 @@ async def fetch_application_command_permissions(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
         guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
-        command: snowflakes.SnowflakeishOr[commands.Command],
+        command: snowflakes.SnowflakeishOr[commands.PartialCommand],
     ) -> commands.GuildCommandPermissions:
         """Fetch the permissions registered for a specific command in a guild.
 
@@ -7053,7 +7233,7 @@ async def fetch_application_command_permissions(
             Object or ID of the application to fetch the command permissions for.
         guild : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialGuild]]
             Object or ID of the guild to fetch the command permissions for.
-        command: hikari.snowflakes.SnowflakeishOr[hikari.commands.Command]
+        command: hikari.snowflakes.SnowflakeishOr[hikari.commands.PartialCommand]
             Objecr or ID of the command to fetch the command permissions for.
 
         Returns
@@ -7090,7 +7270,7 @@ async def set_application_guild_commands_permissions(
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
         guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
         permissions: typing.Mapping[
-            snowflakes.SnowflakeishOr[commands.Command], typing.Sequence[commands.CommandPermission]
+            snowflakes.SnowflakeishOr[commands.PartialCommand], typing.Sequence[commands.CommandPermission]
         ],
     ) -> typing.Sequence[commands.GuildCommandPermissions]:
         """Set permissions in a guild for multiple commands.
@@ -7105,7 +7285,7 @@ async def set_application_guild_commands_permissions(
             Object or ID of the application to set the command permissions for.
         guild : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialGuild]]
             Object or ID of the guild to set the command permissions for.
-        permissions : typing.Mapping[hikari.snowflakes.SnowflakeishOr[hikari.commands.Command], typing.Sequence[hikari.commands.CommandPermission]]
+        permissions : typing.Mapping[hikari.snowflakes.SnowflakeishOr[hikari.commands.PartialCommand], typing.Sequence[hikari.commands.CommandPermission]]
             Mapping of objects and/or IDs of commands to sequences of the commands
             to set for the specified guild.
 
@@ -7145,7 +7325,7 @@ async def set_application_command_permissions(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
         guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
-        command: snowflakes.SnowflakeishOr[commands.Command],
+        command: snowflakes.SnowflakeishOr[commands.PartialCommand],
         permissions: typing.Sequence[commands.CommandPermission],
     ) -> commands.GuildCommandPermissions:
         """Set permissions for a specific command.
@@ -7159,7 +7339,7 @@ async def set_application_command_permissions(
             Object or ID of the application to set the command permissions for.
         guild : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialGuild]]
             Object or ID of the guild to set the command permissions for.
-        command : hikari.snowflakes.SnowflakeishOr[hikari.commands.Command]
+        command : hikari.snowflakes.SnowflakeishOr[hikari.commands.PartialCommand]
             Object or ID of the command to set the permissions for.
         permissions : typing.Sequence[hikari.commands.CommandPermission]
             Sequence of up to 10 of the permission objects to set.
@@ -7209,6 +7389,18 @@ def interaction_deferred_builder(
             The deferred message interaction response builder object.
         """
 
+    @abc.abstractmethod
+    def interaction_autocomplete_builder(
+        self, choices: typing.Sequence[commands.CommandChoice]
+    ) -> special_endpoints.InteractionAutocompleteBuilder:
+        """Create a builder for an autocomplete interaction response.
+
+        Returns
+        -------
+        hikari.api.special_endpoints.InteractionAutocompleteBuilder
+            The autocomplete interaction response builder object.
+        """
+
     @abc.abstractmethod
     def interaction_message_builder(
         self, type: typing.Union[base_interactions.ResponseType, int], /
@@ -7277,6 +7469,8 @@ async def create_interaction_response(
         *,
         flags: typing.Union[int, messages_.MessageFlag, undefined.UndefinedType] = undefined.UNDEFINED,
         tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+        attachment: undefined.UndefinedOr[files.Resourceish] = undefined.UNDEFINED,
+        attachments: undefined.UndefinedOr[typing.Sequence[files.Resourceish]] = undefined.UNDEFINED,
         component: undefined.UndefinedOr[special_endpoints.ComponentBuilder] = undefined.UNDEFINED,
         components: undefined.UndefinedOr[typing.Sequence[special_endpoints.ComponentBuilder]] = undefined.UNDEFINED,
         embed: undefined.UndefinedOr[embeds_.Embed] = undefined.UNDEFINED,
@@ -7318,6 +7512,12 @@ async def create_interaction_response(
             no `embeds` kwarg is provided, then this will instead
             update the embed. This allows for simpler syntax when
             sending an embed alone.
+        attachment : hikari.undefined.UndefinedOr[hikari.files.Resourceish],
+            If provided, the message attachment. This can be a resource,
+            or string of a path on your computer or a URL.
+        attachments : hikari.undefined.UndefinedOr[typing.Sequence[hikari.files.Resourceish]],
+            If provided, the message attachments. These can be resources, or
+            strings consisting of paths on your computer or URLs.
         component : hikari.undefined.UndefinedOr[hikari.api.special_endpoints.ComponentBuilder]
             If provided, builder object of the component to include in this message.
         components : hikari.undefined.UndefinedOr[typing.Sequence[hikari.api.special_endpoints.ComponentBuilder]]
@@ -7579,6 +7779,49 @@ async def delete_interaction_response(
             If an internal error occurs on Discord while handling the request.
         """
 
+    @abc.abstractmethod
+    async def create_autocomplete_response(
+        self,
+        interaction: snowflakes.SnowflakeishOr[base_interactions.PartialInteraction],
+        token: str,
+        choices: typing.Sequence[commands.CommandChoice],
+    ) -> None:
+        """Create the initial response for an autocomplete interaction.
+
+        Parameters
+        ----------
+        interaction : hikari.snowflakes.SnowflakeishOr[hikari.interactions.base_interactions.PartialInteraction]
+            Object or ID of the interaction this response is for.
+        token : builtins.str
+            The command interaction's token.
+
+        Other Parameters
+        ----------------
+        choices : typing.Sequence[commands.CommandChoice]
+            The autocomplete choices themselves.
+
+        Raises
+        ------
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.NotFoundError
+            If the interaction is not found or if the interaction's initial
+            response has already been created.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """  # noqa: E501 - Line too long
+
     @abc.abstractmethod
     def build_action_row(self) -> special_endpoints.ActionRowBuilder:
         """Build an action row message component for use in message create and REST calls.
diff --git a/hikari/api/special_endpoints.py b/hikari/api/special_endpoints.py
index 694f924c..9c48c321 100644
--- a/hikari/api/special_endpoints.py
+++ b/hikari/api/special_endpoints.py
@@ -27,9 +27,12 @@
     "ActionRowBuilder",
     "ButtonBuilder",
     "CommandBuilder",
+    "SlashCommandBuilder",
+    "ContextMenuCommandBuilder",
     "ComponentBuilder",
     "TypingIndicator",
     "GuildBuilder",
+    "InteractionAutocompleteBuilder",
     "InteractionDeferredBuilder",
     "InteractionResponseBuilder",
     "InteractionMessageBuilder",
@@ -627,6 +630,27 @@ def set_flags(self: _T, flags: typing.Union[undefined.UndefinedType, int, messag
         """
 
 
+class InteractionAutocompleteBuilder(InteractionResponseBuilder, abc.ABC):
+    """Interface of an autocomplete interaction response builder."""
+
+    __slots__: typing.Sequence[str] = ()
+
+    @property
+    @abc.abstractmethod
+    def choices(self) -> typing.Sequence[commands.CommandChoice]:
+        """Return autocomplete choices."""
+
+    @abc.abstractmethod
+    def set_choices(self: _T, choices: typing.Sequence[commands.CommandChoice], /) -> _T:
+        """Set autocomplete choices.
+
+        Returns
+        -------
+        InteractionAutocompleteBuilder
+            Object of this builder.
+        """
+
+
 class InteractionMessageBuilder(InteractionResponseBuilder, abc.ABC):
     """Interface of an interaction message response builder used within REST servers.
 
@@ -912,27 +936,13 @@ def name(self) -> str:
 
     @property
     @abc.abstractmethod
-    def description(self) -> str:
-        """Return the description to set for this command.
-
-        !!! warning
-            This should be inclusively between 1-100 characters in length.
-
-        Returns
-        -------
-        builtins.str
-            The description to set for this command.
-        """
-
-    @property
-    @abc.abstractmethod
-    def options(self) -> typing.Sequence[commands.CommandOption]:
-        """Sequence of up to 25 of the options set for this command.
+    def type(self) -> undefined.UndefinedOr[commands.CommandType]:
+        """Return the type of this command.
 
         Returns
         -------
-        typing.Sequence[hikari.commands.CommandOption]
-            A sequence of up to 25 of the options set for this command.
+        hikari.commands.CommandType
+            The type of this command.
         """
 
     @property
@@ -989,6 +999,50 @@ def set_default_permission(self: _T, state: undefined.UndefinedOr[bool], /) -> _
             Object of this command builder for chained calls.
         """
 
+    @abc.abstractmethod
+    def build(self, entity_factory: entity_factory_.EntityFactory, /) -> data_binding.JSONObject:
+        """Build a JSON object from this builder.
+
+        Parameters
+        ----------
+        entity_factory : hikari.api.entity_factory.EntityFactory
+            The entity factory to use to serialize entities within this builder.
+
+        Returns
+        -------
+        hikari.internal.data_binding.JSONObject
+            The built json object representation of this builder.
+        """
+
+
+class SlashCommandBuilder(CommandBuilder):
+    """SlashCommandBuilder."""
+
+    @property
+    @abc.abstractmethod
+    def description(self) -> str:
+        """Return the description to set for this command.
+
+        !!! warning
+            This should be inclusively between 1-100 characters in length.
+
+        Returns
+        -------
+        builtins.str
+            The description to set for this command.
+        """
+
+    @property
+    @abc.abstractmethod
+    def options(self) -> typing.Sequence[commands.CommandOption]:
+        """Sequence of up to 25 of the options set for this command.
+
+        Returns
+        -------
+        typing.Sequence[hikari.commands.CommandOption]
+            A sequence of up to 25 of the options set for this command.
+        """
+
     @abc.abstractmethod
     def add_option(self: _T, option: commands.CommandOption) -> _T:
         """Add an option to this command.
@@ -1007,20 +1061,9 @@ def add_option(self: _T, option: commands.CommandOption) -> _T:
             Object of this command builder.
         """
 
-    @abc.abstractmethod
-    def build(self, entity_factory: entity_factory_.EntityFactory, /) -> data_binding.JSONObject:
-        """Build a JSON object from this builder.
 
-        Parameters
-        ----------
-        entity_factory : hikari.api.entity_factory.EntityFactory
-            The entity factory to use to serialize entities within this builder.
-
-        Returns
-        -------
-        hikari.internal.data_binding.JSONObject
-            The built json object representation of this builder.
-        """
+class ContextMenuCommandBuilder(CommandBuilder):
+    """ContextMenuCommandBuilder."""
 
 
 class ComponentBuilder(abc.ABC):
diff --git a/hikari/commands.py b/hikari/commands.py
index ea70a9eb..55a7ba31 100644
--- a/hikari/commands.py
+++ b/hikari/commands.py
@@ -24,11 +24,14 @@
 from __future__ import annotations
 
 __all__: typing.List[str] = [
-    "Command",
+    "PartialCommand",
+    "ContextMenuCommand",
+    "SlashCommand",
     "CommandChoice",
     "CommandOption",
     "CommandPermission",
     "CommandPermissionType",
+    "CommandType",
     "GuildCommandPermissions",
     "OptionType",
 ]
@@ -48,6 +51,19 @@
     from hikari import guilds
 
 
+class CommandType(int, enums.Enum):
+    """The type of a command."""
+
+    SLASH = 1
+    """A text-based command."""
+
+    USER = 2
+    """A user-based command."""
+
+    MESSAGE = 3
+    """A message-based command."""
+
+
 @typing.final
 class OptionType(int, enums.Enum):
     """The type of a command option."""
@@ -124,8 +140,8 @@ class CommandOption:
         This will be inclusively between 1-100 characters in length.
     """
 
-    is_required: bool = attr.field(repr=False)
-    """Whether this command """
+    is_required: bool = attr.field(default=False, repr=False)
+    """Whether this command option is required."""
 
     choices: typing.Optional[typing.Sequence[CommandChoice]] = attr.field(default=None, repr=False)
     """A sequence of up to (and including) 25 choices for this command.
@@ -146,6 +162,9 @@ class CommandOption:
     If `builtins.None`, then all channel types will be accepted.
     """
 
+    autocomplete: bool = attr.field(default=False, repr=False)
+    """Whether this option has autocomplete."""
+
     min_value: typing.Union[int, float, None] = attr.field(default=None, repr=False)
     """The minimum value permitted (inclusive).
 
@@ -163,8 +182,8 @@ class CommandOption:
 
 @attr_extensions.with_copy
 @attr.define(hash=True, kw_only=True, weakref_slot=False)
-class Command(snowflakes.Unique):
-    """Represents an application command on Discord."""
+class PartialCommand(snowflakes.Unique):
+    """Represents any application command on Discord."""
 
     app: traits.RESTAware = attr.field(eq=False, hash=False, repr=False)
     """The client application that models may use for procedures."""
@@ -172,6 +191,9 @@ class Command(snowflakes.Unique):
     id: snowflakes.Snowflake = attr.field(hash=True, repr=True)
     # <<inherited docstring from Unique>>.
 
+    type: CommandType = attr.field(hash=True, repr=True)
+    """The type of a command."""
+
     application_id: snowflakes.Snowflake = attr.field(eq=False, hash=False, repr=True)
     """ID of the application this command belongs to."""
 
@@ -183,16 +205,6 @@ class Command(snowflakes.Unique):
         lowercase.
     """
 
-    description: str = attr.field(eq=False, hash=False, repr=False)
-    """The command's description.
-
-    !!! note
-        This will be inclusively between 1-100 characters in length.
-    """
-
-    options: typing.Optional[typing.Sequence[CommandOption]] = attr.field(eq=False, hash=False, repr=False)
-    """Sequence of up to (and including) 25 of the options for this command."""
-
     default_permission: bool = attr.field(eq=False, hash=False, repr=True)
     """Whether the command is enabled by default when added to a guild.
 
@@ -209,12 +221,12 @@ class Command(snowflakes.Unique):
     version: snowflakes.Snowflake = attr.field(eq=False, hash=False, repr=True)
     """Auto-incrementing version identifier updated during substantial record changes."""
 
-    async def fetch_self(self) -> Command:
+    async def fetch_self(self) -> PartialCommand:
         """Fetch an up-to-date version of this command object.
 
         Returns
         -------
-        Command
+        PartialCommand
             Object of the fetched command.
 
         Raises
@@ -239,9 +251,10 @@ async def fetch_self(self) -> Command:
         hikari.errors.InternalServerError
             If an internal error occurs on Discord while handling the request.
         """
-        return await self.app.rest.fetch_application_command(
+        command = await self.app.rest.fetch_application_command(
             self.application_id, self.id, undefined.UNDEFINED if self.guild_id is None else self.guild_id
         )
+        return command
 
     async def edit(
         self,
@@ -249,7 +262,7 @@ async def edit(
         name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         description: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         options: undefined.UndefinedOr[typing.Sequence[CommandOption]] = undefined.UNDEFINED,
-    ) -> Command:
+    ) -> PartialCommand:
         """Edit this command.
 
         Other Parameters
@@ -270,7 +283,7 @@ async def edit(
 
         Returns
         -------
-        Command
+        PartialCommand
             The edited command object.
 
         Raises
@@ -297,7 +310,7 @@ async def edit(
         hikari.errors.InternalServerError
             If an internal error occurs on Discord while handling the request.
         """
-        return await self.app.rest.edit_application_command(
+        command = await self.app.rest.edit_application_command(
             self.application_id,
             self.id,
             undefined.UNDEFINED if self.guild_id is None else self.guild_id,
@@ -305,6 +318,7 @@ async def edit(
             description=description,
             options=options,
         )
+        return command
 
     async def delete(self) -> None:
         """Delete this command.
@@ -423,6 +437,30 @@ async def set_guild_permissions(
         )
 
 
+@attr_extensions.with_copy
+@attr.define(hash=True, kw_only=True, weakref_slot=False)
+class SlashCommand(PartialCommand):
+    """Represents a slash command on Discord."""
+
+    description: str = attr.field(eq=False, hash=False, repr=False)
+    """The command's description.
+
+    None if this command is not a slash command.
+
+    !!! note
+        This will be inclusively between 1-100 characters in length.
+    """
+
+    options: typing.Optional[typing.Sequence[CommandOption]] = attr.field(eq=False, hash=False, repr=False)
+    """Sequence of up to (and including) 25 of the options for this command."""
+
+
+@attr_extensions.with_copy
+@attr.define(hash=True, kw_only=True, weakref_slot=False)
+class ContextMenuCommand(PartialCommand):
+    """Represents a slash command on Discord."""
+
+
 class CommandPermissionType(int, enums.Enum):
     """The type of entity a command permission targets."""
 
diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py
index ed0ca025..4ce19b23 100644
--- a/hikari/impl/entity_factory.py
+++ b/hikari/impl/entity_factory.py
@@ -189,6 +189,7 @@ class EntityFactoryImpl(entity_factory.EntityFactory):
         "_app",
         "_audit_log_entry_converters",
         "_audit_log_event_mapping",
+        "_command_mapping",
         "_component_type_mapping",
         "_dm_channel_type_mapping",
         "_guild_channel_type_mapping",
@@ -251,6 +252,11 @@ def __init__(self, app: traits.RESTAware) -> None:
             audit_log_models.AuditLogEventType.MEMBER_DISCONNECT: self._deserialize_member_disconnect_entry_info,
             audit_log_models.AuditLogEventType.MEMBER_MOVE: self._deserialize_member_move_entry_info,
         }
+        self._command_mapping = {
+            commands.CommandType.SLASH: self._deserialize_slash_command,
+            commands.CommandType.USER: self._deserialize_context_menu_command,
+            commands.CommandType.MESSAGE: self._deserialize_context_menu_command,
+        }
         self._component_type_mapping = {
             message_models.ComponentType.ACTION_ROW: self.deserialize_action_row,
             message_models.ComponentType.BUTTON: self.deserialize_button,
@@ -273,6 +279,7 @@ def __init__(self, app: traits.RESTAware) -> None:
         ] = {
             base_interactions.InteractionType.APPLICATION_COMMAND: self.deserialize_command_interaction,
             base_interactions.InteractionType.MESSAGE_COMPONENT: self.deserialize_component_interaction,
+            base_interactions.InteractionType.AUTOCOMPLETE: self.deserialize_autocomplete_interaction,
         }
         self._webhook_type_mapping = {
             webhook_models.WebhookType.INCOMING: self.deserialize_incoming_webhook,
@@ -1697,16 +1704,17 @@ def _deserialize_command_option(self, payload: data_binding.JSONObject) -> comma
             choices=choices,
             options=suboptions,
             channel_types=channel_types,
+            autocomplete=payload.get("autocomplete", False),
             min_value=payload.get("min_value"),
             max_value=payload.get("max_value"),
         )
 
-    def deserialize_command(
+    def _deserialize_slash_command(
         self,
         payload: data_binding.JSONObject,
         *,
         guild_id: undefined.UndefinedNoneOr[snowflakes.Snowflake] = undefined.UNDEFINED,
-    ) -> commands.Command:
+    ) -> commands.SlashCommand:
         if guild_id is undefined.UNDEFINED:
             raw_guild_id = payload["guild_id"]
             guild_id = snowflakes.Snowflake(raw_guild_id) if raw_guild_id is not None else None
@@ -1715,9 +1723,10 @@ def deserialize_command(
         if raw_options := payload.get("options"):
             options = [self._deserialize_command_option(option) for option in raw_options]
 
-        return commands.Command(
+        return commands.SlashCommand(
             app=self._app,
             id=snowflakes.Snowflake(payload["id"]),
+            type=commands.CommandType(payload["type"]),
             application_id=snowflakes.Snowflake(payload["application_id"]),
             name=payload["name"],
             description=payload["description"],
@@ -1727,6 +1736,41 @@ def deserialize_command(
             version=snowflakes.Snowflake(payload["version"]),
         )
 
+    def _deserialize_context_menu_command(
+        self,
+        payload: data_binding.JSONObject,
+        *,
+        guild_id: undefined.UndefinedNoneOr[snowflakes.Snowflake] = undefined.UNDEFINED,
+    ) -> commands.ContextMenuCommand:
+        if guild_id is undefined.UNDEFINED:
+            raw_guild_id = payload["guild_id"]
+            guild_id = snowflakes.Snowflake(raw_guild_id) if raw_guild_id is not None else None
+
+        return commands.ContextMenuCommand(
+            app=self._app,
+            id=snowflakes.Snowflake(payload["id"]),
+            type=commands.CommandType(payload["type"]),
+            application_id=snowflakes.Snowflake(payload["application_id"]),
+            name=payload["name"],
+            default_permission=payload.get("default_permission", True),
+            guild_id=guild_id,
+            version=snowflakes.Snowflake(payload["version"]),
+        )
+
+    def deserialize_command(
+        self,
+        payload: data_binding.JSONObject,
+        *,
+        guild_id: undefined.UndefinedNoneOr[snowflakes.Snowflake] = undefined.UNDEFINED,
+    ) -> commands.PartialCommand:
+        command_type = commands.CommandType(payload["type"])
+
+        if deserialize := self._command_mapping.get(command_type):
+            return deserialize(payload, guild_id=guild_id)
+
+        _LOGGER.debug("Unknown command type %s", command_type)
+        raise errors.UnrecognisedEntityError(f"Unrecognised command type {command_type}")
+
     def deserialize_guild_command_permissions(
         self, payload: data_binding.JSONObject
     ) -> commands.GuildCommandPermissions:
@@ -1777,6 +1821,29 @@ def _deserialize_interaction_command_option(
             options=suboptions,
         )
 
+    def _deserialize_autocomplete_interaction_option(
+        self,
+        payload: data_binding.JSONObject,
+    ) -> command_interactions.AutocompleteInteractionOption:
+        suboptions: typing.Optional[typing.List[command_interactions.CommandInteractionOption]] = None
+        if raw_suboptions := payload.get("options"):
+            suboptions = [self._deserialize_interaction_command_option(suboption) for suboption in raw_suboptions]
+
+        is_focused = payload.get("focused", False)
+
+        option_type = commands.OptionType(payload["type"])
+        value = payload.get("value")
+        if is_focused and (modifier := _interaction_option_type_mapping.get(option_type)):
+            value = modifier(value)
+
+        return command_interactions.AutocompleteInteractionOption(
+            name=payload["name"],
+            type=option_type,
+            value=value,
+            options=suboptions,
+            is_focused=is_focused,
+        )
+
     def _deserialize_interaction_member(
         self,
         payload: data_binding.JSONObject,
@@ -1817,6 +1884,61 @@ def _deserialize_interaction_member(
             raw_communication_disabled_until=disabled_until,
         )
 
+    def _deserialize_resolved_option_data(
+        self,
+        payload: data_binding.JSONObject,
+        *,
+        guild_id: typing.Optional[snowflakes.Snowflake] = None,
+    ) -> command_interactions.ResolvedOptionData:
+        channels: typing.Dict[snowflakes.Snowflake, command_interactions.InteractionChannel] = {}
+        if raw_channels := payload.get("channels"):
+            for channel_payload in raw_channels.values():
+                channel_id = snowflakes.Snowflake(channel_payload["id"])
+                channels[channel_id] = command_interactions.InteractionChannel(
+                    app=self._app,
+                    id=channel_id,
+                    type=channel_models.ChannelType(channel_payload["type"]),
+                    name=channel_payload["name"],
+                    permissions=permission_models.Permissions(int(channel_payload["permissions"])),
+                )
+
+        if raw_users := payload.get("users"):
+            users = {u.id: u for u in map(self.deserialize_user, raw_users.values())}
+
+        else:
+            users = {}
+
+        members: typing.Dict[snowflakes.Snowflake, base_interactions.InteractionMember] = {}
+        if raw_members := payload.get("members"):
+            for user_id, member_payload in raw_members.items():
+                assert guild_id is not None
+                user_id = snowflakes.Snowflake(user_id)
+                members[user_id] = self._deserialize_interaction_member(
+                    member_payload, user=users[user_id], guild_id=guild_id
+                )
+
+        if raw_roles := payload.get("roles"):
+            assert guild_id is not None
+            roles_iter = (self.deserialize_role(role, guild_id=guild_id) for role in raw_roles.values())
+            roles = {r.id: r for r in roles_iter}
+
+        else:
+            roles = {}
+
+        if raw_messages := payload.get("messages"):
+            messages = {m.id: m for m in map(self.deserialize_message, raw_messages.values())}
+
+        else:
+            messages = {}
+
+        return command_interactions.ResolvedOptionData(
+            channels=channels,
+            members=members,
+            users=users,
+            roles=roles,
+            messages=messages,
+        )
+
     def deserialize_command_interaction(
         self, payload: data_binding.JSONObject
     ) -> command_interactions.CommandInteraction:
@@ -1843,49 +1965,62 @@ def deserialize_command_interaction(
 
         resolved: typing.Optional[command_interactions.ResolvedOptionData] = None
         if resolved_payload := data_payload.get("resolved"):
-            channels: typing.Dict[snowflakes.Snowflake, command_interactions.InteractionChannel] = {}
-            if raw_channels := resolved_payload.get("channels"):
-                for channel_payload in raw_channels.values():
-                    channel_id = snowflakes.Snowflake(channel_payload["id"])
-                    channels[channel_id] = command_interactions.InteractionChannel(
-                        app=self._app,
-                        id=channel_id,
-                        type=channel_models.ChannelType(channel_payload["type"]),
-                        name=channel_payload["name"],
-                        permissions=permission_models.Permissions(int(channel_payload["permissions"])),
-                    )
+            resolved = self._deserialize_resolved_option_data(resolved_payload, guild_id=guild_id)
 
-            if raw_users := resolved_payload.get("users"):
-                users = {u.id: u for u in map(self.deserialize_user, raw_users.values())}
+        target_id: typing.Optional[snowflakes.Snowflake] = None
+        if raw_target_id := data_payload.get("target_id"):
+            target_id = snowflakes.Snowflake(raw_target_id)
 
-            else:
-                users = {}
-
-            members: typing.Dict[snowflakes.Snowflake, base_interactions.InteractionMember] = {}
-            if raw_members := resolved_payload.get("members"):
-                for user_id, member_payload in raw_members.items():
-                    assert guild_id is not None
-                    user_id = snowflakes.Snowflake(user_id)
-                    members[user_id] = self._deserialize_interaction_member(
-                        member_payload, user=users[user_id], guild_id=guild_id
-                    )
+        return command_interactions.CommandInteraction(
+            app=self._app,
+            application_id=snowflakes.Snowflake(payload["application_id"]),
+            id=snowflakes.Snowflake(payload["id"]),
+            type=base_interactions.InteractionType(payload["type"]),
+            guild_id=guild_id,
+            guild_locale=payload.get("guild_locale", "en-US"),
+            locale=payload["locale"],
+            channel_id=snowflakes.Snowflake(payload["channel_id"]),
+            member=member,
+            user=user,
+            token=payload["token"],
+            version=payload["version"],
+            command_id=snowflakes.Snowflake(data_payload["id"]),
+            command_name=data_payload["name"],
+            command_type=commands.CommandType(data_payload.get("type", commands.CommandType.SLASH)),
+            options=options,
+            resolved=resolved,
+            target_id=target_id,
+        )
 
-            if raw_roles := resolved_payload.get("roles"):
-                assert guild_id is not None
-                roles_iter = (self.deserialize_role(role, guild_id=guild_id) for role in raw_roles.values())
-                roles = {r.id: r for r in roles_iter}
+    def deserialize_autocomplete_interaction(
+        self, payload: data_binding.JSONObject
+    ) -> command_interactions.AutocompleteInteraction:
+        data_payload = payload["data"]
 
-            else:
-                roles = {}
+        guild_id: typing.Optional[snowflakes.Snowflake] = None
+        if raw_guild_id := payload.get("guild_id"):
+            guild_id = snowflakes.Snowflake(raw_guild_id)
 
-            resolved = command_interactions.ResolvedOptionData(
-                channels=channels,
-                members=members,
-                users=users,
-                roles=roles,
-            )
+        options: typing.Optional[typing.List[command_interactions.AutocompleteInteractionOption]] = None
+        if raw_options := data_payload.get("options"):
+            options = [self._deserialize_autocomplete_interaction_option(option) for option in raw_options]
 
-        return command_interactions.CommandInteraction(
+        member: typing.Optional[base_interactions.InteractionMember]
+        if member_payload := payload.get("member"):
+            assert guild_id is not None
+            member = self._deserialize_interaction_member(member_payload, guild_id=guild_id)
+            # See https://github.com/discord/discord-api-docs/pull/2568
+            user = member.user
+
+        else:
+            member = None
+            user = self.deserialize_user(payload["user"])
+
+        resolved: typing.Optional[command_interactions.ResolvedOptionData] = None
+        if resolved_payload := data_payload.get("resolved"):
+            resolved = self._deserialize_resolved_option_data(resolved_payload, guild_id=guild_id)
+
+        return command_interactions.AutocompleteInteraction(
             app=self._app,
             application_id=snowflakes.Snowflake(payload["application_id"]),
             id=snowflakes.Snowflake(payload["id"]),
@@ -1898,6 +2033,7 @@ def deserialize_command_interaction(
             version=payload["version"],
             command_id=snowflakes.Snowflake(data_payload["id"]),
             command_name=data_payload["name"],
+            command_type=commands.CommandType(data_payload.get("type", commands.CommandType.SLASH)),
             options=options,
             resolved=resolved,
             locale=payload["locale"],
@@ -1930,6 +2066,9 @@ def serialize_command_option(self, option: commands.CommandOption) -> data_bindi
         if option.options is not None:
             payload["options"] = [self.serialize_command_option(suboption) for suboption in option.options]
 
+        if option.autocomplete:
+            payload["autocomplete"] = True
+
         if option.min_value is not None:
             payload["min_value"] = option.min_value
         if option.max_value is not None:
@@ -2300,9 +2439,9 @@ def deserialize_partial_message(  # noqa CFQ001 - Function too long
 
         return message
 
-    def deserialize_message(  # noqa CFQ001 - Function too long
+    def deserialize_message(
         self, payload: data_binding.JSONObject
-    ) -> message_models.Message:
+    ) -> message_models.Message:  # noqa CFQ001 - Function too long
         author = self.deserialize_user(payload["author"])
 
         guild_id: typing.Optional[snowflakes.Snowflake] = None
diff --git a/hikari/impl/interaction_server.py b/hikari/impl/interaction_server.py
index 97daab49..f85ff6fd 100644
--- a/hikari/impl/interaction_server.py
+++ b/hikari/impl/interaction_server.py
@@ -54,7 +54,8 @@
     _InteractionT_co = typing.TypeVar("_InteractionT_co", bound=base_interactions.PartialInteraction, covariant=True)
     _ResponseT_co = typing.TypeVar("_ResponseT_co", bound=special_endpoints.InteractionResponseBuilder, covariant=True)
     _MessageResponseBuilderT = typing.Union[
-        special_endpoints.InteractionDeferredBuilder, special_endpoints.InteractionMessageBuilder
+        special_endpoints.InteractionDeferredBuilder,
+        special_endpoints.InteractionMessageBuilder,
     ]
 
 _LOGGER: typing.Final[logging.Logger] = logging.getLogger("hikari.interaction_server")
@@ -501,6 +502,16 @@ def get_listener(
     ]:
         ...
 
+    @typing.overload
+    def get_listener(
+        self, interaction_type: typing.Type[command_interactions.AutocompleteInteraction], /
+    ) -> typing.Optional[
+        interaction_server.ListenerT[
+            command_interactions.AutocompleteInteraction, special_endpoints.InteractionAutocompleteBuilder
+        ]
+    ]:
+        ...
+
     @typing.overload
     def get_listener(
         self, interaction_type: typing.Type[_InteractionT_co], /
@@ -538,6 +549,21 @@ def set_listener(
     ) -> None:
         ...
 
+    @typing.overload
+    def set_listener(
+        self,
+        interaction_type: typing.Type[command_interactions.AutocompleteInteraction],
+        listener: typing.Optional[
+            interaction_server.ListenerT[
+                command_interactions.AutocompleteInteraction, special_endpoints.InteractionAutocompleteBuilder
+            ]
+        ],
+        /,
+        *,
+        replace: bool = False,
+    ) -> None:
+        ...
+
     def set_listener(
         self,
         interaction_type: typing.Type[_InteractionT_co],
diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py
index 991beac9..b41f5d01 100644
--- a/hikari/impl/rest.py
+++ b/hikari/impl/rest.py
@@ -51,6 +51,7 @@
 from hikari import applications
 from hikari import channels as channels_
 from hikari import colors
+from hikari import commands
 from hikari import config
 from hikari import embeds as embeds_
 from hikari import emojis
@@ -69,6 +70,7 @@
 from hikari.impl import entity_factory as entity_factory_impl
 from hikari.impl import rate_limits
 from hikari.impl import special_endpoints as special_endpoints_impl
+from hikari.interactions import base_interactions
 from hikari.internal import data_binding
 from hikari.internal import deprecation
 from hikari.internal import mentions
@@ -82,7 +84,6 @@
     import types
 
     from hikari import audit_logs
-    from hikari import commands
     from hikari import invites
     from hikari import messages as messages_
     from hikari import sessions
@@ -93,7 +94,6 @@
     from hikari.api import cache as cache_api
     from hikari.api import entity_factory as entity_factory_
     from hikari.api import special_endpoints
-    from hikari.interactions import base_interactions
 
 _LOGGER: typing.Final[logging.Logger] = logging.getLogger("hikari.rest")
 
@@ -1204,26 +1204,32 @@ async def fetch_message(
         assert isinstance(response, dict)
         return self._entity_factory.deserialize_message(response)
 
-    async def _create_message(
+    def _build_message_payload(  # noqa: C901- Function too complex
         self,
-        route: routes.CompiledRoute,
-        body: data_binding.JSONObjectBuilder,
-        query: typing.Optional[data_binding.StringMapBuilder] = None,
+        /,
         *,
-        no_auth: bool = False,
-        content: undefined.UndefinedOr[typing.Any],
-        attachment: undefined.UndefinedOr[files.Resourceish],
-        attachments: undefined.UndefinedOr[typing.Sequence[files.Resourceish]],
-        component: undefined.UndefinedOr[special_endpoints.ComponentBuilder] = undefined.UNDEFINED,
-        components: undefined.UndefinedOr[typing.Sequence[special_endpoints.ComponentBuilder]] = undefined.UNDEFINED,
-        embed: undefined.UndefinedOr[embeds_.Embed],
-        embeds: undefined.UndefinedOr[typing.Sequence[embeds_.Embed]],
-        tts: undefined.UndefinedOr[bool],
-        mentions_everyone: undefined.UndefinedOr[bool],
-        mentions_reply: undefined.UndefinedOr[bool],
-        user_mentions: undefined.UndefinedOr[typing.Union[snowflakes.SnowflakeishSequence[users.PartialUser], bool]],
-        role_mentions: undefined.UndefinedOr[typing.Union[snowflakes.SnowflakeishSequence[guilds.PartialRole], bool]],
-    ) -> messages_.Message:
+        content: undefined.UndefinedOr[typing.Any] = undefined.UNDEFINED,
+        attachment: undefined.UndefinedOr[files.Resourceish] = undefined.UNDEFINED,
+        attachments: undefined.UndefinedOr[typing.Sequence[files.Resourceish]] = undefined.UNDEFINED,
+        component: undefined.UndefinedNoneOr[special_endpoints.ComponentBuilder] = undefined.UNDEFINED,
+        components: undefined.UndefinedNoneOr[
+            typing.Sequence[special_endpoints.ComponentBuilder]
+        ] = undefined.UNDEFINED,
+        embed: undefined.UndefinedNoneOr[embeds_.Embed] = undefined.UNDEFINED,
+        embeds: undefined.UndefinedNoneOr[typing.Sequence[embeds_.Embed]] = undefined.UNDEFINED,
+        replace_attachments: bool = False,
+        flags: typing.Union[undefined.UndefinedType, int, messages_.MessageFlag] = undefined.UNDEFINED,
+        tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+        mentions_everyone: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+        mentions_reply: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+        user_mentions: undefined.UndefinedOr[
+            typing.Union[snowflakes.SnowflakeishSequence[users.PartialUser], bool]
+        ] = undefined.UNDEFINED,
+        role_mentions: undefined.UndefinedOr[
+            typing.Union[snowflakes.SnowflakeishSequence[guilds.PartialRole], bool]
+        ] = undefined.UNDEFINED,
+        edit: bool = False,
+    ) -> typing.Tuple[data_binding.JSONObjectBuilder, typing.Optional[data_binding.URLEncodedFormBuilder]]:
         if not undefined.any_undefined(attachment, attachments):
             raise ValueError("You may only specify one of 'attachment' or 'attachments', not both")
 
@@ -1239,7 +1245,7 @@ async def _create_message(
                 "use 'attachment' (singular) instead?"
             )
 
-        if components is not undefined.UNDEFINED and not isinstance(components, typing.Collection):
+        if components not in _NONE_OR_UNDEFINED and not isinstance(components, typing.Collection):
             raise TypeError(
                 "You passed a non-collection to 'components', but this expects a collection. Maybe you meant to "
                 "use 'component' (singular) instead?"
@@ -1253,7 +1259,7 @@ async def _create_message(
 
         if undefined.all_undefined(embed, embeds) and isinstance(content, embeds_.Embed):
             # Syntactic sugar, common mistake to accidentally send an embed
-            # as the content, so lets detect this and fix it for the user.
+            # as the content, so let's detect this and fix it for the user.
             embed = content
             content = undefined.UNDEFINED
 
@@ -1261,7 +1267,7 @@ async def _create_message(
             content, (files.Resource, files.RAWISH_TYPES, os.PathLike)
         ):
             # Syntactic sugar, common mistake to accidentally send an attachment
-            # as the content, so lets detect this and fix it for the user. This
+            # as the content, so let's detect this and fix it for the user. This
             # will still then work with normal implicit embed attachments as
             # we work this out later.
             attachment = content
@@ -1270,49 +1276,64 @@ async def _create_message(
         final_attachments: typing.List[files.Resource[files.AsyncReader]] = []
         if attachment is not undefined.UNDEFINED:
             final_attachments.append(files.ensure_resource(attachment))
-        if attachments is not undefined.UNDEFINED:
+        elif attachments is not undefined.UNDEFINED:
             final_attachments.extend([files.ensure_resource(a) for a in attachments])
 
+        serialized_components: undefined.UndefinedOr[typing.List[data_binding.JSONObject]] = undefined.UNDEFINED
         if component is not undefined.UNDEFINED:
-            body.put("components", [component.build()])
+            if component is not None:
+                serialized_components = [component.build()]
+            else:
+                serialized_components = []
 
         elif components is not undefined.UNDEFINED:
-            body.put("components", [component.build() for component in components])
+            if components is not None:
+                serialized_components = [component.build() for component in components]
+            else:
+                serialized_components = []
 
         serialized_embeds: undefined.UndefinedOr[data_binding.JSONArray] = undefined.UNDEFINED
-
         if embed is not undefined.UNDEFINED:
-            embed_payload, embed_attachments = self._entity_factory.serialize_embed(embed)
-            final_attachments.extend(embed_attachments)
-            serialized_embeds = [embed_payload]
-        elif embeds is not undefined.UNDEFINED:
-            serialized_embeds = []
-            for embed in embeds:
+            if embed is not None:
                 embed_payload, embed_attachments = self._entity_factory.serialize_embed(embed)
                 final_attachments.extend(embed_attachments)
-                serialized_embeds.append(embed_payload)
+                serialized_embeds = [embed_payload]
 
-        body.put(
-            "allowed_mentions",
-            mentions.generate_allowed_mentions(mentions_everyone, mentions_reply, user_mentions, role_mentions),
-        )
-        body.put("content", content, conversion=str)
+            else:
+                serialized_embeds = []
+
+        elif embeds is not undefined.UNDEFINED:
+            serialized_embeds = []
+            if embeds is not None:
+                for e in embeds:
+                    embed_payload, embed_attachments = self._entity_factory.serialize_embed(e)
+                    final_attachments.extend(embed_attachments)
+                    serialized_embeds.append(embed_payload)
+
+        body = data_binding.JSONObjectBuilder()
+        body.put("content", content, conversion=lambda v: v if v is None else str(v))
         body.put("tts", tts)
+        body.put("flags", flags)
         body.put("embeds", serialized_embeds)
+        body.put("components", serialized_components)
+
+        if replace_attachments:
+            body.put("attachments", None)
+
+        if not edit or not undefined.all_undefined(mentions_everyone, mentions_reply, user_mentions, role_mentions):
+            body.put(
+                "allowed_mentions",
+                mentions.generate_allowed_mentions(mentions_everyone, mentions_reply, user_mentions, role_mentions),
+            )
 
         if final_attachments:
             form = data_binding.URLEncodedFormBuilder(executor=self._executor)
-            form.add_field("payload_json", data_binding.dump_json(body), content_type=_APPLICATION_JSON)
 
             for i, attachment in enumerate(final_attachments):
                 form.add_resource(f"file{i}", attachment)
+            return body, form
 
-            response = await self._request(route, form_builder=form, query=query, no_auth=no_auth)
-        else:
-            response = await self._request(route, json=body, query=query, no_auth=no_auth)
-
-        assert isinstance(response, dict)
-        return self._entity_factory.deserialize_message(response)
+        return body, None
 
     async def create_message(
         self,
@@ -1338,12 +1359,7 @@ async def create_message(
         ] = undefined.UNDEFINED,
     ) -> messages_.Message:
         route = routes.POST_CHANNEL_MESSAGES.compile(channel=channel)
-        body = data_binding.JSONObjectBuilder()
-        body.put("nonce", nonce)
-        body.put("message_reference", reply, conversion=lambda m: {"message_id": str(int(m))})
-        return await self._create_message(
-            route,
-            body,
+        body, form = self._build_message_payload(
             content=content,
             attachment=attachment,
             attachments=attachments,
@@ -1357,6 +1373,17 @@ async def create_message(
             user_mentions=user_mentions,
             role_mentions=role_mentions,
         )
+        body.put("nonce", nonce)
+        body.put("message_reference", reply, conversion=lambda m: {"message_id": str(int(m))})
+
+        if form is not None:
+            form.add_field("payload_json", data_binding.dump_json(body), content_type=_APPLICATION_JSON)
+            response = await self._request(route, form_builder=form)
+        else:
+            response = await self._request(route, json=body)
+
+        assert isinstance(response, dict)
+        return self._entity_factory.deserialize_message(response)
 
     async def crosspost_message(
         self,
@@ -1370,135 +1397,6 @@ async def crosspost_message(
         assert isinstance(response, dict)
         return self._entity_factory.deserialize_message(response)
 
-    async def _edit_message(  # noqa: C901 - Function too complex
-        self,
-        route: routes.CompiledRoute,
-        body: data_binding.JSONObjectBuilder,
-        *,
-        no_auth: bool = False,
-        content: undefined.UndefinedOr[typing.Any],
-        attachment: undefined.UndefinedOr[files.Resourceish],
-        attachments: undefined.UndefinedOr[typing.Sequence[files.Resourceish]],
-        component: undefined.UndefinedNoneOr[special_endpoints.ComponentBuilder] = undefined.UNDEFINED,
-        components: undefined.UndefinedNoneOr[
-            typing.Sequence[special_endpoints.ComponentBuilder]
-        ] = undefined.UNDEFINED,
-        embed: undefined.UndefinedNoneOr[embeds_.Embed],
-        embeds: undefined.UndefinedNoneOr[typing.Sequence[embeds_.Embed]],
-        replace_attachments: bool,
-        mentions_everyone: undefined.UndefinedOr[bool],
-        mentions_reply: undefined.UndefinedOr[bool],
-        user_mentions: undefined.UndefinedOr[typing.Union[snowflakes.SnowflakeishSequence[users.PartialUser], bool]],
-        role_mentions: undefined.UndefinedOr[typing.Union[snowflakes.SnowflakeishSequence[guilds.PartialRole], bool]],
-    ) -> messages_.Message:
-        if not undefined.any_undefined(attachment, attachments):
-            raise ValueError("You may only specify one of 'attachment' or 'attachments', not both")
-
-        if not undefined.any_undefined(component, components):
-            raise ValueError("You may only specify one of 'component' or 'components', not both")
-
-        if not undefined.any_undefined(embed, embeds):
-            raise ValueError("You may only specify one of 'embed' or 'embeds', not both")
-
-        if attachments is not undefined.UNDEFINED and not isinstance(attachments, typing.Collection):
-            raise TypeError(
-                "You passed a non-collection to 'attachments', but this expects a collection. Maybe you meant to "
-                "use 'attachment' (singular) instead?"
-            )
-
-        if components is not undefined.UNDEFINED and not isinstance(components, typing.Collection):
-            raise TypeError(
-                "You passed a non-collection to 'components', but this expects a collection. Maybe you meant to "
-                "use 'component' (singular) instead?"
-            )
-
-        if embeds not in _NONE_OR_UNDEFINED and not isinstance(embeds, typing.Collection):
-            raise TypeError(
-                "You passed a non-collection to 'embeds', but this expects a collection. Maybe you meant to "
-                "use 'embed' (singular) instead?"
-            )
-
-        if not undefined.all_undefined(mentions_everyone, mentions_reply, user_mentions, role_mentions):
-            body.put(
-                "allowed_mentions",
-                mentions.generate_allowed_mentions(mentions_everyone, mentions_reply, user_mentions, role_mentions),
-            )
-
-        if embed is undefined.UNDEFINED and isinstance(content, embeds_.Embed):
-            # Syntactic sugar, common mistake to accidentally send an embed
-            # as the content, so lets detect this and fix it for the user.
-            embed = content
-            content = undefined.UNDEFINED
-        elif undefined.all_undefined(attachment, attachments) and isinstance(
-            content, (files.Resource, files.RAWISH_TYPES, os.PathLike)
-        ):
-            # Syntactic sugar, common mistake to accidentally send an attachment
-            # as the content, so lets detect this and fix it for the user. This
-            # will still then work with normal implicit embed attachments as
-            # we work this out later.
-            attachment = content
-            content = undefined.UNDEFINED
-
-        if content is not None:
-            body.put("content", content, conversion=str)
-        else:
-            body.put("content", None)
-
-        final_attachments: typing.List[files.Resource[files.AsyncReader]] = []
-        if attachment is not undefined.UNDEFINED:
-            final_attachments.append(files.ensure_resource(attachment))
-        if attachments is not undefined.UNDEFINED:
-            final_attachments.extend([files.ensure_resource(a) for a in attachments])
-
-        serialized_components: typing.Optional[typing.List[data_binding.JSONObject]] = None
-        if component is not undefined.UNDEFINED:
-            if component is not None:
-                serialized_components = [component.build()]
-
-            body.put("components", serialized_components)
-
-        elif components is not undefined.UNDEFINED:
-            if components is not None:
-                serialized_components = [component.build() for component in components]
-
-            body.put("components", serialized_components)
-
-        serialized_embeds: data_binding.JSONArray = []
-        update_embeds = False
-        if embed is not undefined.UNDEFINED:
-            update_embeds = True
-            if embed is not None:
-                embed_payload, embed_attachments = self._entity_factory.serialize_embed(embed)
-                serialized_embeds.append(embed_payload)
-                final_attachments.extend(embed_attachments)
-        elif embeds is not undefined.UNDEFINED:
-            update_embeds = True
-            if embeds is not None:
-                for embed in embeds:
-                    embed_payload, embed_attachments = self._entity_factory.serialize_embed(embed)
-                    serialized_embeds.append(embed_payload)
-                    final_attachments.extend(embed_attachments)
-
-        if update_embeds:
-            body.put("embeds", serialized_embeds)
-
-        if replace_attachments:
-            body.put("attachments", None)
-
-        if final_attachments:
-            form_builder = data_binding.URLEncodedFormBuilder(executor=self._executor)
-            form_builder.add_field("payload_json", data_binding.dump_json(body), content_type=_APPLICATION_JSON)
-
-            for i, attachment in enumerate(final_attachments):
-                form_builder.add_resource(f"file{i}", attachment)
-
-            response = await self._request(route, form_builder=form_builder, no_auth=no_auth)
-        else:
-            response = await self._request(route, json=body, no_auth=no_auth)
-
-        assert isinstance(response, dict)
-        return self._entity_factory.deserialize_message(response)
-
     async def edit_message(
         self,
         channel: snowflakes.SnowflakeishOr[channels_.TextableChannel],
@@ -1522,14 +1420,10 @@ async def edit_message(
         role_mentions: undefined.UndefinedOr[
             typing.Union[snowflakes.SnowflakeishSequence[guilds.PartialRole], bool]
         ] = undefined.UNDEFINED,
-        flags: undefined.UndefinedOr[messages_.MessageFlag] = undefined.UNDEFINED,
+        flags: typing.Union[undefined.UndefinedType, int, messages_.MessageFlag] = undefined.UNDEFINED,
     ) -> messages_.Message:
         route = routes.PATCH_CHANNEL_MESSAGE.compile(channel=channel, message=message)
-        body = data_binding.JSONObjectBuilder()
-        body.put("flags", flags)
-        return await self._edit_message(
-            route,
-            body,
+        body, form = self._build_message_payload(
             content=content,
             attachment=attachment,
             attachments=attachments,
@@ -1538,12 +1432,23 @@ async def edit_message(
             embed=embed,
             embeds=embeds,
             replace_attachments=replace_attachments,
+            flags=flags,
             mentions_everyone=mentions_everyone,
             mentions_reply=mentions_reply,
             user_mentions=user_mentions,
             role_mentions=role_mentions,
+            edit=True,
         )
 
+        if form is not None:
+            form.add_field("payload_json", data_binding.dump_json(body), content_type=_APPLICATION_JSON)
+            response = await self._request(route, form_builder=form)
+        else:
+            response = await self._request(route, json=body)
+
+        assert isinstance(response, dict)
+        return self._entity_factory.deserialize_message(response)
+
     async def delete_message(
         self,
         channel: snowflakes.SnowflakeishOr[channels_.TextableChannel],
@@ -1844,17 +1749,10 @@ async def execute_webhook(
         webhook_id = webhook if isinstance(webhook, int) else webhook.webhook_id
         route = routes.POST_WEBHOOK_WITH_TOKEN.compile(webhook=webhook_id, token=token)
 
-        body = data_binding.JSONObjectBuilder()
-        body.put("username", username)
-        body.put("avatar_url", avatar_url, conversion=str)
-        body.put("flags", flags)
         query = data_binding.StringMapBuilder()
         query.put("wait", True)
-        return await self._create_message(
-            route,
-            body,
-            query,
-            no_auth=True,
+
+        body, form = self._build_message_payload(
             content=content,
             attachment=attachment,
             attachments=attachments,
@@ -1863,11 +1761,22 @@ async def execute_webhook(
             embed=embed,
             embeds=embeds,
             tts=tts,
+            flags=flags,
             mentions_everyone=mentions_everyone,
             user_mentions=user_mentions,
             role_mentions=role_mentions,
-            mentions_reply=undefined.UNDEFINED,
         )
+        body.put("username", username)
+        body.put("avatar_url", avatar_url, conversion=str)
+
+        if form is not None:
+            form.add_field("payload_json", data_binding.dump_json(body), content_type=_APPLICATION_JSON)
+            response = await self._request(route, form_builder=form, query=query, no_auth=True)
+        else:
+            response = await self._request(route, json=body, query=query, no_auth=True)
+
+        assert isinstance(response, dict)
+        return self._entity_factory.deserialize_message(response)
 
     async def fetch_webhook_message(
         self,
@@ -1909,10 +1818,8 @@ async def edit_webhook_message(
         # int(ExecutableWebhook) isn't guaranteed to be valid nor the ID used to execute this entity as a webhook.
         webhook_id = webhook if isinstance(webhook, int) else webhook.webhook_id
         route = routes.PATCH_WEBHOOK_MESSAGE.compile(webhook=webhook_id, token=token, message=message)
-        return await self._edit_message(
-            route,
-            data_binding.JSONObjectBuilder(),
-            no_auth=True,
+
+        body, form = self._build_message_payload(
             content=content,
             attachment=attachment,
             attachments=attachments,
@@ -1922,11 +1829,20 @@ async def edit_webhook_message(
             embeds=embeds,
             replace_attachments=replace_attachments,
             mentions_everyone=mentions_everyone,
-            mentions_reply=undefined.UNDEFINED,
             user_mentions=user_mentions,
             role_mentions=role_mentions,
+            edit=True,
         )
 
+        if form is not None:
+            form.add_field("payload_json", data_binding.dump_json(body), content_type=_APPLICATION_JSON)
+            response = await self._request(route, form_builder=form, no_auth=True)
+        else:
+            response = await self._request(route, json=body, no_auth=True)
+
+        assert isinstance(response, dict)
+        return self._entity_factory.deserialize_message(response)
+
     async def delete_webhook_message(
         self,
         webhook: typing.Union[webhooks.ExecutableWebhook, snowflakes.Snowflakeish],
@@ -3197,15 +3113,26 @@ async def delete_template(
         assert isinstance(response, dict)
         return self._entity_factory.deserialize_template(response)
 
-    def command_builder(self, name: str, description: str, /) -> special_endpoints.CommandBuilder:
-        return special_endpoints_impl.CommandBuilder(name=name, description=description)
+    @deprecation.deprecated("2.0.0.dev106", "slash_command_builder or context_menu_builder")
+    def command_builder(self, name: str, description: str) -> special_endpoints.SlashCommandBuilder:
+        return self.slash_command_builder(name, description)
+
+    def slash_command_builder(self, name: str, description: str) -> special_endpoints.SlashCommandBuilder:
+        return special_endpoints_impl.SlashCommandBuilder(name, description)
+
+    def context_menu_command_builder(
+        self,
+        type: typing.Union[commands.CommandType, int],
+        name: str,
+    ) -> special_endpoints.ContextMenuCommandBuilder:
+        return special_endpoints_impl.ContextMenuCommandBuilder(commands.CommandType(type), name)
 
     async def fetch_application_command(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
-        command: snowflakes.SnowflakeishOr[commands.Command],
+        command: snowflakes.SnowflakeishOr[commands.PartialCommand],
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
-    ) -> commands.Command:
+    ) -> commands.PartialCommand:
         if guild is undefined.UNDEFINED:
             route = routes.GET_APPLICATION_COMMAND.compile(application=application, command=command)
 
@@ -3222,7 +3149,7 @@ async def fetch_application_commands(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
-    ) -> typing.Sequence[commands.Command]:
+    ) -> typing.Sequence[commands.PartialCommand]:
         if guild is undefined.UNDEFINED:
             route = routes.GET_APPLICATION_COMMANDS.compile(application=application)
 
@@ -3234,16 +3161,17 @@ async def fetch_application_commands(
         guild_id = snowflakes.Snowflake(guild) if guild is not undefined.UNDEFINED else None
         return [self._entity_factory.deserialize_command(command, guild_id=guild_id) for command in response]
 
-    async def create_application_command(
+    async def _create_application_command(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
+        type: typing.Union[commands.CommandType, int],
         name: str,
-        description: str,
-        guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
+        description: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         *,
+        guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
         options: undefined.UndefinedOr[typing.Sequence[commands.CommandOption]] = undefined.UNDEFINED,
         default_permission: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
-    ) -> commands.Command:
+    ) -> commands.PartialCommand:
         if guild is undefined.UNDEFINED:
             route = routes.POST_APPLICATION_COMMAND.compile(application=application)
 
@@ -3253,6 +3181,7 @@ async def create_application_command(
         body = data_binding.JSONObjectBuilder()
         body.put("name", name)
         body.put("description", description)
+        body.put("type", type)
         body.put_array("options", options, conversion=self._entity_factory.serialize_command_option)
         body.put("default_permission", default_permission)
 
@@ -3262,12 +3191,71 @@ async def create_application_command(
             response, guild_id=snowflakes.Snowflake(guild) if guild is not undefined.UNDEFINED else None
         )
 
+    @deprecation.deprecated("2.0.0.dev106", "create_slash_command or create_context_menu_command")
+    async def create_application_command(
+        self,
+        application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
+        name: str,
+        description: str,
+        guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
+        *,
+        options: undefined.UndefinedOr[typing.Sequence[commands.CommandOption]] = undefined.UNDEFINED,
+        default_permission: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+    ) -> commands.SlashCommand:
+        return await self.create_slash_command(
+            application=application,
+            name=name,
+            description=description,
+            guild=guild,
+            options=options,
+            default_permission=default_permission,
+        )
+
+    async def create_slash_command(
+        self,
+        application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
+        name: str,
+        description: str,
+        *,
+        guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
+        options: undefined.UndefinedOr[typing.Sequence[commands.CommandOption]] = undefined.UNDEFINED,
+        default_permission: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+    ) -> commands.SlashCommand:
+        command = await self._create_application_command(
+            application=application,
+            type=commands.CommandType.SLASH,
+            name=name,
+            description=description,
+            guild=guild,
+            options=options,
+            default_permission=default_permission,
+        )
+        return typing.cast(commands.SlashCommand, command)
+
+    async def create_context_menu_command(
+        self,
+        application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
+        type: typing.Literal[commands.CommandType.USER, commands.CommandType.MESSAGE, 2, 3],
+        name: str,
+        *,
+        guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
+        default_permission: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+    ) -> commands.ContextMenuCommand:
+        command = await self._create_application_command(
+            application=application,
+            type=type,
+            name=name,
+            guild=guild,
+            default_permission=default_permission,
+        )
+        return typing.cast(commands.ContextMenuCommand, command)
+
     async def set_application_commands(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
         commands: typing.Sequence[special_endpoints.CommandBuilder],
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
-    ) -> typing.Sequence[commands.Command]:
+    ) -> typing.Sequence[commands.PartialCommand]:
         if guild is undefined.UNDEFINED:
             route = routes.PUT_APPLICATION_COMMANDS.compile(application=application)
 
@@ -3282,13 +3270,13 @@ async def set_application_commands(
     async def edit_application_command(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
-        command: snowflakes.SnowflakeishOr[commands.Command],
+        command: snowflakes.SnowflakeishOr[commands.PartialCommand],
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
         *,
         name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         description: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         options: undefined.UndefinedOr[typing.Sequence[commands.CommandOption]] = undefined.UNDEFINED,
-    ) -> commands.Command:
+    ) -> commands.PartialCommand:
         if guild is undefined.UNDEFINED:
             route = routes.PATCH_APPLICATION_COMMAND.compile(application=application, command=command)
 
@@ -3311,7 +3299,7 @@ async def edit_application_command(
     async def delete_application_command(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
-        command: snowflakes.SnowflakeishOr[commands.Command],
+        command: snowflakes.SnowflakeishOr[commands.PartialCommand],
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
     ) -> None:
         if guild is undefined.UNDEFINED:
@@ -3338,7 +3326,7 @@ async def fetch_application_command_permissions(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
         guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
-        command: snowflakes.SnowflakeishOr[commands.Command],
+        command: snowflakes.SnowflakeishOr[commands.PartialCommand],
     ) -> commands.GuildCommandPermissions:
         route = routes.GET_APPLICATION_COMMAND_PERMISSIONS.compile(
             application=application, guild=guild, command=command
@@ -3352,7 +3340,7 @@ async def set_application_guild_commands_permissions(
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
         guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
         permissions: typing.Mapping[
-            snowflakes.SnowflakeishOr[commands.Command], typing.Sequence[commands.CommandPermission]
+            snowflakes.SnowflakeishOr[commands.PartialCommand], typing.Sequence[commands.CommandPermission]
         ],
     ) -> typing.Sequence[commands.GuildCommandPermissions]:
         route = routes.PUT_APPLICATION_GUILD_COMMANDS_PERMISSIONS.compile(application=application, guild=guild)
@@ -3372,7 +3360,7 @@ async def set_application_command_permissions(
         self,
         application: snowflakes.SnowflakeishOr[guilds.PartialApplication],
         guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
-        command: snowflakes.SnowflakeishOr[commands.Command],
+        command: snowflakes.SnowflakeishOr[commands.PartialCommand],
         permissions: typing.Sequence[commands.CommandPermission],
     ) -> commands.GuildCommandPermissions:
         route = routes.PUT_APPLICATION_COMMAND_PERMISSIONS.compile(
@@ -3390,6 +3378,11 @@ def interaction_deferred_builder(
     ) -> special_endpoints.InteractionDeferredBuilder:
         return special_endpoints_impl.InteractionDeferredBuilder(type=type_)
 
+    def interaction_autocomplete_builder(
+        self, choices: typing.Sequence[commands.CommandChoice]
+    ) -> special_endpoints.InteractionAutocompleteBuilder:
+        return special_endpoints_impl.InteractionAutocompleteBuilder(choices)
+
     def interaction_message_builder(
         self, type_: typing.Union[base_interactions.ResponseType, int], /
     ) -> special_endpoints.InteractionMessageBuilder:
@@ -3408,10 +3401,12 @@ async def create_interaction_response(
         interaction: snowflakes.SnowflakeishOr[base_interactions.PartialInteraction],
         token: str,
         response_type: typing.Union[int, base_interactions.ResponseType],
-        content: undefined.UndefinedOr[typing.Any] = undefined.UNDEFINED,
+        content: undefined.UndefinedNoneOr[typing.Any] = undefined.UNDEFINED,
         *,
         flags: typing.Union[int, messages_.MessageFlag, undefined.UndefinedType] = undefined.UNDEFINED,
         tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+        attachment: undefined.UndefinedOr[files.Resourceish] = undefined.UNDEFINED,
+        attachments: undefined.UndefinedOr[typing.Sequence[files.Resourceish]] = undefined.UNDEFINED,
         component: undefined.UndefinedOr[special_endpoints.ComponentBuilder] = undefined.UNDEFINED,
         components: undefined.UndefinedOr[typing.Sequence[special_endpoints.ComponentBuilder]] = undefined.UNDEFINED,
         embed: undefined.UndefinedOr[embeds_.Embed] = undefined.UNDEFINED,
@@ -3424,69 +3419,31 @@ async def create_interaction_response(
             typing.Union[snowflakes.SnowflakeishSequence[guilds.PartialRole], bool]
         ] = undefined.UNDEFINED,
     ) -> None:
-        if not undefined.any_undefined(component, components):
-            raise ValueError("You may only specify one of 'component' or 'components', not both")
-
-        if not undefined.any_undefined(embed, embeds):
-            raise ValueError("You may only specify one of 'embed' or 'embeds', not both")
-
-        if components is not undefined.UNDEFINED and not isinstance(components, typing.Collection):
-            raise TypeError(
-                "You passed a non-collection to 'components', but this expects a collection. Maybe you meant to "
-                "use 'component' (singular) instead?"
-            )
-
-        if embeds not in _NONE_OR_UNDEFINED and not isinstance(embeds, typing.Collection):
-            raise TypeError(
-                "You passed a non-collection to 'embeds', but this expects a collection. Maybe you meant to "
-                "use 'embed' (singular) instead?"
-            )
-
-        if undefined.all_undefined(embed, embeds) and isinstance(content, embeds_.Embed):
-            # Syntactic sugar, common mistake to accidentally send an embed
-            # as the content, so lets detect this and fix it for the user.
-            embed = content
-            content = undefined.UNDEFINED
-
         route = routes.POST_INTERACTION_RESPONSE.compile(interaction=interaction, token=token)
 
+        data, form = self._build_message_payload(
+            content=content,
+            attachment=attachment,
+            attachments=attachments,
+            component=component,
+            components=components,
+            embed=embed,
+            embeds=embeds,
+            tts=tts,
+            flags=flags,
+            mentions_everyone=mentions_everyone,
+            user_mentions=user_mentions,
+            role_mentions=role_mentions,
+        )
         body = data_binding.JSONObjectBuilder()
         body.put("type", response_type)
-
-        data = data_binding.JSONObjectBuilder()
-        data.put("content", content, conversion=str)
-        data.put("flags", flags)
-        data.put("tts", tts)
-        data.put(
-            "allowed_mentions",
-            mentions.generate_allowed_mentions(mentions_everyone, undefined.UNDEFINED, user_mentions, role_mentions),
-        )
-
-        if component is not undefined.UNDEFINED:
-            data.put("components", [component.build()])
-
-        elif components is not undefined.UNDEFINED:
-            data.put("components", [component.build() for component in components])
-
-        if embed is not undefined.UNDEFINED:
-            embed_payload, attachments = self._entity_factory.serialize_embed(embed)
-            if attachments:
-                raise ValueError("Cannot send an embed with attachments in a slash command's initial response")
-
-            data.put("embeds", [embed_payload])
-
-        elif embeds is not undefined.UNDEFINED:
-            embed_payloads: data_binding.JSONArray = []
-            for embed in embeds:
-                serialized_embed, attachments = self._entity_factory.serialize_embed(embed)
-                embed_payloads.append(serialized_embed)
-                if attachments:
-                    raise ValueError("Cannot send an embed with attachments in a slash command's initial response")
-
-            data.put("embeds", embed_payloads)
-
         body.put("data", data)
-        await self._request(route, json=body, no_auth=True)
+
+        if form is not None:
+            form.add_field("payload_json", data_binding.dump_json(body), content_type=_APPLICATION_JSON)
+            await self._request(route, form_builder=form, no_auth=True)
+        else:
+            await self._request(route, json=body, no_auth=True)
 
     async def edit_interaction_response(
         self,
@@ -3512,10 +3469,8 @@ async def edit_interaction_response(
         ] = undefined.UNDEFINED,
     ) -> messages_.Message:
         route = routes.PATCH_INTERACTION_RESPONSE.compile(webhook=application, token=token)
-        return await self._edit_message(
-            route,
-            data_binding.JSONObjectBuilder(),
-            no_auth=True,
+
+        body, form = self._build_message_payload(
             content=content,
             attachment=attachment,
             attachments=attachments,
@@ -3527,14 +3482,40 @@ async def edit_interaction_response(
             mentions_everyone=mentions_everyone,
             user_mentions=user_mentions,
             role_mentions=role_mentions,
-            mentions_reply=undefined.UNDEFINED,
+            edit=True,
         )
 
+        if form is not None:
+            form.add_field("payload_json", data_binding.dump_json(body), content_type=_APPLICATION_JSON)
+            response = await self._request(route, form_builder=form, no_auth=True)
+        else:
+            response = await self._request(route, json=body, no_auth=True)
+
+        assert isinstance(response, dict)
+        return self._entity_factory.deserialize_message(response)
+
     async def delete_interaction_response(
         self, application: snowflakes.SnowflakeishOr[guilds.PartialApplication], token: str
     ) -> None:
         route = routes.DELETE_INTERACTION_RESPONSE.compile(webhook=application, token=token)
         await self._request(route, no_auth=True)
 
+    async def create_autocomplete_response(
+        self,
+        interaction: snowflakes.SnowflakeishOr[base_interactions.PartialInteraction],
+        token: str,
+        choices: typing.Sequence[commands.CommandChoice],
+    ) -> None:
+        route = routes.POST_INTERACTION_RESPONSE.compile(interaction=interaction, token=token)
+
+        body = data_binding.JSONObjectBuilder()
+        body.put("type", base_interactions.ResponseType.AUTOCOMPLETE)
+
+        data = data_binding.JSONObjectBuilder()
+        data.put("choices", [{"name": choice.name, "value": choice.value} for choice in choices])
+
+        body.put("data", data)
+        await self._request(route, json=body, no_auth=True)
+
     def build_action_row(self) -> special_endpoints.ActionRowBuilder:
         return special_endpoints_impl.ActionRowBuilder()
diff --git a/hikari/impl/rest_bot.py b/hikari/impl/rest_bot.py
index 4a26907a..428e6241 100644
--- a/hikari/impl/rest_bot.py
+++ b/hikari/impl/rest_bot.py
@@ -55,7 +55,8 @@
 
     _InteractionT_co = typing.TypeVar("_InteractionT_co", bound=base_interactions.PartialInteraction, covariant=True)
     _MessageResponseBuilderT = typing.Union[
-        special_endpoints.InteractionDeferredBuilder, special_endpoints.InteractionMessageBuilder
+        special_endpoints.InteractionDeferredBuilder,
+        special_endpoints.InteractionMessageBuilder,
     ]
 
 _LOGGER: typing.Final[logging.Logger] = logging.getLogger("hikari.rest_bot")
@@ -587,6 +588,16 @@ def get_listener(
     ]:
         ...
 
+    @typing.overload
+    def get_listener(
+        self, interaction_type: typing.Type[command_interactions.AutocompleteInteraction], /
+    ) -> typing.Optional[
+        interaction_server_.ListenerT[
+            command_interactions.AutocompleteInteraction, special_endpoints.InteractionAutocompleteBuilder
+        ]
+    ]:
+        ...
+
     @typing.overload
     def get_listener(
         self, interaction_type: typing.Type[_InteractionT_co], /
@@ -624,6 +635,21 @@ def set_listener(
     ) -> None:
         ...
 
+    @typing.overload
+    def set_listener(
+        self,
+        interaction_type: typing.Type[command_interactions.AutocompleteInteraction],
+        listener: typing.Optional[
+            interaction_server_.ListenerT[
+                command_interactions.AutocompleteInteraction, special_endpoints.InteractionAutocompleteBuilder
+            ]
+        ],
+        /,
+        *,
+        replace: bool = False,
+    ) -> None:
+        ...
+
     def set_listener(
         self,
         interaction_type: typing.Type[_InteractionT_co],
diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py
index 669cda6f..7f1f32d7 100644
--- a/hikari/impl/special_endpoints.py
+++ b/hikari/impl/special_endpoints.py
@@ -29,8 +29,11 @@
 __all__: typing.List[str] = [
     "ActionRowBuilder",
     "CommandBuilder",
+    "SlashCommandBuilder",
+    "ContextMenuCommandBuilder",
     "TypingIndicator",
     "GuildBuilder",
+    "InteractionAutocompleteBuilder",
     "InteractionDeferredBuilder",
     "InteractionMessageBuilder",
     "InteractiveButtonBuilder",
@@ -76,8 +79,13 @@
 
     _T = typing.TypeVar("_T")
     _CommandBuilderT = typing.TypeVar("_CommandBuilderT", bound="CommandBuilder")
+    _SlashCommandBuilderT = typing.TypeVar("_SlashCommandBuilderT", bound="SlashCommandBuilder")
+    _ContextMenuCommandBuilderT = typing.TypeVar("_ContextMenuCommandBuilderT", bound="ContextMenuCommandBuilder")
     _InteractionMessageBuilderT = typing.TypeVar("_InteractionMessageBuilderT", bound="InteractionMessageBuilder")
     _InteractionDeferredBuilderT = typing.TypeVar("_InteractionDeferredBuilderT", bound="InteractionDeferredBuilder")
+    _InteractionAutocompleteBuilderT = typing.TypeVar(
+        "_InteractionAutocompleteBuilderT", bound="InteractionAutocompleteBuilder"
+    )
     _ActionRowBuilderT = typing.TypeVar("_ActionRowBuilderT", bound="ActionRowBuilder")
     _ButtonBuilderT = typing.TypeVar("_ButtonBuilderT", bound="_ButtonBuilder[typing.Any]")
     _SelectOptionBuilderT = typing.TypeVar("_SelectOptionBuilderT", bound="_SelectOptionBuilder[typing.Any]")
@@ -693,6 +701,39 @@ async def __anext__(self) -> audit_logs.AuditLog:
         return log
 
 
+@attr_extensions.with_copy
+@attr.define(kw_only=False, weakref_slot=False)
+class InteractionAutocompleteBuilder(special_endpoints.InteractionAutocompleteBuilder):
+    """Standard implementation of `hikari.api.special_endpoints.InteractionAutocompleteBuilder`."""
+
+    _choices: typing.Sequence[commands.CommandChoice] = attr.field(factory=list)
+
+    @property
+    def type(self) -> typing.Literal[base_interactions.ResponseType.AUTOCOMPLETE]:
+        return base_interactions.ResponseType.AUTOCOMPLETE
+
+    @property
+    def choices(self) -> typing.Sequence[commands.CommandChoice]:
+        return self._choices
+
+    def set_choices(
+        self: _InteractionAutocompleteBuilderT, choices: typing.Sequence[commands.CommandChoice], /
+    ) -> _InteractionAutocompleteBuilderT:
+        """Set autocomplete choices.
+
+        Returns
+        -------
+        InteractionAutocompleteBuilder
+            Object of this builder.
+        """
+        self._choices = choices
+        return self
+
+    def build(self, _: entity_factory_.EntityFactory, /) -> data_binding.JSONObject:
+        data = {"choices": [{"name": choice.name, "value": choice.value} for choice in self._choices]}
+        return {"type": self.type, "data": data}
+
+
 @attr_extensions.with_copy
 @attr.define(kw_only=False, weakref_slot=False)
 class InteractionDeferredBuilder(special_endpoints.InteractionDeferredBuilder):
@@ -898,41 +939,33 @@ def build(self, entity_factory: entity_factory_.EntityFactory, /) -> data_bindin
 class CommandBuilder(special_endpoints.CommandBuilder):
     """Standard implementation of `hikari.api.special_endpoints.CommandBuilder`."""
 
-    # Required arguments.
+    _type: commands.CommandType = attr.field()
     _name: str = attr.field()
-    _description: str = attr.field()
 
-    # Key-word only not-required arguments.
+    _description: undefined.UndefinedOr[str] = attr.field(default=undefined.UNDEFINED, kw_only=True)
     _id: undefined.UndefinedOr[snowflakes.Snowflake] = attr.field(default=undefined.UNDEFINED, kw_only=True)
     _default_permission: undefined.UndefinedOr[bool] = attr.field(default=undefined.UNDEFINED, kw_only=True)
 
-    # Non-arguments.
-    _options: typing.List[commands.CommandOption] = attr.field(factory=list)
-
-    @property
-    def description(self) -> str:
-        return self._description
+    _options: undefined.UndefinedOr[typing.List[commands.CommandOption]] = attr.field(
+        default=undefined.UNDEFINED, kw_only=True
+    )
 
     @property
     def id(self) -> undefined.UndefinedOr[snowflakes.Snowflake]:
         return self._id
 
     @property
-    def default_permission(self) -> undefined.UndefinedOr[bool]:
-        return self._default_permission
+    def type(self) -> undefined.UndefinedOr[commands.CommandType]:
+        return self._type
 
     @property
-    def options(self) -> typing.Sequence[commands.CommandOption]:
-        return self._options.copy()
+    def default_permission(self) -> undefined.UndefinedOr[bool]:
+        return self._default_permission
 
     @property
     def name(self) -> str:
         return self._name
 
-    def add_option(self: _CommandBuilderT, option: commands.CommandOption) -> _CommandBuilderT:
-        self._options.append(option)
-        return self
-
     def set_id(self: _CommandBuilderT, id_: undefined.UndefinedOr[snowflakes.Snowflakeish], /) -> _CommandBuilderT:
         self._id = snowflakes.Snowflake(id_) if id_ is not undefined.UNDEFINED else undefined.UNDEFINED
         return self
@@ -941,16 +974,53 @@ def set_default_permission(self: _CommandBuilderT, state: undefined.UndefinedOr[
         self._default_permission = state
         return self
 
-    def build(self, entity_factory: entity_factory_.EntityFactory, /) -> data_binding.JSONObject:
+    def build(self, entity_factory: entity_factory_.EntityFactory, /) -> data_binding.JSONObjectBuilder:
         data = data_binding.JSONObjectBuilder()
         data["name"] = self._name
-        data["description"] = self._description
-        data.put_array("options", self._options, conversion=entity_factory.serialize_command_option)
         data.put_snowflake("id", self._id)
         data.put("default_permission", self._default_permission)
         return data
 
 
+@attr.define(kw_only=False, weakref_slot=False)
+class SlashCommandBuilder(CommandBuilder, special_endpoints.SlashCommandBuilder):
+    """Builder class for slash commands."""
+
+    _description: str = attr.field()
+    _options: typing.List[commands.CommandOption] = attr.field(factory=list, kw_only=True)
+
+    _type: commands.CommandType = attr.field(default=commands.CommandType.SLASH, init=False)
+
+    @property
+    def description(self) -> str:
+        return self._description
+
+    def add_option(self: _SlashCommandBuilderT, option: commands.CommandOption) -> _SlashCommandBuilderT:
+        self._options.append(option)
+        return self
+
+    @property
+    def options(self) -> typing.Sequence[commands.CommandOption]:
+        return self._options.copy()
+
+    def build(self, entity_factory: entity_factory_.EntityFactory, /) -> data_binding.JSONObjectBuilder:
+        data = super().build(entity_factory)
+        data["type"] = commands.CommandType.SLASH
+        data.put("description", self._description)
+        data.put_array("options", self._options, conversion=entity_factory.serialize_command_option)
+        return data
+
+
+@attr.define(kw_only=False, weakref_slot=False)
+class ContextMenuCommandBuilder(CommandBuilder, special_endpoints.ContextMenuCommandBuilder):
+    """Builder class for context menu commands."""
+
+    def build(self, entity_factory: entity_factory_.EntityFactory, /) -> data_binding.JSONObjectBuilder:
+        data = super().build(entity_factory)
+        data["type"] = self._type
+        return data
+
+
 def _build_emoji(
     emoji: typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, undefined.UndefinedType] = undefined.UNDEFINED
 ) -> typing.Tuple[undefined.UndefinedOr[str], undefined.UndefinedOr[str]]:
diff --git a/hikari/interactions/base_interactions.py b/hikari/interactions/base_interactions.py
index 2ee3b834..019547ad 100644
--- a/hikari/interactions/base_interactions.py
+++ b/hikari/interactions/base_interactions.py
@@ -71,6 +71,9 @@ class InteractionType(int, enums.Enum):
     MESSAGE_COMPONENT = 3
     """An interaction triggered by a user calling a message component."""
 
+    AUTOCOMPLETE = 4
+    """An interaction triggered by a user typing in a slash command option."""
+
 
 @typing.final
 class ResponseType(int, enums.Enum):
@@ -115,6 +118,14 @@ class ResponseType(int, enums.Enum):
     * `InteractionType.MESSAGE_COMPONENT`
     """
 
+    AUTOCOMPLETE = 8
+    """Respond to an autocomplete interaction with suggested choices.
+
+    This is valid for the following interaction types:
+
+    * `InteractionType.AUTOCOMPLETE`
+    """
+
 
 MESSAGE_RESPONSE_TYPES: typing.Final[typing.AbstractSet[MessageResponseTypesT]] = frozenset(
     [ResponseType.MESSAGE_CREATE, ResponseType.MESSAGE_UPDATE]
@@ -233,6 +244,8 @@ async def create_initial_response(
         *,
         flags: typing.Union[int, messages.MessageFlag, undefined.UndefinedType] = undefined.UNDEFINED,
         tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+        attachment: undefined.UndefinedOr[files.Resourceish] = undefined.UNDEFINED,
+        attachments: undefined.UndefinedOr[typing.Sequence[files.Resourceish]] = undefined.UNDEFINED,
         component: undefined.UndefinedOr[special_endpoints.ComponentBuilder] = undefined.UNDEFINED,
         components: undefined.UndefinedOr[typing.Sequence[special_endpoints.ComponentBuilder]] = undefined.UNDEFINED,
         embed: undefined.UndefinedOr[embeds_.Embed] = undefined.UNDEFINED,
@@ -269,6 +282,12 @@ async def create_initial_response(
             If this is a `hikari.embeds.Embed` and no `embed` nor `embeds` kwarg
             is provided, then this will instead update the embed. This allows
             for simpler syntax when sending an embed alone.
+        attachment : hikari.undefined.UndefinedOr[hikari.files.Resourceish],
+            If provided, the message attachment. This can be a resource,
+            or string of a path on your computer or a URL.
+        attachments : hikari.undefined.UndefinedOr[typing.Sequence[hikari.files.Resourceish]],
+            If provided, the message attachments. These can be resources, or
+            strings consisting of paths on your computer or URLs.
         component : hikari.undefined.UndefinedOr[hikari.api.special_endpoints.ComponentBuilder]
             If provided, builder object of the component to include in this message.
         components : hikari.undefined.UndefinedOr[typing.Sequence[hikari.api.special_endpoints.ComponentBuilder]]
@@ -343,6 +362,8 @@ async def create_initial_response(
             response_type,
             content,
             tts=tts,
+            attachment=attachment,
+            attachments=attachments,
             component=component,
             components=components,
             embed=embed,
diff --git a/hikari/interactions/command_interactions.py b/hikari/interactions/command_interactions.py
index aeff3784..7f7faeee 100644
--- a/hikari/interactions/command_interactions.py
+++ b/hikari/interactions/command_interactions.py
@@ -24,7 +24,10 @@
 from __future__ import annotations
 
 __all__: typing.List[str] = [
+    "AutocompleteInteraction",
+    "BaseCommandInteraction",
     "CommandInteractionOption",
+    "AutocompleteInteractionOption",
     "CommandInteraction",
     "COMMAND_RESPONSE_TYPES",
     "CommandResponseTypesT",
@@ -46,6 +49,7 @@
 
 if typing.TYPE_CHECKING:
     from hikari import guilds
+    from hikari import messages as messages_
     from hikari import permissions as permissions_
     from hikari import users as users_
     from hikari.api import special_endpoints
@@ -100,6 +104,9 @@ class ResolvedOptionData:
     channels: typing.Mapping[snowflakes.Snowflake, InteractionChannel] = attr.field(repr=False)
     """Mapping of snowflake iDs to the resolved option partial channel objects."""
 
+    messages: typing.Mapping[snowflakes.Snowflake, messages_.Message]
+    """Mapping of snowflake iDs to the resolved option partial message objects."""
+
 
 @attr_extensions.with_copy
 @attr.define(hash=False, kw_only=True, weakref_slot=False)
@@ -131,10 +138,26 @@ class CommandInteractionOption:
     """
 
 
+@attr_extensions.with_copy
+@attr.define(hash=False, kw_only=True, weakref_slot=False)
+class AutocompleteInteractionOption(CommandInteractionOption):
+    """Represents the options passed for a command autocomplete interaction."""
+
+    is_focused: bool = attr.field(default=False, repr=True)
+    """Whether this option is the currently focused option for autocomplete.
+
+    Focused options are not guaranteed to be parsed so the value may be a string
+    even if the option type says otherwise.
+    """
+
+
 @attr_extensions.with_copy
 @attr.define(hash=True, kw_only=True, weakref_slot=False)
-class CommandInteraction(base_interactions.MessageResponseMixin[CommandResponseTypesT]):
-    """Represents a command interaction on Discord."""
+class BaseCommandInteraction(base_interactions.PartialInteraction):
+    """Represents a base command interaction on Discord.
+
+    May be a command interaction or an autocomplete interaction.
+    """
 
     channel_id: snowflakes.Snowflake = attr.field(eq=False, hash=False, repr=True)
     """ID of the channel this command interaction event was triggered in."""
@@ -177,59 +200,12 @@ class CommandInteraction(base_interactions.MessageResponseMixin[CommandResponseT
     command_name: str = attr.field(eq=False, hash=False, repr=True)
     """Name of the command being invoked."""
 
-    options: typing.Optional[typing.Sequence[CommandInteractionOption]] = attr.field(eq=False, hash=False, repr=True)
-    """Parameter values provided by the user invoking this command."""
+    command_type: typing.Union[commands.CommandType, int] = attr.field(eq=False, hash=False, repr=True)
+    """The type of the command."""
 
     resolved: typing.Optional[ResolvedOptionData] = attr.field(eq=False, hash=False, repr=False)
     """Mappings of the objects resolved for the provided command options."""
 
-    def build_response(self) -> special_endpoints.InteractionMessageBuilder:
-        """Get a message response builder for use in the REST server flow.
-
-        !!! note
-            For interactions received over the gateway
-            `CommandInteraction.create_initial_response` should be used to set
-            the interaction response message.
-
-        Examples
-        --------
-        ```py
-        async def handle_command_interaction(interaction: CommandInteraction) -> InteractionMessageBuilder:
-            return (
-                interaction
-                .build_response()
-                .add_embed(Embed(description="Hi there"))
-                .set_content("Konnichiwa")
-            )
-        ```
-
-        Returns
-        -------
-        hikari.api.special_endpoints.InteractionMessageBuilder
-            Interaction message response builder object.
-        """
-        return self.app.rest.interaction_message_builder(base_interactions.ResponseType.MESSAGE_CREATE)
-
-    def build_deferred_response(self) -> special_endpoints.InteractionDeferredBuilder:
-        """Get a deferred message response builder for use in the REST server flow.
-
-        !!! note
-            For interactions received over the gateway
-            `CommandInteraction.create_initial_response` should be used to set
-            the interaction response message.
-
-        !!! note
-            Unlike `hikari.api.special_endpoints.InteractionMessageBuilder`,
-            the result of this call can be returned as is without any modifications
-            being made to it.
-
-        Returns
-        -------
-        hikari.api.special_endpoints.InteractionMessageBuilder
-            Deferred interaction message response builder object.
-        """
-        return self.app.rest.interaction_deferred_builder(base_interactions.ResponseType.DEFERRED_MESSAGE_CREATE)
-
     async def fetch_channel(self) -> channels.TextableChannel:
         """Fetch the guild channel this was triggered in.
 
@@ -288,12 +264,12 @@ def get_channel(self) -> typing.Optional[channels.TextableGuildChannel]:
 
         return None
 
-    async def fetch_command(self) -> commands.Command:
+    async def fetch_command(self) -> commands.PartialCommand:
         """Fetch the command which triggered this interaction.
 
         Returns
         -------
-        hikari.commands.Command
+        hikari.commands.PartialCommand
             Object of this interaction's command.
 
         Raises
@@ -370,3 +346,114 @@ def get_guild(self) -> typing.Optional[guilds.GatewayGuild]:
             return self.app.cache.get_guild(self.guild_id)
 
         return None
+
+
+@attr_extensions.with_copy
+@attr.define(hash=True, kw_only=True, weakref_slot=False)
+class CommandInteraction(BaseCommandInteraction, base_interactions.MessageResponseMixin[CommandResponseTypesT]):
+    """Represents a command interaction on Discord."""
+
+    options: typing.Optional[typing.Sequence[CommandInteractionOption]] = attr.field(eq=False, hash=False, repr=True)
+    """Parameter values provided by the user invoking this command."""
+
+    target_id: typing.Optional[snowflakes.Snowflake] = attr.field(default=None, eq=False, hash=False, repr=True)
+    """The target of the command. Only available if the command is a context menu command."""
+
+    def build_response(self) -> special_endpoints.InteractionMessageBuilder:
+        """Get a message response builder for use in the REST server flow.
+
+        !!! note
+            For interactions received over the gateway
+            `CommandInteraction.create_initial_response` should be used to set
+            the interaction response message.
+
+        Examples
+        --------
+        ```py
+        async def handle_command_interaction(interaction: CommandInteraction) -> InteractionMessageBuilder:
+            return (
+                interaction
+                .build_response()
+                .add_embed(Embed(description="Hi there"))
+                .set_content("Konnichiwa")
+            )
+        ```
+
+        Returns
+        -------
+        hikari.api.special_endpoints.InteractionMessageBuilder
+            Interaction message response builder object.
+        """
+        return self.app.rest.interaction_message_builder(base_interactions.ResponseType.MESSAGE_CREATE)
+
+    def build_deferred_response(self) -> special_endpoints.InteractionDeferredBuilder:
+        """Get a deferred message response builder for use in the REST server flow.
+
+        !!! note
+            For interactions received over the gateway
+            `CommandInteraction.create_initial_response` should be used to set
+            the interaction response message.
+
+        !!! note
+            Unlike `hikari.api.special_endpoints.InteractionMessageBuilder`,
+            the result of this call can be returned as is without any modifications
+            being made to it.
+
+        Returns
+        -------
+        hikari.api.special_endpoints.InteractionMessageBuilder
+            Deferred interaction message response builder object.
+        """
+        return self.app.rest.interaction_deferred_builder(base_interactions.ResponseType.DEFERRED_MESSAGE_CREATE)
+
+
+@attr_extensions.with_copy
+@attr.define(hash=True, kw_only=True, weakref_slot=False)
+class AutocompleteInteraction(BaseCommandInteraction):
+    """Represents an autocomplete interaction on Discord."""
+
+    options: typing.Optional[typing.Sequence[AutocompleteInteractionOption]] = attr.field(
+        eq=False, hash=False, repr=True
+    )
+    """Parameter values provided by the user invoking this command."""
+
+    def build_response(
+        self, choices: typing.Sequence[commands.CommandChoice]
+    ) -> special_endpoints.InteractionAutocompleteBuilder:
+        """Get a message response builder for use in the REST server flow.
+
+        !!! note
+            For interactions received over the gateway
+            `AutocompleteInteraction.create_response` should be used to set
+            the interaction response.
+
+        Examples
+        --------
+        ```py
+        async def handle_autocomplete_interaction(interaction: AutocompleteInteraction) -> InteractionAutocompleteBuilder:
+            return (
+                interaction
+                .build_response(
+                    [
+                        CommandChoice(name="foo", value="a"),
+                        CommandChoice(name="bar", value="b"),
+                        CommandChoice(name="baz", value="c"),
+                    ]
+                )
+            )
+        ```
+
+        Returns
+        -------
+        hikari.api.special_endpoints.InteractionAutocompleteBuilder
+            Interaction autocomplete response builder object.
+        """
+        return self.app.rest.interaction_autocomplete_builder(choices)
+
+    async def create_response(self, choices: typing.Sequence[commands.CommandChoice]) -> None:
+        """Create a response for this autocomplete interaction."""
+        await self.app.rest.create_autocomplete_response(
+            self.id,
+            self.token,
+            choices,
+        )
diff --git a/hikari/interactions/component_interactions.py b/hikari/interactions/component_interactions.py
index f94bbe9e..a0e2ffa2 100644
--- a/hikari/interactions/component_interactions.py
+++ b/hikari/interactions/component_interactions.py
@@ -55,8 +55,7 @@
 ]
 
 
-# This type ignore accounts for a regression introduced to MyPy in v0.900
-COMPONENT_RESPONSE_TYPES: typing.Final[typing.AbstractSet[ComponentResponseTypesT]] = frozenset(  # type: ignore[assignment]
+COMPONENT_RESPONSE_TYPES: typing.Final[typing.AbstractSet[ComponentResponseTypesT]] = frozenset(
     [*_DEFERRED_TYPES, *_IMMEDIATE_TYPES]
 )
 """Set of the response types which are valid for a component interaction.


Test patch:
diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py
index 7d38b66c..7469617c 100644
--- a/tests/hikari/impl/test_entity_factory.py
+++ b/tests/hikari/impl/test_entity_factory.py
@@ -2718,6 +2718,7 @@ def command_payload(self):
             "id": "1231231231",
             "application_id": "12354123",
             "guild_id": "49949494",
+            "type": 1,
             "name": "good name",
             "description": "very good description",
             "default_permission": False,
@@ -2790,12 +2791,13 @@ def test_deserialize_command(self, entity_factory_impl, mock_app, command_payloa
 
         assert isinstance(suboption, commands.CommandOption)
         assert isinstance(option, commands.CommandOption)
-        assert isinstance(command, commands.Command)
+        assert isinstance(command, commands.SlashCommand)
 
     def test_deserialize_command_with_passed_through_guild_id(self, entity_factory_impl):
         payload = {
             "id": "1231231231",
             "application_id": "12354123",
+            "type": 1,
             "name": "good name",
             "description": "very good description",
             "options": [],
@@ -2811,6 +2813,7 @@ def test_deserialize_command_with_null_and_unset_values(self, entity_factory_imp
             "id": "1231231231",
             "application_id": "12354123",
             "guild_id": "49949494",
+            "type": 1,
             "name": "good name",
             "description": "very good description",
             "options": [],
@@ -2821,7 +2824,7 @@ def test_deserialize_command_with_null_and_unset_values(self, entity_factory_imp
 
         assert command.options is None
         assert command.default_permission is True
-        assert isinstance(command, commands.Command)
+        assert isinstance(command, commands.SlashCommand)
 
     @pytest.fixture()
     def guild_command_permissions_payload(self):
@@ -2966,7 +2969,9 @@ def test__deserialize_interaction_member_with_passed_user(
         assert member.user is mock_user
 
     @pytest.fixture()
-    def command_interaction_payload(self, member_payload, user_payload, guild_role_payload, interaction_member_payload):
+    def command_interaction_payload(
+        self, member_payload, user_payload, guild_role_payload, interaction_member_payload, message_payload
+    ):
         member_payload = member_payload.copy()
         member_payload["permissions"] = "47"
         del member_payload["user"]
@@ -2977,6 +2982,7 @@ def command_interaction_payload(self, member_payload, user_payload, guild_role_p
             "data": {
                 "id": "43123123",
                 "name": "okokokok",
+                "type": 1,
                 "options": [
                     {
                         "name": "an option",
@@ -2999,6 +3005,7 @@ def command_interaction_payload(self, member_payload, user_payload, guild_role_p
                     "members": {"115590097100865541": member_payload},
                     "roles": {"41771983423143936": guild_role_payload},
                     "users": {"115590097100865541": user_payload},
+                    "messages": {"123": message_payload},
                 },
             },
             "channel_id": "49949494",
@@ -3019,6 +3026,7 @@ def test_deserialize_command_interaction(
         guild_role_payload,
         interaction_member_payload,
         member_payload,
+        message_payload,
     ):
         interaction = entity_factory_impl.deserialize_command_interaction(command_interaction_payload)
         member_payload = member_payload.copy()
@@ -3085,6 +3093,8 @@ def test_deserialize_command_interaction(
             41771983423143936: entity_factory_impl.deserialize_role(guild_role_payload, guild_id=43123123)
         }
         assert interaction.resolved.users == {115590097100865541: entity_factory_impl.deserialize_user(user_payload)}
+        assert interaction.resolved.messages == {123: entity_factory_impl.deserialize_message(message_payload)}
+
         assert isinstance(interaction.resolved, command_interactions.ResolvedOptionData)
 
         assert isinstance(interaction, command_interactions.CommandInteraction)
@@ -3124,6 +3134,101 @@ def test_deserialize_command_interaction_with_empty_resolved_resources(
         assert interaction.resolved.roles == {}
         assert interaction.resolved.users == {}
 
+    @pytest.fixture()
+    def autocomplete_interaction_payload(self, user_payload):
+        return {
+            "id": "3490190239012093",
+            "type": 4,
+            "guild_id": "43123123",
+            "data": {
+                "id": "43123123",
+                "name": "okokokok",
+                "type": 1,
+                "options": [
+                    {"name": "go ice", "type": 4, "value": 42},
+                    {"name": "go fire", "type": 3, "value": "typing...", "focused": True},
+                ],
+                "resolved": {},
+            },
+            "channel_id": "49949494",
+            "user": user_payload,
+            "token": "moe cat girls",
+            "locale": "es-ES",
+            "guild_locale": "en-US",
+            "version": 69420,
+            "application_id": "76234234",
+        }
+
+    @pytest.fixture()
+    def autocomplete_interaction_payload_with_optional(
+        self,
+        interaction_member_payload,
+        user_payload,
+    ):
+        return {
+            "id": "3490190239012093",
+            "type": 4,
+            "guild_id": "43123123",
+            "data": {
+                "id": "43123123",
+                "name": "okokokok",
+                "type": 1,
+                "options": [
+                    {"name": "go ice", "type": 6, "value": "115590097100865541"},
+                    {"name": "go fire", "type": 3, "value": "typing...", "focused": True},
+                    {"name": "stuff", "type": 1, "value": {"name": "stuff", "type": 3, "value": "none"}},
+                ],
+                "resolved": {
+                    "users": {
+                        "115590097100865541": user_payload,
+                    }
+                },
+            },
+            "channel_id": "49949494",
+            "member": interaction_member_payload,
+            "token": "moe cat girls",
+            "locale": "es-ES",
+            "guild_locale": "en-US",
+            "version": 69420,
+            "application_id": "76234234",
+        }
+
+    def test_deserialize_autocomplete_interaction(
+        self,
+        entity_factory_impl,
+        mock_app,
+        autocomplete_interaction_payload,
+    ):
+        interaction = entity_factory_impl.deserialize_autocomplete_interaction(autocomplete_interaction_payload)
+
+        assert interaction.app is mock_app
+        assert interaction.application_id == 76234234
+        assert interaction.id == 3490190239012093
+        assert interaction.type is base_interactions.InteractionType.AUTOCOMPLETE
+        assert interaction.token == "moe cat girls"
+        assert interaction.version == 69420
+        assert interaction.channel_id == 49949494
+        assert interaction.guild_id == 43123123
+        assert interaction.locale == "es-ES"
+        assert interaction.guild_locale == "en-US"
+
+        assert len(interaction.options) == 2
+        assert interaction.options[0].value == 42
+        assert interaction.options[1].value == "typing..."
+        assert interaction.options[1].is_focused
+
+    def test_deserialize_autocomplete_interaction_with_optional(
+        self,
+        entity_factory_impl,
+        mock_app,
+        autocomplete_interaction_payload_with_optional,
+    ):
+        interaction = entity_factory_impl.deserialize_autocomplete_interaction(
+            autocomplete_interaction_payload_with_optional
+        )
+
+        assert interaction
+
     def test_deserialize_interaction_returns_expected_type(
         self, entity_factory_impl, command_interaction_payload, component_interaction_payload
     ):
@@ -3234,6 +3339,99 @@ def test_serialize_command_option_with_options(self, entity_factory_impl):
             ],
         }
 
+    def test_serialize_command_option_with_autocomplete(self, entity_factory_impl):
+        option = commands.CommandOption(
+            type=commands.OptionType.STRING,
+            name="a name",
+            description="go away",
+            is_required=True,
+            autocomplete=True,
+        )
+
+        result = entity_factory_impl.serialize_command_option(option)
+
+        assert result == {
+            "type": 3,
+            "name": "a name",
+            "description": "go away",
+            "required": True,
+            "autocomplete": True,
+        }
+
+    @pytest.fixture()
+    def context_menu_command_interaction_payload(self, interaction_member_payload, user_payload):
+        return {
+            "id": "3490190239012093",
+            "type": 4,
+            "guild_id": "43123123",
+            "data": {
+                "id": "43123123",
+                "name": "okokokok",
+                "type": 2,
+                "target_id": "115590097100865541",
+                "resolved": {
+                    "users": {
+                        "115590097100865541": user_payload,
+                    }
+                },
+            },
+            "channel_id": "49949494",
+            "member": interaction_member_payload,
+            "token": "moe cat girls",
+            "locale": "es-ES",
+            "guild_locale": "en-US",
+            "version": 69420,
+            "application_id": "76234234",
+        }
+
+    def test_deserialize_context_menu_command_interaction(
+        self,
+        entity_factory_impl,
+        context_menu_command_interaction_payload,
+    ):
+        interaction = entity_factory_impl.deserialize_command_interaction(context_menu_command_interaction_payload)
+        assert isinstance(interaction, command_interactions.CommandInteraction)
+        assert interaction.target_id == 115590097100865541
+
+    @pytest.fixture()
+    def context_menu_command_payload(self):
+        return {
+            "id": "1231231231",
+            "application_id": "12354123",
+            "guild_id": "49949494",
+            "type": 2,
+            "name": "good name",
+            "default_permission": False,
+            "version": "123321123",
+        }
+
+    def test_deserialize_context_menu_command(
+        self,
+        entity_factory_impl,
+        context_menu_command_payload,
+    ):
+        command = entity_factory_impl.deserialize_command(context_menu_command_payload)
+        assert isinstance(command, commands.ContextMenuCommand)
+
+        assert command.id == 1231231231
+        assert command.application_id == 12354123
+        assert command.guild_id == 49949494
+        assert command.type == commands.CommandType.USER
+        assert command.name == "good name"
+        assert command.default_permission is False
+        assert command.version == 123321123
+
+    def test_unknown_command_type(
+        self,
+        entity_factory_impl,
+        command_payload,
+    ):
+        payload = command_payload.copy()
+        payload["type"] = 4
+
+        with pytest.raises(errors.UnrecognisedEntityError):
+            entity_factory_impl.deserialize_command(payload)
+
     @pytest.fixture()
     def component_interaction_payload(self, interaction_member_payload, message_payload):
         return {
diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py
index 7f6a67cc..e948badc 100644
--- a/tests/hikari/impl/test_rest.py
+++ b/tests/hikari/impl/test_rest.py
@@ -33,7 +33,9 @@
 from hikari import audit_logs
 from hikari import channels
 from hikari import colors
+from hikari import commands
 from hikari import config
+from hikari import embeds
 from hikari import emojis
 from hikari import errors
 from hikari import files
@@ -52,6 +54,7 @@
 from hikari.impl import rest
 from hikari.impl import special_endpoints
 from hikari.internal import data_binding
+from hikari.internal import mentions
 from hikari.internal import net
 from hikari.internal import routes
 from hikari.internal import time
@@ -474,7 +477,7 @@ def rest_client(rest_client_class, live_attributes, mock_cache):
         token_type="tYpe",
         max_retries=0,
         rest_url="https://some.where/api/v3",
-        executor=mock.Mock(),
+        executor=object(),
         entity_factory=mock.Mock(),
     )
     obj._live_attributes = live_attributes
@@ -1003,7 +1006,7 @@ def test_fetch_members(self, rest_client):
                 guild=guild,
             )
 
-    def test_kick_member(self, rest_client: rest.RESTClientImpl):
+    def test_kick_member(self, rest_client):
         mock_kick_user = mock.Mock()
         rest_client.kick_user = mock_kick_user
 
@@ -1012,7 +1015,7 @@ def test_kick_member(self, rest_client: rest.RESTClientImpl):
         assert result is mock_kick_user.return_value
         mock_kick_user.assert_called_once_with(123, 5423, reason="oewkwkwk")
 
-    def test_ban_member(self, rest_client: rest.RESTClientImpl):
+    def test_ban_member(self, rest_client):
         mock_ban_user = mock.Mock()
         rest_client.ban_user = mock_ban_user
 
@@ -1021,7 +1024,7 @@ def test_ban_member(self, rest_client: rest.RESTClientImpl):
         assert result is mock_ban_user.return_value
         mock_ban_user.assert_called_once_with(43123, 54123, delete_message_days=6, reason="wowowowo")
 
-    def test_unban_member(self, rest_client: rest.RESTClientImpl):
+    def test_unban_member(self, rest_client):
         mock_unban_user = mock.Mock()
         rest_client.unban_user = mock_unban_user
 
@@ -1031,12 +1034,23 @@ def test_unban_member(self, rest_client: rest.RESTClientImpl):
         mock_unban_user.assert_called_once_with(123, 321, reason="ayaya")
 
     def test_command_builder(self, rest_client):
-        result = rest_client.command_builder("a name", "very very good")
+        with warnings.catch_warnings():
+            warnings.simplefilter("ignore", category=DeprecationWarning)
+            result = rest_client.command_builder("a name", description="very very good")
 
-        assert isinstance(result, special_endpoints.CommandBuilder)
+        assert isinstance(result, special_endpoints.SlashCommandBuilder)
         assert result.name == "a name"
         assert result.description == "very very good"
 
+    def test_slash_command_builder(self, rest_client):
+        result = rest_client.slash_command_builder("a name", "a description")
+        assert isinstance(result, special_endpoints.SlashCommandBuilder)
+
+    def test_context_menu_command_command_builder(self, rest_client):
+        result = rest_client.context_menu_command_builder(3, "a name")
+        assert isinstance(result, special_endpoints.ContextMenuCommandBuilder)
+        assert result.type == commands.CommandType.MESSAGE
+
     def test_build_action_row(self, rest_client):
         with mock.patch.object(special_endpoints, "ActionRowBuilder") as action_row_builder:
             assert rest_client.build_action_row() is action_row_builder.return_value
@@ -1882,78 +1896,351 @@ async def test_fetch_message(self, rest_client):
         rest_client._request.assert_awaited_once_with(expected_route)
         rest_client._entity_factory.deserialize_message.assert_called_once_with({"id": "456"})
 
+    def test__build_message_payload_with_undefined_args(self, rest_client):
+        with mock.patch.object(
+            mentions, "generate_allowed_mentions", return_value={"allowed_mentions": 1}
+        ) as generate_allowed_mentions:
+            body, form = rest_client._build_message_payload()
+
+        assert body == {"allowed_mentions": {"allowed_mentions": 1}}
+        assert form is None
+
+        generate_allowed_mentions.assert_called_once_with(
+            undefined.UNDEFINED, undefined.UNDEFINED, undefined.UNDEFINED, undefined.UNDEFINED
+        )
+
+    @pytest.mark.parametrize("args", [("embeds", "components"), ("embed", "component")])
+    def test__build_message_payload_with_None_args(self, rest_client, args):
+        kwargs = {}
+        for arg in args:
+            kwargs[arg] = None
+
+        with mock.patch.object(
+            mentions, "generate_allowed_mentions", return_value={"allowed_mentions": 1}
+        ) as generate_allowed_mentions:
+            body, form = rest_client._build_message_payload(**kwargs)
+
+        assert body == {"embeds": [], "components": [], "allowed_mentions": {"allowed_mentions": 1}}
+        assert form is None
+
+        generate_allowed_mentions.assert_called_once_with(
+            undefined.UNDEFINED, undefined.UNDEFINED, undefined.UNDEFINED, undefined.UNDEFINED
+        )
+
+    def test__build_message_payload_with_edit_and_all_mentions_undefined(self, rest_client):
+        with mock.patch.object(mentions, "generate_allowed_mentions") as generate_allowed_mentions:
+            body, form = rest_client._build_message_payload(edit=True)
+
+        assert body == {}
+        assert form is None
+
+        generate_allowed_mentions.assert_not_called()
+
+    def test__build_message_payload_embed_content_syntactic_sugar(self, rest_client):
+        embed = mock.Mock(embeds.Embed)
+
+        stack = contextlib.ExitStack()
+        generate_allowed_mentions = stack.enter_context(
+            mock.patch.object(mentions, "generate_allowed_mentions", return_value={"allowed_mentions": 1})
+        )
+        rest_client._entity_factory.serialize_embed.return_value = ({"embed": 1}, [])
+
+        with stack:
+            body, form = rest_client._build_message_payload(content=embed)
+
+        # Returned
+        assert body == {"embeds": [{"embed": 1}], "allowed_mentions": {"allowed_mentions": 1}}
+        assert form is None
+
+        # Embeds
+        rest_client._entity_factory.serialize_embed.assert_called_once_with(embed)
+
+        # Generate allowed mentions
+        generate_allowed_mentions.assert_called_once_with(
+            undefined.UNDEFINED, undefined.UNDEFINED, undefined.UNDEFINED, undefined.UNDEFINED
+        )
+
+    def test__build_message_payload_attachment_content_syntactic_sugar(self, rest_client):
+        attachment = mock.Mock(files.Resource)
+        resource_attachment = object()
+
+        stack = contextlib.ExitStack()
+        ensure_resource = stack.enter_context(
+            mock.patch.object(files, "ensure_resource", return_value=resource_attachment)
+        )
+        generate_allowed_mentions = stack.enter_context(
+            mock.patch.object(mentions, "generate_allowed_mentions", return_value={"allowed_mentions": 1})
+        )
+        url_encoded_form = stack.enter_context(mock.patch.object(data_binding, "URLEncodedFormBuilder"))
+
+        with stack:
+            body, form = rest_client._build_message_payload(content=attachment)
+
+        # Returned
+        assert body == {"allowed_mentions": {"allowed_mentions": 1}}
+        assert form is url_encoded_form.return_value
+
+        # Attachments
+        ensure_resource.assert_called_once_with(attachment)
+
+        # Generate allowed mentions
+        generate_allowed_mentions.assert_called_once_with(
+            undefined.UNDEFINED, undefined.UNDEFINED, undefined.UNDEFINED, undefined.UNDEFINED
+        )
+
+        # Form builder
+        url_encoded_form.assert_called_once_with(executor=rest_client._executor)
+        url_encoded_form.return_value.add_resource.assert_called_once_with("file0", resource_attachment)
+
+    def test__build_message_payload_with_singular_args(self, rest_client):
+        attachment = object()
+        resource_attachment = object()
+        component = mock.Mock(build=mock.Mock(return_value={"component": 1}))
+        embed = object()
+        embed_attachment = object()
+        mentions_everyone = object()
+        mentions_reply = object()
+        user_mentions = object()
+        role_mentions = object()
+
+        stack = contextlib.ExitStack()
+        ensure_resource = stack.enter_context(
+            mock.patch.object(files, "ensure_resource", return_value=resource_attachment)
+        )
+        generate_allowed_mentions = stack.enter_context(
+            mock.patch.object(mentions, "generate_allowed_mentions", return_value={"allowed_mentions": 1})
+        )
+        url_encoded_form = stack.enter_context(mock.patch.object(data_binding, "URLEncodedFormBuilder"))
+        rest_client._entity_factory.serialize_embed.return_value = ({"embed": 1}, [embed_attachment])
+
+        with stack:
+            body, form = rest_client._build_message_payload(
+                content=987654321,
+                attachment=attachment,
+                component=component,
+                embed=embed,
+                replace_attachments=True,
+                flags=120,
+                tts=True,
+                mentions_everyone=mentions_everyone,
+                mentions_reply=mentions_reply,
+                user_mentions=user_mentions,
+                role_mentions=role_mentions,
+            )
+
+        # Returned
+        assert body == {
+            "content": "987654321",
+            "tts": True,
+            "flags": 120,
+            "embeds": [{"embed": 1}],
+            "components": [{"component": 1}],
+            "attachments": None,
+            "allowed_mentions": {"allowed_mentions": 1},
+        }
+        assert form is url_encoded_form.return_value
+
+        # Attachments
+        ensure_resource.assert_called_once_with(attachment)
+
+        # Embeds
+        rest_client._entity_factory.serialize_embed.assert_called_once_with(embed)
+
+        # Components
+        component.build.assert_called_once_with()
+
+        # Generate allowed mentions
+        generate_allowed_mentions.assert_called_once_with(
+            mentions_everyone, mentions_reply, user_mentions, role_mentions
+        )
+
+        # Form builder
+        url_encoded_form.assert_called_once_with(executor=rest_client._executor)
+        assert url_encoded_form.return_value.add_resource.call_count == 2
+        url_encoded_form.return_value.add_resource.assert_has_calls(
+            [mock.call("file0", resource_attachment), mock.call("file1", embed_attachment)]
+        )
+
+    def test__build_message_payload_with_plural_args(self, rest_client):
+        attachment1 = object()
+        attachment2 = object()
+        resource_attachment1 = object()
+        resource_attachment2 = object()
+        component1 = mock.Mock(build=mock.Mock(return_value={"component": 1}))
+        component2 = mock.Mock(build=mock.Mock(return_value={"component": 2}))
+        embed1 = object()
+        embed2 = object()
+        embed_attachment1 = object()
+        embed_attachment2 = object()
+        embed_attachment3 = object()
+        embed_attachment4 = object()
+        mentions_everyone = object()
+        mentions_reply = object()
+        user_mentions = object()
+        role_mentions = object()
+
+        stack = contextlib.ExitStack()
+        ensure_resource = stack.enter_context(
+            mock.patch.object(files, "ensure_resource", side_effect=[resource_attachment1, resource_attachment2])
+        )
+        generate_allowed_mentions = stack.enter_context(
+            mock.patch.object(mentions, "generate_allowed_mentions", return_value={"allowed_mentions": 1})
+        )
+        url_encoded_form = stack.enter_context(mock.patch.object(data_binding, "URLEncodedFormBuilder"))
+        rest_client._entity_factory.serialize_embed.side_effect = [
+            ({"embed": 1}, [embed_attachment1, embed_attachment2]),
+            ({"embed": 2}, [embed_attachment3, embed_attachment4]),
+        ]
+
+        with stack:
+            body, form = rest_client._build_message_payload(
+                content=987654321,
+                attachments=[attachment1, attachment2],
+                components=[component1, component2],
+                embeds=[embed1, embed2],
+                replace_attachments=True,
+                flags=120,
+                tts=True,
+                mentions_everyone=mentions_everyone,
+                mentions_reply=mentions_reply,
+                user_mentions=user_mentions,
+                role_mentions=role_mentions,
+            )
+
+        # Returned
+        assert body == {
+            "content": "987654321",
+            "tts": True,
+            "flags": 120,
+            "embeds": [{"embed": 1}, {"embed": 2}],
+            "components": [{"component": 1}, {"component": 2}],
+            "attachments": None,
+            "allowed_mentions": {"allowed_mentions": 1},
+        }
+        assert form is url_encoded_form.return_value
+
+        # Attachments
+        assert ensure_resource.call_count == 2
+        ensure_resource.assert_has_calls([mock.call(attachment1), mock.call(attachment2)])
+
+        # Embeds
+        assert rest_client._entity_factory.serialize_embed.call_count == 2
+        rest_client._entity_factory.serialize_embed.assert_has_calls([mock.call(embed1), mock.call(embed2)])
+
+        # Components
+        component1.build.assert_called_once_with()
+        component2.build.assert_called_once_with()
+
+        # Generate allowed mentions
+        generate_allowed_mentions.assert_called_once_with(
+            mentions_everyone, mentions_reply, user_mentions, role_mentions
+        )
+
+        # Form builder
+        url_encoded_form.assert_called_once_with(executor=rest_client._executor)
+        assert url_encoded_form.return_value.add_resource.call_count == 6
+        url_encoded_form.return_value.add_resource.assert_has_calls(
+            [
+                mock.call("file0", resource_attachment1),
+                mock.call("file1", resource_attachment2),
+                mock.call("file2", embed_attachment1),
+                mock.call("file3", embed_attachment2),
+                mock.call("file4", embed_attachment3),
+                mock.call("file5", embed_attachment4),
+            ]
+        )
+
     @pytest.mark.parametrize(
         ("singular_arg", "plural_arg"),
         [("attachment", "attachments"), ("component", "components"), ("embed", "embeds")],
     )
-    async def test__create_message_when_both_single_and_plural_args_passed(self, rest_client, singular_arg, plural_arg):
-        kwargs = {
-            "content": "testing",
-            "attachment": undefined.UNDEFINED,
-            "attachments": undefined.UNDEFINED,
-            "component": undefined.UNDEFINED,
-            "components": undefined.UNDEFINED,
-            "embed": undefined.UNDEFINED,
-            "embeds": undefined.UNDEFINED,
-            "tts": undefined.UNDEFINED,
-            "mentions_everyone": undefined.UNDEFINED,
-            "mentions_reply": undefined.UNDEFINED,
-            "user_mentions": undefined.UNDEFINED,
-            "role_mentions": undefined.UNDEFINED,
-            # Keys will be replaced
-            singular_arg: object(),
-            plural_arg: object(),
-        }
-
+    def test__build_message_payload_when_both_single_and_plural_args_passed(
+        self, rest_client, singular_arg, plural_arg
+    ):
         with pytest.raises(
             ValueError, match=rf"You may only specify one of '{singular_arg}' or '{plural_arg}', not both"
         ):
-            await rest_client._create_message(None, {}, **kwargs)
+            rest_client._build_message_payload(**{singular_arg: object(), plural_arg: object()})
 
     @pytest.mark.parametrize(
         ("singular_arg", "plural_arg"),
         [("attachment", "attachments"), ("component", "components"), ("embed", "embeds")],
     )
-    async def test__create_message_when_non_collection_passed_to_plural(self, rest_client, singular_arg, plural_arg):
-        kwargs = {
-            "content": "testing",
-            "attachment": undefined.UNDEFINED,
-            "attachments": undefined.UNDEFINED,
-            "component": undefined.UNDEFINED,
-            "components": undefined.UNDEFINED,
-            "embed": undefined.UNDEFINED,
-            "embeds": undefined.UNDEFINED,
-            "tts": undefined.UNDEFINED,
-            "mentions_everyone": undefined.UNDEFINED,
-            "mentions_reply": undefined.UNDEFINED,
-            "user_mentions": undefined.UNDEFINED,
-            "role_mentions": undefined.UNDEFINED,
-            # Keys will be replaced
-            plural_arg: object(),
-        }
-
+    def test__build_message_payload_when_non_collection_passed_to_plural(self, rest_client, singular_arg, plural_arg):
         expected_error_message = (
             f"You passed a non-collection to '{plural_arg}', but this expects a collection. Maybe you meant to use "
             f"'{singular_arg}' (singular) instead?"
         )
 
         with pytest.raises(TypeError, match=re.escape(expected_error_message)):
-            await rest_client._create_message(None, {}, **kwargs)
+            rest_client._build_message_payload(**{plural_arg: object()})
 
-    @pytest.mark.skip("TODO")
-    async def test__create_message(self, rest_client):
-        ...
+    async def test_create_message_when_form(self, rest_client):
+        attachment_obj = object()
+        attachment_obj2 = object()
+        component_obj = object()
+        component_obj2 = object()
+        embed_obj = object()
+        embed_obj2 = object()
+        mock_form = mock.Mock()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
+        expected_route = routes.POST_CHANNEL_MESSAGES.compile(channel=123456789)
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, mock_form))
+        rest_client._request = mock.AsyncMock(return_value={"message_id": 123})
 
-    async def test_create_message(self, rest_client):
-        mock_object = object()
+        returned = await rest_client.create_message(
+            StubModel(123456789),
+            content="new content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            tts=True,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+            nonce="noncing",
+            reply=StubModel(987654321),
+        )
+        assert returned is rest_client._entity_factory.deserialize_message.return_value
+
+        rest_client._build_message_payload.assert_called_once_with(
+            content="new content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            tts=True,
+            mentions_everyone=False,
+            mentions_reply=undefined.UNDEFINED,
+            user_mentions=[9876],
+            role_mentions=[1234],
+        )
+        mock_form.add_field.assert_called_once_with(
+            "payload_json",
+            '{"testing": "ensure_in_test", "nonce": "noncing", "message_reference": {"message_id": "987654321"}}',
+            content_type="application/json",
+        )
+        rest_client._request.assert_awaited_once_with(expected_route, form_builder=mock_form)
+        rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
+
+    async def test_create_message_when_no_form(self, rest_client):
         attachment_obj = object()
         attachment_obj2 = object()
         component_obj = object()
         component_obj2 = object()
         embed_obj = object()
         embed_obj2 = object()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
         expected_route = routes.POST_CHANNEL_MESSAGES.compile(channel=123456789)
-        rest_client._create_message = mock.AsyncMock(return_value=mock_object)
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, None))
+        rest_client._request = mock.AsyncMock(return_value={"message_id": 123})
 
         returned = await rest_client.create_message(
             StubModel(123456789),
@@ -1971,11 +2258,9 @@ async def test_create_message(self, rest_client):
             nonce="noncing",
             reply=StubModel(987654321),
         )
-        assert returned is mock_object
+        assert returned is rest_client._entity_factory.deserialize_message.return_value
 
-        rest_client._create_message.assert_awaited_once_with(
-            expected_route,
-            {"nonce": "noncing", "message_reference": {"message_id": "987654321"}},
+        rest_client._build_message_payload.assert_called_once_with(
             content="new content",
             attachment=attachment_obj,
             attachments=[attachment_obj2],
@@ -1989,6 +2274,11 @@ async def test_create_message(self, rest_client):
             user_mentions=[9876],
             role_mentions=[1234],
         )
+        rest_client._request.assert_awaited_once_with(
+            expected_route,
+            json={"testing": "ensure_in_test", "nonce": "noncing", "message_reference": {"message_id": "987654321"}},
+        )
+        rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
 
     async def test_crosspost_message(self, rest_client):
         expected_route = routes.POST_CHANNEL_CROSSPOST.compile(channel=444432, message=12353234)
@@ -2004,78 +2294,72 @@ async def test_crosspost_message(self, rest_client):
         )
         rest_client._request.assert_awaited_once_with(expected_route)
 
-    @pytest.mark.parametrize(
-        ("singular_arg", "plural_arg"),
-        [("attachment", "attachments"), ("component", "components"), ("embed", "embeds")],
-    )
-    async def test__edit_message_when_both_single_and_plural_args_passed(self, rest_client, singular_arg, plural_arg):
-        kwargs = {
-            "content": "testing",
-            "attachment": undefined.UNDEFINED,
-            "attachments": undefined.UNDEFINED,
-            "component": undefined.UNDEFINED,
-            "components": undefined.UNDEFINED,
-            "embed": undefined.UNDEFINED,
-            "embeds": undefined.UNDEFINED,
-            "replace_attachments": True,
-            "mentions_everyone": undefined.UNDEFINED,
-            "mentions_reply": undefined.UNDEFINED,
-            "user_mentions": undefined.UNDEFINED,
-            "role_mentions": undefined.UNDEFINED,
-            # Keys will be replaced
-            singular_arg: object(),
-            plural_arg: object(),
-        }
-
-        with pytest.raises(
-            ValueError, match=rf"You may only specify one of '{singular_arg}' or '{plural_arg}', not both"
-        ):
-            await rest_client._edit_message(None, {}, **kwargs)
-
-    @pytest.mark.parametrize(
-        ("singular_arg", "plural_arg"),
-        [("attachment", "attachments"), ("component", "components"), ("embed", "embeds")],
-    )
-    async def test__edit_message_when_non_collection_passed_to_plural(self, rest_client, singular_arg, plural_arg):
-        kwargs = {
-            "content": "testing",
-            "attachment": undefined.UNDEFINED,
-            "attachments": undefined.UNDEFINED,
-            "component": undefined.UNDEFINED,
-            "components": undefined.UNDEFINED,
-            "embed": undefined.UNDEFINED,
-            "embeds": undefined.UNDEFINED,
-            "replace_attachments": True,
-            "mentions_everyone": undefined.UNDEFINED,
-            "mentions_reply": undefined.UNDEFINED,
-            "user_mentions": undefined.UNDEFINED,
-            "role_mentions": undefined.UNDEFINED,
-            # Keys will be replaced
-            plural_arg: object(),
-        }
+    async def test_edit_message_when_form(self, rest_client):
+        attachment_obj = object()
+        attachment_obj2 = object()
+        component_obj = object()
+        component_obj2 = object()
+        embed_obj = object()
+        embed_obj2 = object()
+        mock_form = mock.Mock()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
+        expected_route = routes.PATCH_CHANNEL_MESSAGE.compile(channel=123456789, message=987654321)
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, mock_form))
+        rest_client._request = mock.AsyncMock(return_value={"message_id": 123})
 
-        expected_error_message = (
-            f"You passed a non-collection to '{plural_arg}', but this expects a collection. Maybe you meant to use "
-            f"'{singular_arg}' (singular) instead?"
+        returned = await rest_client.edit_message(
+            StubModel(123456789),
+            StubModel(987654321),
+            content="new content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            replace_attachments=True,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+            flags=120,
         )
+        assert returned is rest_client._entity_factory.deserialize_message.return_value
 
-        with pytest.raises(TypeError, match=re.escape(expected_error_message)):
-            await rest_client._edit_message(None, {}, **kwargs)
-
-    @pytest.mark.skip("TODO")
-    async def test__edit_message(self, rest_client):
-        ...
+        rest_client._build_message_payload.assert_called_once_with(
+            content="new content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            flags=120,
+            replace_attachments=True,
+            mentions_everyone=False,
+            mentions_reply=undefined.UNDEFINED,
+            user_mentions=[9876],
+            role_mentions=[1234],
+            edit=True,
+        )
+        mock_form.add_field.assert_called_once_with(
+            "payload_json", '{"testing": "ensure_in_test"}', content_type="application/json"
+        )
+        rest_client._request.assert_awaited_once_with(expected_route, form_builder=mock_form)
+        rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
 
-    async def test_edit_message(self, rest_client):
-        mock_object = object()
+    async def test_edit_message_when_no_form(self, rest_client):
         attachment_obj = object()
         attachment_obj2 = object()
         component_obj = object()
         component_obj2 = object()
         embed_obj = object()
         embed_obj2 = object()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
         expected_route = routes.PATCH_CHANNEL_MESSAGE.compile(channel=123456789, message=987654321)
-        rest_client._edit_message = mock.AsyncMock(return_value=mock_object)
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, None))
+        rest_client._request = mock.AsyncMock(return_value={"message_id": 123})
 
         returned = await rest_client.edit_message(
             StubModel(123456789),
@@ -2093,11 +2377,9 @@ async def test_edit_message(self, rest_client):
             role_mentions=[1234],
             flags=120,
         )
-        assert returned is mock_object
+        assert returned is rest_client._entity_factory.deserialize_message.return_value
 
-        rest_client._edit_message.assert_awaited_once_with(
-            expected_route,
-            {"flags": 120},
+        rest_client._build_message_payload.assert_called_once_with(
             content="new content",
             attachment=attachment_obj,
             attachments=[attachment_obj2],
@@ -2105,12 +2387,16 @@ async def test_edit_message(self, rest_client):
             components=[component_obj2],
             embed=embed_obj,
             embeds=[embed_obj2],
+            flags=120,
             replace_attachments=True,
             mentions_everyone=False,
             mentions_reply=undefined.UNDEFINED,
             user_mentions=[9876],
             role_mentions=[1234],
+            edit=True,
         )
+        rest_client._request.assert_awaited_once_with(expected_route, json={"testing": "ensure_in_test"})
+        rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
 
     async def test_follow_channel(self, rest_client):
         expected_route = routes.POST_CHANNEL_FOLLOWERS.compile(channel=3333)
@@ -2428,22 +2714,25 @@ async def test_delete_webhook_without_token(self, rest_client):
             (432, "https://website.com/davfsa_logo"),
         ],
     )
-    async def test_execute_webhook(self, rest_client, webhook, avatar_url):
-        mock_object = object()
+    async def test_execute_webhook_when_form(self, rest_client, webhook, avatar_url):
         attachment_obj = object()
         attachment_obj2 = object()
         component_obj = object()
         component_obj2 = object()
         embed_obj = object()
         embed_obj2 = object()
+        mock_form = mock.Mock()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
         expected_route = routes.POST_WEBHOOK_WITH_TOKEN.compile(webhook=432, token="hi, im a token")
-        rest_client._create_message = mock.AsyncMock(return_value=mock_object)
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, mock_form))
+        rest_client._request = mock.AsyncMock(return_value={"message_id": 123})
 
         returned = await rest_client.execute_webhook(
-            webhook,
+            432,
             "hi, im a token",
             username="davfsa",
-            avatar_url=avatar_url,
+            avatar_url="https://website.com/davfsa_logo",
             content="new content",
             attachment=attachment_obj,
             attachments=[attachment_obj2],
@@ -2457,13 +2746,53 @@ async def test_execute_webhook(self, rest_client, webhook, avatar_url):
             role_mentions=[1234],
             flags=120,
         )
-        assert returned is mock_object
+        assert returned is rest_client._entity_factory.deserialize_message.return_value
 
-        rest_client._create_message.assert_awaited_once_with(
+        rest_client._build_message_payload.assert_called_once_with(
+            content="new content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            tts=True,
+            flags=120,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+        )
+        mock_form.add_field.assert_called_once_with(
+            "payload_json",
+            '{"testing": "ensure_in_test", "username": "davfsa", "avatar_url": "https://website.com/davfsa_logo"}',
+            content_type="application/json",
+        )
+        rest_client._request.assert_awaited_once_with(
             expected_route,
-            {"username": "davfsa", "avatar_url": "https://website.com/davfsa_logo", "flags": 120},
-            {"wait": "true"},
+            form_builder=mock_form,
+            query={"wait": "true"},
             no_auth=True,
+        )
+        rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
+
+    async def test_execute_webhook_when_no_form(self, rest_client):
+        attachment_obj = object()
+        attachment_obj2 = object()
+        component_obj = object()
+        component_obj2 = object()
+        embed_obj = object()
+        embed_obj2 = object()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
+        expected_route = routes.POST_WEBHOOK_WITH_TOKEN.compile(webhook=432, token="hi, im a token")
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, None))
+        rest_client._request = mock.AsyncMock(return_value={"message_id": 123})
+
+        returned = await rest_client.execute_webhook(
+            432,
+            "hi, im a token",
+            username="davfsa",
+            avatar_url="https://website.com/davfsa_logo",
             content="new content",
             attachment=attachment_obj,
             attachments=[attachment_obj2],
@@ -2473,10 +2802,33 @@ async def test_execute_webhook(self, rest_client, webhook, avatar_url):
             embeds=[embed_obj2],
             tts=True,
             mentions_everyone=False,
-            mentions_reply=undefined.UNDEFINED,
             user_mentions=[9876],
             role_mentions=[1234],
+            flags=120,
         )
+        assert returned is rest_client._entity_factory.deserialize_message.return_value
+
+        rest_client._build_message_payload.assert_called_once_with(
+            content="new content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            tts=True,
+            flags=120,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+        )
+        rest_client._request.assert_awaited_once_with(
+            expected_route,
+            json={"testing": "ensure_in_test", "username": "davfsa", "avatar_url": "https://website.com/davfsa_logo"},
+            query={"wait": "true"},
+            no_auth=True,
+        )
+        rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
 
     @pytest.mark.parametrize("webhook", [mock.Mock(webhooks.ExecutableWebhook, webhook_id=432), 432])
     async def test_fetch_webhook_message(self, rest_client, webhook):
@@ -2491,16 +2843,19 @@ async def test_fetch_webhook_message(self, rest_client, webhook):
         rest_client._entity_factory.deserialize_message.assert_called_once_with({"id": "456"})
 
     @pytest.mark.parametrize("webhook", [mock.Mock(webhooks.ExecutableWebhook, webhook_id=432), 432])
-    async def test_edit_webhook_message(self, rest_client, webhook):
-        mock_object = object()
+    async def test_edit_webhook_message_when_form(self, rest_client, webhook):
         attachment_obj = object()
         attachment_obj2 = object()
         component_obj = object()
         component_obj2 = object()
         embed_obj = object()
         embed_obj2 = object()
+        mock_form = mock.Mock()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
         expected_route = routes.PATCH_WEBHOOK_MESSAGE.compile(webhook=432, token="hi, im a token", message=456)
-        rest_client._edit_message = mock.AsyncMock(return_value=mock_object)
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, mock_form))
+        rest_client._request = mock.AsyncMock(return_value={"message_id": 123})
 
         returned = await rest_client.edit_webhook_message(
             webhook,
@@ -2518,12 +2873,60 @@ async def test_edit_webhook_message(self, rest_client, webhook):
             user_mentions=[9876],
             role_mentions=[1234],
         )
-        assert returned is mock_object
+        assert returned is rest_client._entity_factory.deserialize_message.return_value
 
-        rest_client._edit_message.assert_awaited_once_with(
-            expected_route,
-            {},
-            no_auth=True,
+        rest_client._build_message_payload.assert_called_once_with(
+            content="new content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            replace_attachments=True,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+            edit=True,
+        )
+        mock_form.add_field.assert_called_once_with(
+            "payload_json", '{"testing": "ensure_in_test"}', content_type="application/json"
+        )
+        rest_client._request.assert_awaited_once_with(expected_route, form_builder=mock_form, no_auth=True)
+        rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
+
+    async def test_edit_webhook_message_when_no_form(self, rest_client):
+        attachment_obj = object()
+        attachment_obj2 = object()
+        component_obj = object()
+        component_obj2 = object()
+        embed_obj = object()
+        embed_obj2 = object()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
+        expected_route = routes.PATCH_WEBHOOK_MESSAGE.compile(webhook=432, token="hi, im a token", message=456)
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, None))
+        rest_client._request = mock.AsyncMock(return_value={"message_id": 123})
+
+        returned = await rest_client.edit_webhook_message(
+            432,
+            "hi, im a token",
+            StubModel(456),
+            content="new content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            replace_attachments=True,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+        )
+        assert returned is rest_client._entity_factory.deserialize_message.return_value
+
+        rest_client._build_message_payload.assert_called_once_with(
             content="new content",
             attachment=attachment_obj,
             attachments=[attachment_obj2],
@@ -2533,10 +2936,12 @@ async def test_edit_webhook_message(self, rest_client, webhook):
             embeds=[embed_obj2],
             replace_attachments=True,
             mentions_everyone=False,
-            mentions_reply=undefined.UNDEFINED,
             user_mentions=[9876],
             role_mentions=[1234],
+            edit=True,
         )
+        rest_client._request.assert_awaited_once_with(expected_route, json={"testing": "ensure_in_test"}, no_auth=True)
+        rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
 
     @pytest.mark.parametrize("webhook", [mock.Mock(webhooks.ExecutableWebhook, webhook_id=123), 123])
     async def test_delete_webhook_message(self, rest_client, webhook):
@@ -3454,7 +3859,7 @@ async def test_fetch_member(self, rest_client):
         rest_client._request.assert_awaited_once_with(expected_route)
         rest_client._entity_factory.deserialize_member.assert_called_once_with({"id": "789"}, guild_id=123)
 
-    async def test_fetch_my_member(self, rest_client) -> guilds.Member:
+    async def test_fetch_my_member(self, rest_client):
         expected_route = routes.GET_MY_GUILD_MEMBER.compile(guild=45123)
         rest_client._request = mock.AsyncMock(return_value={"id": "595995"})
 
@@ -4180,9 +4585,15 @@ async def test_create_application_command_with_optionals(self, rest_client):
         rest_client._request = mock.AsyncMock(return_value={"id": "29393939"})
         mock_option = object()
 
-        result = await rest_client.create_application_command(
-            StubModel(4332123), "okokok", "not ok anymore", StubModel(653452134), options=[mock_option]
-        )
+        with warnings.catch_warnings():
+            warnings.simplefilter("ignore", category=DeprecationWarning)
+            result = await rest_client.create_application_command(
+                application=StubModel(4332123),
+                guild=StubModel(653452134),
+                name="okokok",
+                description="not ok anymore",
+                options=[mock_option],
+            )
 
         assert result is rest_client._entity_factory.deserialize_command.return_value
         rest_client._entity_factory.serialize_command_option.assert_called_once_with(mock_option)
@@ -4192,6 +4603,7 @@ async def test_create_application_command_with_optionals(self, rest_client):
         rest_client._request.assert_awaited_once_with(
             expected_route,
             json={
+                "type": 1,
                 "name": "okokok",
                 "description": "not ok anymore",
                 "options": [rest_client._entity_factory.serialize_command_option.return_value],
@@ -4202,16 +4614,48 @@ async def test_create_application_command_without_optionals(self, rest_client):
         expected_route = routes.POST_APPLICATION_COMMAND.compile(application=4332123)
         rest_client._request = mock.AsyncMock(return_value={"id": "29393939"})
 
-        result = await rest_client.create_application_command(StubModel(4332123), "okokok", "not ok anymore")
+        with warnings.catch_warnings():
+            warnings.simplefilter("ignore", category=DeprecationWarning)
+            result = await rest_client.create_application_command(
+                StubModel(4332123),
+                name="okokok",
+                description="not ok anymore",
+            )
 
         assert result is rest_client._entity_factory.deserialize_command.return_value
         rest_client._entity_factory.deserialize_command.assert_called_once_with(
             rest_client._request.return_value, guild_id=None
         )
         rest_client._request.assert_awaited_once_with(
-            expected_route, json={"name": "okokok", "description": "not ok anymore"}
+            expected_route, json={"type": 1, "name": "okokok", "description": "not ok anymore"}
         )
 
+    async def test_create_slash_command(self, rest_client):
+        expected_route = routes.POST_APPLICATION_COMMAND.compile(application=4332123)
+        rest_client._request = mock.AsyncMock(return_value={"id": "29393939"})
+
+        result = await rest_client.create_slash_command(StubModel(4332123), "okokok", "not ok anymore")
+
+        assert result is rest_client._entity_factory.deserialize_command.return_value
+        rest_client._entity_factory.deserialize_command.assert_called_once_with(
+            rest_client._request.return_value, guild_id=None
+        )
+        rest_client._request.assert_awaited_once_with(
+            expected_route, json={"type": 1, "name": "okokok", "description": "not ok anymore"}
+        )
+
+    async def test_create_context_menu_command(self, rest_client):
+        expected_route = routes.POST_APPLICATION_COMMAND.compile(application=4332123)
+        rest_client._request = mock.AsyncMock(return_value={"id": "29393939"})
+
+        result = await rest_client.create_context_menu_command(StubModel(4332123), 2, "okokok")
+
+        assert result is rest_client._entity_factory.deserialize_command.return_value
+        rest_client._entity_factory.deserialize_command.assert_called_once_with(
+            rest_client._request.return_value, guild_id=None
+        )
+        rest_client._request.assert_awaited_once_with(expected_route, json={"type": 2, "name": "okokok"})
+
     async def test_set_application_commands_with_guild(self, rest_client):
         expected_route = routes.PUT_APPLICATION_GUILD_COMMANDS.compile(application=4321231, guild=6543234)
         rest_client._request = mock.AsyncMock(return_value=[{"id": "9459329932"}])
@@ -4368,6 +4812,27 @@ def test_interaction_deferred_builder(self, rest_client):
         assert result.type == 5
         assert isinstance(result, special_endpoints.InteractionDeferredBuilder)
 
+    def test_interaction_autocomplete_builder(self, rest_client):
+        result = rest_client.interaction_autocomplete_builder(
+            [
+                commands.CommandChoice(name="name", value="value"),
+                commands.CommandChoice(name="a", value="b"),
+            ]
+        )
+
+        assert result.type == 8
+        assert isinstance(result, special_endpoints.InteractionAutocompleteBuilder)
+        assert len(result.choices) == 2
+
+        raw = result.build(mock.Mock())
+        assert raw["data"] == {"choices": [{"name": "name", "value": "value"}, {"name": "a", "value": "b"}]}
+
+    def test_interaction_autocomplete_builder_with_set_choices(self, rest_client):
+        result = rest_client.interaction_autocomplete_builder([commands.CommandChoice(name="name", value="value")])
+
+        result.set_choices([commands.CommandChoice(name="a", value="b")])
+        assert result.choices == [commands.CommandChoice(name="a", value="b")]
+
     def test_interaction_message_builder(self, rest_client):
         result = rest_client.interaction_message_builder(4)
 
@@ -4384,24 +4849,123 @@ async def test_fetch_interaction_response(self, rest_client):
         rest_client._entity_factory.deserialize_message.assert_called_once_with(rest_client._request.return_value)
         rest_client._request.assert_awaited_once_with(expected_route, no_auth=True)
 
-    @pytest.mark.skip("TODO")
-    async def test_create_interaction_response(self, rest_client):
-        ...
+    async def test_create_interaction_response_when_form(self, rest_client):
+        attachment_obj = object()
+        attachment_obj2 = object()
+        component_obj = object()
+        component_obj2 = object()
+        embed_obj = object()
+        embed_obj2 = object()
+        mock_form = mock.Mock()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
+        expected_route = routes.POST_INTERACTION_RESPONSE.compile(interaction=432, token="some token")
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, mock_form))
+        rest_client._request = mock.AsyncMock()
+
+        await rest_client.create_interaction_response(
+            StubModel(432),
+            "some token",
+            1,
+            "some content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            tts=True,
+            flags=120,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+        )
+
+        rest_client._build_message_payload.assert_called_once_with(
+            content="some content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            tts=True,
+            flags=120,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+        )
+        mock_form.add_field.assert_called_once_with(
+            "payload_json", '{"type": 1, "data": {"testing": "ensure_in_test"}}', content_type="application/json"
+        )
+        rest_client._request.assert_awaited_once_with(expected_route, form_builder=mock_form, no_auth=True)
+
+    async def test_create_interaction_response_when_no_form(self, rest_client):
+        attachment_obj = object()
+        attachment_obj2 = object()
+        component_obj = object()
+        component_obj2 = object()
+        embed_obj = object()
+        embed_obj2 = object()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
+        expected_route = routes.POST_INTERACTION_RESPONSE.compile(interaction=432, token="some token")
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, None))
+        rest_client._request = mock.AsyncMock()
+
+        await rest_client.create_interaction_response(
+            StubModel(432),
+            "some token",
+            1,
+            "some content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            tts=True,
+            flags=120,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+        )
+
+        rest_client._build_message_payload.assert_called_once_with(
+            content="some content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            tts=True,
+            flags=120,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+        )
+        rest_client._request.assert_awaited_once_with(
+            expected_route, json={"type": 1, "data": {"testing": "ensure_in_test"}}, no_auth=True
+        )
 
-    async def test_edit_interaction_response(self, rest_client):
-        mock_object = object()
+    async def test_edit_interaction_response_when_form(self, rest_client):
         attachment_obj = object()
         attachment_obj2 = object()
         component_obj = object()
         component_obj2 = object()
         embed_obj = object()
         embed_obj2 = object()
-        expected_route = routes.PATCH_INTERACTION_RESPONSE.compile(webhook=432, token="hi, im a token")
-        rest_client._edit_message = mock.AsyncMock(return_value=mock_object)
+        mock_form = mock.Mock()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
+        expected_route = routes.PATCH_INTERACTION_RESPONSE.compile(webhook=432, token="some token")
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, mock_form))
+        rest_client._request = mock.AsyncMock(return_value={"message_id": 123})
 
         returned = await rest_client.edit_interaction_response(
             StubModel(432),
-            "hi, im a token",
+            "some token",
             content="new content",
             attachment=attachment_obj,
             attachments=[attachment_obj2],
@@ -4414,12 +4978,9 @@ async def test_edit_interaction_response(self, rest_client):
             user_mentions=[9876],
             role_mentions=[1234],
         )
-        assert returned is mock_object
+        assert returned is rest_client._entity_factory.deserialize_message.return_value
 
-        rest_client._edit_message.assert_awaited_once_with(
-            expected_route,
-            {},
-            no_auth=True,
+        rest_client._build_message_payload.assert_called_once_with(
             content="new content",
             attachment=attachment_obj,
             attachments=[attachment_obj2],
@@ -4429,10 +4990,62 @@ async def test_edit_interaction_response(self, rest_client):
             embeds=[embed_obj2],
             replace_attachments=True,
             mentions_everyone=False,
-            mentions_reply=undefined.UNDEFINED,
             user_mentions=[9876],
             role_mentions=[1234],
+            edit=True,
         )
+        mock_form.add_field.assert_called_once_with(
+            "payload_json", '{"testing": "ensure_in_test"}', content_type="application/json"
+        )
+        rest_client._request.assert_awaited_once_with(expected_route, form_builder=mock_form, no_auth=True)
+        rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
+
+    async def test_edit_interaction_response_when_no_form(self, rest_client):
+        attachment_obj = object()
+        attachment_obj2 = object()
+        component_obj = object()
+        component_obj2 = object()
+        embed_obj = object()
+        embed_obj2 = object()
+        mock_body = data_binding.JSONObjectBuilder()
+        mock_body.put("testing", "ensure_in_test")
+        expected_route = routes.PATCH_INTERACTION_RESPONSE.compile(webhook=432, token="some token")
+        rest_client._build_message_payload = mock.Mock(return_value=(mock_body, None))
+        rest_client._request = mock.AsyncMock(return_value={"message_id": 123})
+
+        returned = await rest_client.edit_interaction_response(
+            StubModel(432),
+            "some token",
+            content="new content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            replace_attachments=True,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+        )
+        assert returned is rest_client._entity_factory.deserialize_message.return_value
+
+        rest_client._build_message_payload.assert_called_once_with(
+            content="new content",
+            attachment=attachment_obj,
+            attachments=[attachment_obj2],
+            component=component_obj,
+            components=[component_obj2],
+            embed=embed_obj,
+            embeds=[embed_obj2],
+            replace_attachments=True,
+            mentions_everyone=False,
+            user_mentions=[9876],
+            role_mentions=[1234],
+            edit=True,
+        )
+        rest_client._request.assert_awaited_once_with(expected_route, json={"testing": "ensure_in_test"}, no_auth=True)
+        rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
 
     async def test_delete_interaction_response(self, rest_client):
         expected_route = routes.DELETE_INTERACTION_RESPONSE.compile(webhook=1235431, token="go homo now")
@@ -4441,3 +5054,16 @@ async def test_delete_interaction_response(self, rest_client):
         await rest_client.delete_interaction_response(StubModel(1235431), "go homo now")
 
         rest_client._request.assert_awaited_once_with(expected_route, no_auth=True)
+
+    async def test_create_autocomplete_response(self, rest_client):
+        expected_route = routes.POST_INTERACTION_RESPONSE.compile(interaction=1235431, token="dissssnake")
+        rest_client._request = mock.AsyncMock()
+
+        choices = [commands.CommandChoice(name="a", value="b"), commands.CommandChoice(name="foo", value="bar")]
+        await rest_client.create_autocomplete_response(StubModel(1235431), "dissssnake", choices)
+
+        rest_client._request.assert_awaited_once_with(
+            expected_route,
+            json={"type": 8, "data": {"choices": [{"name": "a", "value": "b"}, {"name": "foo", "value": "bar"}]}},
+            no_auth=True,
+        )
diff --git a/tests/hikari/impl/test_special_endpoints.py b/tests/hikari/impl/test_special_endpoints.py
index 3fa6cc9a..7afc1bde 100644
--- a/tests/hikari/impl/test_special_endpoints.py
+++ b/tests/hikari/impl/test_special_endpoints.py
@@ -23,6 +23,7 @@
 import mock
 import pytest
 
+from hikari import commands
 from hikari import emojis
 from hikari import messages
 from hikari import snowflakes
@@ -185,19 +186,19 @@ def test_build_handles_attachments(self):
             builder.build(mock_entity_factory)
 
 
-class TestCommandBuilder:
+class TestSlashCommandBuilder:
     def test_description_property(self):
-        builder = special_endpoints.CommandBuilder("ok", "NO")
+        builder = special_endpoints.SlashCommandBuilder("ok", "NO")
 
         assert builder.description == "NO"
 
     def test_name_property(self):
-        builder = special_endpoints.CommandBuilder("NOOOOO", "OKKKK")
+        builder = special_endpoints.SlashCommandBuilder("NOOOOO", "OKKKK")
 
         assert builder.name == "NOOOOO"
 
     def test_options_property(self):
-        builder = special_endpoints.CommandBuilder("OKSKDKSDK", "inmjfdsmjiooikjsa")
+        builder = special_endpoints.SlashCommandBuilder("OKSKDKSDK", "inmjfdsmjiooikjsa")
         mock_option = object()
 
         assert builder.options == []
@@ -207,12 +208,12 @@ def test_options_property(self):
         assert builder.options == [mock_option]
 
     def test_id_property(self):
-        builder = special_endpoints.CommandBuilder("OKSKDKSDK", "inmjfdsmjiooikjsa").set_id(3212123)
+        builder = special_endpoints.SlashCommandBuilder("OKSKDKSDK", "inmjfdsmjiooikjsa").set_id(3212123)
 
         assert builder.id == 3212123
 
     def test_default_permission(self):
-        builder = special_endpoints.CommandBuilder("oksksksk", "kfdkodfokfd").set_default_permission(True)
+        builder = special_endpoints.SlashCommandBuilder("oksksksk", "kfdkodfokfd").set_default_permission(True)
 
         assert builder.default_permission is True
 
@@ -220,7 +221,7 @@ def test_build_with_optional_data(self):
         mock_entity_factory = mock.Mock()
         mock_option = object()
         builder = (
-            special_endpoints.CommandBuilder("we are number", "one")
+            special_endpoints.SlashCommandBuilder("we are number", "one")
             .add_option(mock_option)
             .set_id(3412312)
             .set_default_permission(False)
@@ -232,17 +233,43 @@ def test_build_with_optional_data(self):
         assert result == {
             "name": "we are number",
             "description": "one",
+            "type": 1,
             "default_permission": False,
             "options": [mock_entity_factory.serialize_command_option.return_value],
             "id": "3412312",
         }
 
     def test_build_without_optional_data(self):
-        builder = special_endpoints.CommandBuilder("we are numberr", "oner")
+        builder = special_endpoints.SlashCommandBuilder("we are numberr", "oner")
 
         result = builder.build(mock.Mock())
 
-        assert result == {"name": "we are numberr", "description": "oner", "options": []}
+        assert result == {"type": 1, "name": "we are numberr", "description": "oner", "options": []}
+
+
+class TestContextMenuBuilder:
+    def test_build_with_optional_data(self):
+        builder = (
+            special_endpoints.ContextMenuCommandBuilder(commands.CommandType.USER, "we are number")
+            .set_id(3412312)
+            .set_default_permission(False)
+        )
+
+        result = builder.build(mock.Mock())
+
+        assert result == {
+            "name": "we are number",
+            "type": 2,
+            "default_permission": False,
+            "id": "3412312",
+        }
+
+    def test_build_without_optional_data(self):
+        builder = special_endpoints.ContextMenuCommandBuilder(commands.CommandType.MESSAGE, "nameeeee")
+
+        result = builder.build(mock.Mock())
+
+        assert result == {"type": 3, "name": "nameeeee"}
 
 
 @pytest.mark.parametrize("emoji", ["UNICORN", emojis.UnicodeEmoji("UNICORN")])
diff --git a/tests/hikari/interactions/test_base_interactions.py b/tests/hikari/interactions/test_base_interactions.py
index 69ea6eee..16f92862 100644
--- a/tests/hikari/interactions/test_base_interactions.py
+++ b/tests/hikari/interactions/test_base_interactions.py
@@ -74,6 +74,8 @@ async def test_create_initial_response_with_optional_args(self, mock_message_res
         mock_embed_2 = object()
         mock_component = object()
         mock_components = object(), object()
+        mock_attachment = object()
+        mock_attachments = object(), object()
         await mock_message_response_mixin.create_initial_response(
             base_interactions.ResponseType.MESSAGE_CREATE,
             "content",
@@ -83,6 +85,8 @@ async def test_create_initial_response_with_optional_args(self, mock_message_res
             embeds=[mock_embed_2],
             component=mock_component,
             components=mock_components,
+            attachment=mock_attachment,
+            attachments=mock_attachments,
             mentions_everyone=False,
             user_mentions=[123432],
             role_mentions=[6324523],
@@ -99,6 +103,8 @@ async def test_create_initial_response_with_optional_args(self, mock_message_res
             embeds=[mock_embed_2],
             component=mock_component,
             components=mock_components,
+            attachment=mock_attachment,
+            attachments=mock_attachments,
             mentions_everyone=False,
             user_mentions=[123432],
             role_mentions=[6324523],
@@ -121,6 +127,8 @@ async def test_create_initial_response_without_optional_args(self, mock_message_
             embeds=undefined.UNDEFINED,
             component=undefined.UNDEFINED,
             components=undefined.UNDEFINED,
+            attachment=undefined.UNDEFINED,
+            attachments=undefined.UNDEFINED,
             mentions_everyone=undefined.UNDEFINED,
             user_mentions=undefined.UNDEFINED,
             role_mentions=undefined.UNDEFINED,
diff --git a/tests/hikari/interactions/test_command_interactions.py b/tests/hikari/interactions/test_command_interactions.py
index 74dc5a5a..0cd383da 100644
--- a/tests/hikari/interactions/test_command_interactions.py
+++ b/tests/hikari/interactions/test_command_interactions.py
@@ -23,6 +23,7 @@
 import pytest
 
 from hikari import channels
+from hikari import commands
 from hikari import snowflakes
 from hikari import traits
 from hikari.interactions import base_interactions
@@ -50,6 +51,7 @@ def mock_command_interaction(self, mock_app):
             application_id=snowflakes.Snowflake(43123),
             command_id=snowflakes.Snowflake(3123123),
             command_name="OKOKOK",
+            command_type=1,
             options=[],
             resolved=None,
             locale="es-ES",
@@ -89,3 +91,53 @@ def test_get_channel_without_cache(self, mock_command_interaction):
         mock_command_interaction.app = mock.Mock(traits.RESTAware)
 
         assert mock_command_interaction.get_channel() is None
+
+
+class TestAutocompleteInteraction:
+    @pytest.fixture()
+    def mock_autocomplete_interaction(self, mock_app):
+        return command_interactions.AutocompleteInteraction(
+            app=mock_app,
+            id=snowflakes.Snowflake(2312312),
+            type=base_interactions.InteractionType.APPLICATION_COMMAND,
+            channel_id=snowflakes.Snowflake(3123123),
+            guild_id=snowflakes.Snowflake(5412231),
+            guild_locale="en-US",
+            locale="en-US",
+            member=object(),
+            user=object(),
+            token="httptptptptptptptp",
+            version=1,
+            application_id=snowflakes.Snowflake(43123),
+            command_id=snowflakes.Snowflake(3123123),
+            command_name="OKOKOK",
+            command_type=1,
+            options=[],
+            resolved=None,
+        )
+
+    @pytest.fixture()
+    def mock_command_choices(self):
+        return [commands.CommandChoice(name="a", value="b"), commands.CommandChoice(name="foo", value="bar")]
+
+    def test_build_response(self, mock_autocomplete_interaction, mock_app, mock_command_choices):
+        mock_app.rest.interaction_autocomplete_builder = mock.Mock()
+        builder = mock_autocomplete_interaction.build_response(mock_command_choices)
+
+        assert builder is mock_app.rest.interaction_autocomplete_builder.return_value
+        mock_app.rest.interaction_autocomplete_builder.assert_called_once_with(mock_command_choices)
+
+    @pytest.mark.asyncio()
+    async def test_create_response(
+        self,
+        mock_autocomplete_interaction: command_interactions.AutocompleteInteraction,
+        mock_app,
+        mock_command_choices,
+    ):
+        await mock_autocomplete_interaction.create_response(mock_command_choices)
+
+        mock_app.rest.create_autocomplete_response.assert_awaited_once_with(
+            2312312,
+            "httptptptptptptptp",
+            mock_command_choices,
+        )
diff --git a/tests/hikari/test_commands.py b/tests/hikari/test_commands.py
index 93b7a088..19b4861d 100644
--- a/tests/hikari/test_commands.py
+++ b/tests/hikari/test_commands.py
@@ -26,6 +26,7 @@
 from hikari import snowflakes
 from hikari import traits
 from hikari import undefined
+from tests.hikari import hikari_test_helpers
 
 
 @pytest.fixture()
@@ -33,18 +34,17 @@ def mock_app():
     return mock.Mock(traits.CacheAware, rest=mock.AsyncMock())
 
 
-class TestCommand:
+class TestPartialCommand:
     @pytest.fixture()
     def mock_command(self, mock_app):
-        return commands.Command(
+        return hikari_test_helpers.mock_class_namespace(commands.PartialCommand)(
             app=mock_app,
             id=snowflakes.Snowflake(34123123),
+            type=commands.CommandType.SLASH,
             application_id=snowflakes.Snowflake(65234123),
             name="Name",
-            description="very descript",
-            options=[],
-            guild_id=snowflakes.Snowflake(31231235),
             default_permission=False,
+            guild_id=snowflakes.Snowflake(31231235),
             version=snowflakes.Snowflake(43123123),
         )