'403 Error when setting application command permissions on Discord

My bot got a 403 error, even though it never happened before, and the code hasn't changed at all.

throw new DiscordAPIError(data, res.status, request);
            ^
DiscordAPIError: Bots cannot use this endpoint
  ---
{
  method: 'put',
  path: '/applications/---/guilds/---/commands/---/permissions',
  code: 20001,
  httpStatus: 403,
  requestData: { json: { permissions: [Array] }, files: [] }
}

Trying to set fullPermissions gives me a 405 Error instead

DiscordAPIError: 405: Method Not Allowed
  ---
{
  method: 'put',
  path: '/applications/---/guilds/---/commands/permissions',
  code: 0,
  httpStatus: 405,
  requestData: {
    json: [
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object], [Object], [Object],
      [Object]
    ],
    files: []
  }
}

The paths /applications/{applicationId}/guilds/{guildId}/commands/{commandId}/permissions and /applications/{applicationId}/guilds/{guildId}/commands/permissions worked perfectly fine before, why wouldn't they work now?



Solution 1:[1]

Discord has released Permissions V2 to all guilds yesterday (April 27, 2022). Bots can no longer control the permissions, only server administrators can. It's better to put simple permission/role/user checks in the code instead if you want to restrict it, and remove the code that would be setting the command permissions.

// only run for specific user
if (interaction.user.id !== "userId") return;
// only run for specific role
if (!interaction.member.roles.cache.has("roleId")) return;
// only run for specific permission
if (!interaction.member.permissions.has("BAN_MEMBERS")) return; // You can use any permission flag of course

Admins can set it by going to Server Settings --> Integerations, but you are able to programmatically edit the command permissions using a Bearer Token with a scope called applications.commands.permissions.update, which you can get from a URL like this

https://discord.com/api/oauth2/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=token&scope=applications.commands.permissions.update

And you will get redirected to what you set the redirect_uri as, with URL parameters. You can copy the access_token part

PUT COMMAND PERMISSIONS

const applicationId = "APPLICATION_ID"
const guildId = "GUILD_ID"
const commandId = "COMMAND_ID" // Set this as your application ID to set default permissions
const url = `https://discord.com/api/v9/applications/${applicationId}/guilds/${guildId}/commands/${commandId}/permissions`
// requires the user that the bearer token belongs to have used applications.commands.permissions.update scope and have manage guild/roles permission
const token = "Bearer {token}" // replace {token} with what you copied from access_token
const payload = {
  permissions: [
    {
      id: "ID", // role/user/channel ID
      type: 1, // 1 for role, 2 for user, and 3 for channel
      permission: false // whether or not that role/user can use the command or you can use the command in the channel
    }
  ] 
}
await fetch(url, {
    method: "PUT",
    body: JSON.stringify(payload),
    headers: {
      Authorization: `${token}`,
      Accept: 'application/json',
     'Content-Type': 'application/json'
    },
  })

GET COMMAND PERMISSIONS

const applicationId = "APPLICATION_ID"
const guildId = "GUILD_ID"
const commandId = "COMMAND_ID" // Set this as your application ID to get default permissions
const url = `https://discord.com/api/v9/applications/${applicationId}/guilds/${guildId}/commands/${commandId}/permissions`
const token = "Bearer {token}"
await fetch(url, {
    method: "GET",
    headers: {
      Authorization: `${token}`,
      Accept: 'application/json',
     'Content-Type': 'application/json'
    },
  })

GET ALL COMMAND PERMISSIONS

const applicationId = "APPLICATION_ID"
const guildId = "GUILD_ID"
const url = `https://discord.com/api/v9/applications/${applicationId}/guilds/${guildId}/commands/permissions`
const token = "Bearer {token}"
await fetch(url, {
    method: "GET",
    headers: {
      Authorization: `${token}`,
      Accept: 'application/json',
     'Content-Type': 'application/json'
    },
  })

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1