Channel.moderate Event Sub giving missing proper authorization

I have an issue with channel.moderate erroring with code 403 Forbidden.

My code is using token/clientid from the moderating account and also in the moderator userid. Broadcaster ID is the broadcaster the account is mod for. Yes, the moderating account has all scopes needed for this subscription.

const subTypes = [
    {
      type: "channel.moderate",
      version: "1",
      token: twitchAccessToken1, 
      clientid: mod_clientid,
      condition: {
        broadcaster_user_id: broadcasterId,
        moderator_user_id: "1373486129",
      },
    },
    {
      type: "channel.ban",
      version: "1",
      token: twitchAccessToken,
      clientid: clientid,
      condition: { broadcaster_user_id: broadcasterId },
    },
    {
      type: "channel.unban",
      version: "1",
      token: twitchAccessToken,
      clientid: clientid,
      condition: { broadcaster_user_id: broadcasterId },
    },
    {
      type: "channel.unban_request.create",
      version: "1",
      token: twitchAccessToken,
      clientid: clientid,
      condition: { broadcaster_user_id: broadcasterId, moderator_user_id: broadcasterId },
    },
    {
      type: "channel.unban_request.resolve",
      version: "1",
      token: twitchAccessToken,
      clientid: clientid,
      condition: { broadcaster_user_id: broadcasterId, moderator_user_id: broadcasterId  },
    },
  ];


  for (const sub of subTypes) {
    console.log(`📡 Attempting to subscribe: ${sub.type} (v${sub.version})`);
    console.log("➡️ Condition:", JSON.stringify(sub.condition, null, 2));

    try {
      const res = await fetch("https://api.twitch.tv/helix/eventsub/subscriptions", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${sub.token}`,
          "Client-Id": sub.clientid,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          type: sub.type,
          version: sub.version,
          condition: sub.condition,
          transport: {
            method: "websocket",
            session_id: sessionId,
          },
        }),
      });

      const result = await res.json();

      if (!res.ok) {
        console.error(`❌ Failed to subscribe to ${sub.type} (v${sub.version}):`, result);
      } else {
        console.log(`âś… Successfully subscribed to ${sub.type} (v${sub.version})`);
       // console.log("🔹 Twitch Response:", JSON.stringify(result, null, 2));
      }
    } catch (err) {
      console.error(`⚠️ Error subscribing to ${sub.type}:`, err);
    }
  }

I keep getting this error and all other subs work.

📡 Attempting to subscribe: channel.moderate (v1)
➡️ Condition: {
  "broadcaster_user_id": "1325565884",
  "moderator_user_id": "1373486129"
}
❌ Failed to subscribe to channel.moderate (v1): {
  error: 'Forbidden',
  status: 403,
  message: 'subscription missing proper authorization'
}

I am using this to use for mod actions logs.

This is also the code for the broadcasterID

if (!broadcasterId) {
    const userRes = await fetch("https://api.twitch.tv/helix/users", {
      headers: {
        Authorization: `Bearer ${twitchAccessToken}`,
        "Client-Id": clientid,
      },
    });
    const userData = await userRes.json();
    if (!userData.data?.length) {
      console.error("❌ Failed to fetch broadcaster info:", userData);
      return;
    }
    broadcasterId = userData.data[0].id;
    console.log(`🎯 Broadcaster: ${userData.data[0].display_name} (${broadcasterId})`);
  }
  1. Should be using v2 of this type
  2. Since websocket you are using a user access token? And that token is for 1373486129 cuzanirl" and is a mod of attyofficial?
  3. You also seem to want to mix two userID’s tokens on the same websocket which is not allowed

Use the Validate Token endpoint https://dev.twitch.tv/docs/authentication/validate-tokens/ and ensure that the token you’re attempting to use has the correct scopes, and has the user_id of the moderator you’re attempting to subscribe as.

Also I’m not sure what you mean by “clientid from the moderating account”, as accounts don’t have Client ID’s.

I meant one of the accounts application, sorry. Also I did check, it did have all scopes.

cuzanirl is a current mod for attyofficial and v2 still comes out to the same problem.

The account that made the application has no bearing on this at all, all that matters is that the OAuth token has the appropriate scopes, and the user_id of the token matches the moderator_id you’re attempting to use here.

That might be the issue and race conditoning if running in parrellelele (but this should throw a different error tbh)

You are trying to auth as attyofficial and cuzanirl on the same socket which is not allowed

For this use case you probably don’t need channel.ban and unban just auth as the moderator for the remaining topics. (as ban/unabn will emit a message on moderate)

So I am confused, what will be a fix for the channel.moderation as it says err code 403; subscription missing proper authorization as I do need that.

Distill you code back to JUST doing that one type.

Check the Generated oAuth token is for the right clientID (since mixing two here for no reason)

And check the generated oAuth tokens is for the moderator.

Here you don’t need a token from the caster at all

Still errors when I did change to only the moderator as the controller not the caster

async function subscribeToEvents(sessionId) {
  const subTypes = [
    {
      type: "channel.moderate",
      version: "2",
    },
    {
      type: "channel.unban_request.create",
      version: "1",
    },
    {
      type: "channel.unban_request.resolve",
      version: "1",
    },
  ];

  for (const sub of subTypes) {
    const body = {
      type: sub.type,
      version: sub.version,
      condition: { broadcaster_user_id: "1325565884", moderator_user_id: "1373486129" },
      transport: { method: "websocket", session_id: sessionId },
    };

    try {
      const res = await fetch("https://api.twitch.tv/helix/eventsub/subscriptions", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${twitchAccessToken1}`,
          "Client-Id": mod_clientid,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(body),
      });

      const result = await res.json();
      if (!res.ok) console.error(`❌ Failed to subscribe to ${sub.type}:`, result);
      else console.log(`âś… Subscribed to ${sub.type}`);
    } catch (err) {
      console.error(`⚠️ Error subscribing to ${sub.type}:`, err);
    }
  }
}```
❌ Failed to subscribe to channel.moderate: {
  error: 'Forbidden',
  status: 403,
  message: 'subscription missing proper authorization'
}

And you have all the scopes listed here?


moderator:read:blocked_terms OR moderator:manage:blocked_terms
moderator:read:chat_settings OR moderator:manage:chat_settings
moderator:read:unban_requests OR moderator:manage:unban_requests
moderator:read:banned_users OR moderator:manage:banned_users
moderator:read:chat_messages OR moderator:manage:chat_messages
moderator:read:warnings OR moderator:manage:warnings
moderator:read:moderators
moderator:read:vips

Yes, all of them are set.

And the moderator user is authed against the mod_clientid

yes, everything is under the moderator

I don’t know what everything is under that means in context? As the owner of the ClientID is irrelevant.

  1. Have a ClientID who owns it doesn’t matter
  2. Create a oAuth Entry URL with the clientID and the needed scopes
  3. Send the moderator to click that link and grant access
  4. Use the resultant token

Code example:

Here it generates an auth link, you click it and then the code uses the resultant token to access each channel the user is a mod of.

I own the ClientID

You a mod grant access to it

Then you could conceptually manage the unban requests of attyofficial