Notification API WebSocket: Handling 1008 policy_violation on subscription

Subscribing to routing:conversation:activity via the Notification API WebSocket. The connection establishes fine, but as soon as I send the subscription message, the server closes it with code 1008 (policy_violation). Here’s the payload I’m sending:

{"subscribedEvents":["routing:conversation:activity"],"filters":{"routing:conversation:activity":{"conversationType":["voice"]}}}

Token is valid for 24h. Any idea why this specific event triggers a policy violation while user:presence works fine?

The 1008 error usually means the payload structure is slightly off or the token doesn’t have the right scope. You’re missing the subscriptionId in the subscription message. The server needs this to track the session. Also, check your OAuth token. It needs notification-api:read scope. If you’re using client credentials, make sure that scope is added in the app settings.

Here is the correct JSON structure for the subscription message:

{
 "subscriptionId": "unique-id-for-this-session",
 "subscribedEvents": ["routing:conversation:activity"],
 "filters": {
 "routing:conversation:activity": {
 "conversationType": ["voice"]
 }
 }
}

You also need to send a subscribe command before sending the subscription details. The sequence matters. First, send the subscribe command with the subscription ID. Then send the subscription details. If you send them together, the server might reject it.

Here is the sequence:

  1. Send subscribe command:
{
 "command": "subscribe",
 "subscriptionId": "unique-id-for-this-session"
}
  1. Wait for the server to acknowledge with a subscribed message.

  2. Send the subscription details:

{
 "subscriptionId": "unique-id-for-this-session",
 "subscribedEvents": ["routing:conversation:activity"],
 "filters": {
 "routing:conversation:activity": {
 "conversationType": ["voice"]
 }
 }
}

If you’re still getting 1008, check the token scopes. You can verify the scopes by decoding the JWT payload. Look for notification-api:read. If it’s missing, add it in the Genesys Cloud admin console under Apps.

Also, make sure you’re not exceeding the subscription limit. There’s a limit on how many subscriptions you can have per token. If you’re testing, try creating a new token with a fresh subscription ID.

The filters look correct for voice conversations. The issue is likely the missing subscriptionId or the sequence of messages. Try the sequence above and see if it works.

The subscriptionId is definitely required, but don’t overlook the scope on the token itself. You’ll get a 1008 if the token lacks notification-api:read. I usually generate these via the client SDK to avoid manual header issues. Here’s how I structure the initial connect and subscribe using PureCloudPlatformClientV2. It handles the handshake cleanly.

const { NotificationApi } = require('@genesyscloud/genesyscloud');

const notificationApi = new NotificationApi();

// Connect with a token that has 'notification-api:read'
const ws = await notificationApi.connect({
 token: 'YOUR_BEARER_TOKEN',
 onMessage: (msg) => console.log('Received:', msg)
});

// Subscribe after connection is stable
await ws.subscribe({
 subscribedEvents: ['routing:conversation:activity'],
 filters: {
 'routing:conversation:activity': {
 conversationType: ['voice']
 }
 },
 subscriptionId: 'my-unique-sub-id-123' // Required
});

Make sure the subscriptionId is unique per session. Reusing one across reconnects can cause silent failures or duplicate events. The SDK wraps the raw WebSocket, so you don’t need to manage the 1008 code manually unless you’re doing raw socket work. Just verify the token scope in the app settings.