---
source_url: https://www.pubnub.com/docs/migration-guides/access-manager-v3
title: Migrate from Access Manager v2 to v3
updated_at: 2026-09-30T07:20:08.000Z
---

# Migrate from Access Manager v2 to v3

## Documentation index

To discover more PubNub resources:

1. Fetch [PubNub's llms.txt](https://www.pubnub.com/llms-full.txt) for a list of available pages in Markdown format.
2. Identify relevant URLs from that index.
3. Fetch the target pages.

Do not assume a path exists, always check the index first.

Access Manager v2 relies on an `authKey` your server assigns, granted through a separate `grant()` call for each permission set. Access Manager v3 replaces both of those with a signed token your server requests through `grantToken()` and sets on the client with a single `setToken()` call. This guide moves an existing v2 integration to v3.

Access Manager v2 still works. The reason to move is the per-request cost: v2 stores every permission mapping in a server-side database and checks it on every request. A v3 token carries its own permissions, so PubNub checks it instantly instead.

## Before you start

Confirm you have:

* Access Manager enabled on your keyset. See [Configure access control](https://www.pubnub.com/docs/security/access-control/configure-access-control.md).
* A server-side PubNub instance already initialized with a `secretKey`, since that's what both `grant()` and `grantToken()` require. See [Initialize a server SDK with a secret key](https://www.pubnub.com/docs/security/access-control/initialize-server-sdk.md).
* An existing v2 integration: clients that set an `authKey` at initialization, and a server that calls `grant()`.

## Migrate with an AI coding assistant

If you use an AI coding assistant, paste this prompt into it to run the migration. The prompt makes the assistant read this guide, list every `grant()` call and `authKey` first, and change the server before any client. It also makes the assistant stop before it removes `authKey` from a client or removes a v2 `grant()` call.

```text
Migrate this codebase from PubNub Access Manager v2 to Access Manager v3.

1. Read the migration guide first:
   https://www.pubnub.com/docs/migration-guides/access-manager-v3.md
   If the PubNub MCP server is connected, you can call get_general_migration_guide
   instead. Where the guide and your own knowledge of PubNub SDKs disagree,
   follow the guide and tell me.
2. Before you edit anything, list every server-side grant() call, every client
   that sets authKey, and the code path that hands the auth key to each client.
   Tell me which clients are not in this repository. Then show me a plan and wait
   for my approval.
3. Change the server first. Replace grant() with grantToken(), and combine every
   permission set for the same client into one grantToken() call. Keep the
   existing arguments and change only the method and the resource structure.
   Return the token to the client where the auth key used to go.
4. Keep each grantToken() request within the request-size limit in the guide.
   Where a grant lists many resources by name, use an RE2 pattern instead, and
   tell me if our resource names don't fit a pattern.
5. STOP before you change any client. Ask me to confirm that the server change
   is deployed and returns tokens.
6. On each client, remove authKey from the configuration and set the token from
   the server with the SDK's setToken method. To refresh a token, call setToken
   again instead of recreating the client.
7. STOP before you remove any v2 grant() call. Clients that still set authKey
   depend on it, so keep it until I confirm those clients are retired.
8. grant() and grantToken() need the secret key, so they run on the server only.
   Never put the PubNub secret key in client code, and never commit keys or
   tokens.
9. Make one small change at a time. After each change, run the build and tests
   and show me the output.
10. When you finish, list every file you changed and the steps left for me.
```

## How the authorization flow changes

Both versions involve the same three actors: your client, your server, and PubNub. The difference is what moves between them, and what PubNub checks on every call.

Access Manager v2 stores the `authKey`-to-permissions mapping in a database on PubNub's servers, so every authenticated request costs a database lookup. The v2 flow runs in six steps:

1. The client sends a login request to your server.
2. Your server calls `grant()` with an `authKey`, using the `secretKey`.
3. PubNub stores the `authKey`-to-permissions mapping and acknowledges.
4. Your server returns the `authKey` to the client.
5. The client sets the `authKey` in the SDK and makes API calls with it.
6. On each call, PubNub looks up the permissions for that `authKey` in its database and allows or rejects the request.

```mermaid
flowchart TB
    CLIENT["<b>Client</b>"]
    SERVER["<b>Your server</b>"]

    subgraph NET[" "]
        HUB["<b>PubNub</b>"]
        LOOKUP["<b>Database lookup</b><br/>authKey &rarr; permissions"]
    end

    CLIENT -->|"1#46; Login request"| SERVER
    SERVER -->|"2#46; grant#40;#41; with authKey, using secretKey"| HUB
    HUB -->|"3#46; Store authKey&rarr;permissions, acknowledge"| SERVER
    SERVER -->|"4#46; Return authKey"| CLIENT
    CLIENT -->|"5#46; API call with authKey set in SDK"| HUB
    HUB -->|"6#46; Look up authKey permissions"| LOOKUP
    LOOKUP -->|"Allow or reject"| HUB

    class SERVER muted
    class LOOKUP emphasis
    class NET platform
```

Access Manager v3 embeds the permissions inside a signed token, so PubNub checks the signature instead of looking anything up. Once the client has the token, its calls carry no extra round trip. The v3 flow runs in six steps:

1. The client sends a login request to your server.
2. Your server calls `grantToken()` using the `secretKey`.
3. PubNub returns a signed, time-limited token to your server.
4. Your server returns the token to the client.
5. The client sets the token with `setToken()` and makes API calls with it.
6. On each call, PubNub validates the token signature and permissions, with no database lookup, and allows or rejects the request.

```mermaid
flowchart TB
    CLIENT["<b>Client</b>"]
    SERVER["<b>Your server</b>"]

    subgraph NET[" "]
        HUB["<b>PubNub</b>"]
        VALIDATE["<b>Token validation</b><br/>signature check,<br/>no database lookup"]
    end

    CLIENT -->|"1#46; Login request"| SERVER
    SERVER -->|"2#46; grantToken#40;#41; using secretKey"| HUB
    HUB -->|"3#46; Return signed, time-limited token"| SERVER
    SERVER -->|"4#46; Return token"| CLIENT
    CLIENT -->|"5#46; API call, token set via setToken#40;#41;"| HUB
    HUB -->|"6#46; Validate token signature and permissions"| VALIDATE
    VALIDATE -->|"Allow or reject"| HUB

    class SERVER muted
    class NET platform
```

## Update your client configuration

Remove `authKey` from client-side configuration, and set the token your server returns using `setToken` (the method name varies slightly by SDK) instead.

### JavaScript

Before, with a v2 `authKey`:

```javascript
const pubnub = new PubNub({
  subscribeKey: "mySubscribeKey",
  publishKey: "myPublishKey",
  userId: "myUniqueUserId",
  authKey: "yourAuthKey"
});
```

After, with a v3 token:

```javascript
const pubnub = new PubNub({
  subscribeKey: "mySubscribeKey",
  publishKey: "myPublishKey",
  userId: "myUniqueUserId"
});

pubnub.setToken("yourToken"); // Token returned by your server's grantToken call
```

### Python

Before, with a v2 `authKey`:

```python
pn_config = PNConfiguration()
pn_config.subscribe_key = "my_subscribe_key"
pn_config.publish_key = "my_publish_key"
pn_config.user_id = "my_unique_user_id"
pn_config.auth_key = "your_auth_key"

pubnub = PubNub(pn_config)
```

After, with a v3 token:

```python
pn_config = PNConfiguration()
pn_config.subscribe_key = "my_subscribe_key"
pn_config.publish_key = "my_publish_key"
pn_config.user_id = "my_unique_user_id"

pubnub = PubNub(pn_config)

pubnub.set_token("your_token")  # Token returned by your server's grant_token call
```

### Java

Before, with a v2 `authKey`:

```java
PNConfiguration.Builder configBuilder = PNConfiguration.builder(new UserId("yourUserId"), "yourSubscribeKey");
configBuilder.publishKey("myPublishKey");
configBuilder.authKey("yourAuthKey");
PubNub pubNub = PubNub.create(configBuilder.build());
```

After, with a v3 token:

```java
PNConfiguration.Builder configBuilder = PNConfiguration.builder(new UserId("yourUserId"), "yourSubscribeKey");
configBuilder.publishKey("myPublishKey");
PubNub pubNub = PubNub.create(configBuilder.build());

pubNub.setToken("yourToken"); // Token returned by your server's grantToken call
```

### Kotlin

Before, with a v2 `authKey`:

```kotlin
val pnConfiguration = PNConfiguration(UserId("myUserId")).apply {
    subscribeKey = "my_subkey"
    publishKey = "my_pubkey"
    authKey = "yourAuthKey"
    secure = true
}
val pubnub = PubNub.create(pnConfiguration)
```

After, with a v3 token:

```kotlin
val pnConfiguration = PNConfiguration(UserId("myUserId")).apply {
    subscribeKey = "my_subkey"
    publishKey = "my_pubkey"
    secure = true
}
val pubnub = PubNub.create(pnConfiguration)

pubnub.setToken("yourToken") // Token returned by your server's grantToken call
```

### Go

Before, with a v2 `authKey`:

```go
pnconfig := pubnub.NewConfig()
pnconfig.SubscribeKey = "MySubscribeKey"
pnconfig.PublishKey = "MyPublishKey"
pnconfig.SetUserId(pubnub.UserId("myUniqueUserId"))
pnconfig.AuthKey = "yourAuthKey"

pn := pubnub.NewPubNub(pnconfig)
```

After, with a v3 token:

```go
pnconfig := pubnub.NewConfig()
pnconfig.SubscribeKey = "MySubscribeKey"
pnconfig.PublishKey = "MyPublishKey"
pnconfig.SetUserId(pubnub.UserId("myUniqueUserId"))

pn := pubnub.NewPubNub(pnconfig)

pn.SetToken("NewToken") // Token returned by your server's GrantToken call
```

### C#

Before, with a v2 `authKey`:

```csharp
PNConfiguration pnconfig = new PNConfiguration(new UserId("myUniqueUserId"));
pnconfig.SubscribeKey = "mySubscribeKey";
pnconfig.PublishKey = "myPublishKey";
pnconfig.AuthKey = "yourAuthKey";

Pubnub pubnub = new Pubnub(pnconfig);
```

After, with a v3 token:

```csharp
PNConfiguration pnconfig = new PNConfiguration(new UserId("myUniqueUserId"));
pnconfig.SubscribeKey = "mySubscribeKey";
pnconfig.PublishKey = "myPublishKey";

Pubnub pubnub = new Pubnub(pnconfig);

pubnub.SetAuthToken("NewToken"); // Token returned by your server's GrantToken call
```

### Dart

Before, with a v2 `authKey`:

```dart
final myKeyset = Keyset(
  subscribeKey: 'mySubscribeKey',
  publishKey: 'myPublishKey',
  authKey: 'yourAuthKey',
  userId: UserId('yourUniqueUserId'),
);

final pubnub = PubNub(defaultKeyset: myKeyset);
```

After, with a v3 token:

```dart
final myKeyset = Keyset(
  subscribeKey: 'mySubscribeKey',
  publishKey: 'myPublishKey',
  userId: UserId('yourUniqueUserId'),
);

final pubnub = PubNub(defaultKeyset: myKeyset);

pubnub.setToken('yourToken'); // Token returned by your server's grantToken call
```

### PHP

Before, with a v2 `authKey`:

```php
$pnConfiguration = new PNConfiguration();
$pnConfiguration->setSubscribeKey("MySubscribeKey");
$pnConfiguration->setPublishKey("MyPublishKey");
$pnConfiguration->setUuid("MyUniqueUuid");
$pnConfiguration->setAuthKey("yourAuthKey");

$pubnub = new PubNub($pnConfiguration);
```

After, with a v3 token:

```php
$pnConfiguration = new PNConfiguration();
$pnConfiguration->setSubscribeKey("MySubscribeKey");
$pnConfiguration->setPublishKey("MyPublishKey");
$pnConfiguration->setUuid("MyUniqueUuid");

$pubnub = new PubNub($pnConfiguration);

$pubnub->setToken("NewToken"); // Token returned by your server's grantToken call
```

### Ruby

Before, with a v2 `authKey`:

```ruby
pubnub = Pubnub.new(
  subscribe_key: 'my_subscribe_key',
  publish_key: 'my_publish_key',
  user_id: 'myUniqueUserId',
  auth_key: 'yourAuthKey'
)
```

After, with a v3 token:

```ruby
pubnub = Pubnub.new(
  subscribe_key: 'my_subscribe_key',
  publish_key: 'my_publish_key',
  user_id: 'myUniqueUserId'
)

pubnub.set_token('yourToken') # Token returned by your server's grant_token call
```

If you need to update the token again later, without recreating the client, call the same `setToken`-family method again. Refer to [Initialize a client SDK with an auth key](https://www.pubnub.com/docs/security/access-control/initialize-client-sdk.md) for the full procedure, including every SDK's method name.

## Update your server's grant calls

Replace each `grant()` call with a `grantToken()` call. In v2, granting different permissions to different resources for the same client needs one `grant()` call per permission set. In v3, `grantToken()` accepts every resource-permission mapping in a single call, so the equivalent grant collapses into one request.

The examples below show a v2 `grant()` that gives `read` access to `channel-a`, `channel-group-b`, and `uuid-c`. Giving `read` and `write` to `channel-b`, `channel-c`, and `channel-d` at the same time needs a second `grant()` call with the same shape, since v2 applies one permission set per call. The v3 `grantToken()` example grants both permission sets, plus a RE2 pattern, in the single call.

### JavaScript

Before, a v2 grant limited to one permission set:

```javascript
pubnub.grant(
  {
    channels: ["channel-a"],
    channelGroups: ["channel-group-b"],
    uuids: ["uuid-c"],
    authKeys: ["my-authorized-key"],
    ttl: 15,
    read: true
  },
  function (status) {
    console.log(status);
  }
);
```

After, a v3 token covering multiple permission sets in one call:

```javascript
pubnub.grantToken(
    {
        ttl: 15,
        authorized_uuid: "my-authorized-uuid",
        resources: {
            channels: {
                "channel-a": { read: true },
                "channel-b": { read: true, write: true },
                "channel-c": { read: true, write: true },
                "channel-d": { read: true, write: true }
            },
            groups: {
                "channel-group-b": { read: true }
            },
            uuids: {
                "uuid-c": { get: true },
                "uuid-d": { get: true, update: true }
            }
        },
        patterns: {
            channels: {
                "^channel-[A-Za-z0-9]$": { read: true }
            }
        }
    },
    function (status, token) {
        console.log(token)
    });
```

### Python

Before, a v2 grant limited to one permission set:

```python
pubnub.grant() \
    .channels(["channel-a"]) \
    .channel_groups(["channel-group-b"]) \
    .uuids(["uuid-c"]) \
    .auth_keys(["my-authorized-key"]) \
    .read(True) \
    .ttl(15) \
    .sync()
```

After, a v3 token covering multiple permission sets in one call:

```python
from pubnub.models.consumer.v3.channel import Channel
from pubnub.models.consumer.v3.group import Group
from pubnub.models.consumer.v3.uuid import UUID

channels = [
    Channel.id("channel-a").read(),
    Channel.pattern("channel-[A-Za-z0-9]").read(),
    Channel.id("channel-b").read().write(),
    Channel.id("channel-c").read().write(),
    Channel.id("channel-d").read().write()
]
channel_groups = [
    Group.id("channel-group-b").read()
]
uuids = [
    UUID.id("uuid-c").get(),
    UUID.id("uuid-d").get().update()
]
envelope = pubnub.grant_token() \
    .authorized_uuid("my-authorized-uuid") \
    .channels(channels) \
    .groups(channel_groups) \
    .uuids(uuids) \
    .ttl(15) \
    .sync()

token = envelope.result.token
```

### Java

Before, a v2 grant limited to one permission set:

```java
pubnub.grant()
    .channels(Arrays.asList("channel-a"))
    .channelGroups(Arrays.asList("channel-group-b"))
    .uuids(Arrays.asList("uuid-c"))
    .authKeys(Arrays.asList("my-authorized-key"))
    .read(true)
    .ttl(15)
    .async(result -> { /* check result */ });
```

After, a v3 token covering multiple permission sets in one call:

```java
pubnub.grantToken()
    .ttl(15)
    .authorizedUUID("my-authorized-uuid")
    .channels(Arrays.asList(
            ChannelGrant.name("channel-a").read(),
            ChannelGrant.name("channel-b").read().write(),
            ChannelGrant.name("channel-c").read().write(),
            ChannelGrant.name("channel-d").read().write(),
            ChannelGrant.pattern("^channel-[A-Za-z0-9]*$").read()))
    .channelGroups(Collections.singletonList(
            ChannelGroupGrant.id("channel-group-b").read()))
    .uuids(Arrays.asList(
            UUIDGrant.id("uuid-c").get(),
            UUIDGrant.id("uuid-d").get().update()))
    .async(result -> { /* check result */ });
```

### Kotlin

Before, a v2 grant limited to one permission set:

```kotlin
pubnub.grant(
    channels = listOf("channel-a"),
    channelGroups = listOf("channel-group-b"),
    uuids = listOf("uuid-c"),
    authKeys = listOf("my-authorized-key"),
    read = true,
    ttl = 15
).async { result ->
    result.onFailure {
        // Handle error
    }
}
```

After, a v3 token covering multiple permission sets in one call:

```kotlin
pubnub.grantToken(
    ttl = 15,
    authorizedUUID = "my-authorized-uuid",
    channels = listOf(
        ChannelGrant.name(name = "channel-a", read = true),
        ChannelGrant.name(name = "channel-b", read = true, write = true),
        ChannelGrant.name(name = "channel-c", read = true, write = true),
        ChannelGrant.name(name = "channel-d", read = true, write = true),
        ChannelGrant.pattern(pattern = "^channel-[A-Za-z0-9]*$", read = true)
    ),
    channelGroups = listOf(
        ChannelGroupGrant.id(id = "channel-group-b", read = true)
    ),
    uuids = listOf(
        UUIDGrant.id(id = "uuid-c", get = true),
        UUIDGrant.id(id = "uuid-d", get = true, update = true)
    )
).async { result ->
    result.onFailure {
        // Handle error
    }.onSuccess {
        // Provide logic to return this token to your client
    }
}
```

### Go

Before, a v2 grant limited to one permission set:

```go
res, status, err := pn.Grant().
    Channels([]string{"channel-a"}).
    ChannelGroups([]string{"channel-group-b"}).
    UUIDs([]string{"uuid-c"}).
    AuthKeys([]string{"my-authorized-key"}).
    Read(true).
    TTL(15).
    Execute()
```

After, a v3 token covering multiple permission sets in one call:

```go
res, status, err := pn.GrantToken().
    TTL(15).
    AuthorizedUUID("my-authorized-uuid").
    Channels(map[string]pubnub.ChannelPermissions{
        "channel-a": { Read: true },
        "channel-b": { Read: true, Write: true },
        "channel-c": { Read: true, Write: true },
        "channel-d": { Read: true, Write: true },
    }).
    ChannelGroups(map[string]pubnub.GroupPermissions{
        "channel-group-b": { Read: true },
    }).
    UUIDs(map[string]pubnub.UUIDPermissions{
        "uuid-c": { Get: true },
        "uuid-d": { Get: true, Update: true },
    }).
    ChannelsPattern(map[string]pubnub.ChannelPermissions{
        "^channel-[A-Za-z0-9]*$": { Read: true },
    }).
    Execute()
```

### C#

Before, a v2 grant limited to one permission set:

```csharp
pubnub.Grant()
    .Channels(new string[] { "channel-a" })
    .ChannelGroups(new string[] { "channel-group-b" })
    .Uuids(new string[] { "uuid-c" })
    .AuthKeys(new string[] { "my-authorized-key" })
    .Read(true)
    .TTL(15)
    .Execute(new PNAccessManagerGrantResultExt(
        (result, status) => {
            // handle result/status
        }
    ));
```

After, a v3 token covering multiple permission sets in one call:

```csharp
PNResult<PNAccessManagerTokenResult> grantTokenResponse = await pubnub.GrantToken()
    .TTL(15)
    .AuthorizedUuid("my-authorized-uuid")
    .Resources(new PNTokenResources()
    {
        Channels = new Dictionary<string, PNTokenAuthValues>() {
            { "channel-a", new PNTokenAuthValues() { Read = true } },
            { "channel-b", new PNTokenAuthValues() { Read = true, Write = true } },
            { "channel-c", new PNTokenAuthValues() { Read = true, Write = true } },
            { "channel-d", new PNTokenAuthValues() { Read = true, Write = true } }},
        ChannelGroups = new Dictionary<string, PNTokenAuthValues>() {
            { "channel-group-b", new PNTokenAuthValues() { Read = true } } },
        Uuids = new Dictionary<string, PNTokenAuthValues>() {
            { "uuid-c", new PNTokenAuthValues() { Get = true } },
            { "uuid-d", new PNTokenAuthValues() { Get = true, Update = true } }}
    })
    .Patterns(new PNTokenPatterns()
    {
        Channels = new Dictionary<string, PNTokenAuthValues>() {
            { "channel-[A-Za-z0-9]", new PNTokenAuthValues() { Read = true } }}
    })
    .ExecuteAsync();
```

### Dart

Before, a v2 grant limited to one permission set:

```dart
var request = pubnub.requestGrant(
  ttl: 15,
  authKeys: ['my-authorized-key'],
)
  ..add(ResourceType.channel, name: 'channel-a', read: true)
  ..add(ResourceType.channelGroup, name: 'channel-group-b', read: true)
  ..add(ResourceType.uuid, name: 'uuid-c', read: true);

await pubnub.grant(request);
```

After, a v3 token covering multiple permission sets in one call:

```dart
var request = pubnub.requestToken(
    ttl: 15, authorizedUUID: 'my-authorized-uuid')
..add(ResourceType.channel, name: 'channel-a', read: true)
..add(ResourceType.channelGroup, name: 'channel-group-b', read: true)
..add(ResourceType.uuid, name: 'uuid-c', get: true)
..add(ResourceType.channel, name: 'channel-b', read: true, write: true)
..add(ResourceType.channel, name: 'channel-c', read: true, write: true)
..add(ResourceType.channel, name: 'channel-d', read: true, write: true)
..add(ResourceType.uuid, name: 'uuid-d', get: true, update: true)
..add(ResourceType.channel, pattern: 'channel-[A-Za-z0-9]', read: true);

var token = await pubnub.grantToken(request);
```

### PHP

Before, a v2 grant limited to one permission set:

```php
$pubnub->grant()
    ->ttl(15)
    ->authKeys(['my-authorized-key'])
    ->addChannelResources(['channel-a' => ['read' => true]])
    ->addChannelGroupResources(['channel-group-b' => ['read' => true]])
    ->addUuidResources(['uuid-c' => ['read' => true]])
    ->sync();
```

After, a v3 token covering multiple permission sets in one call:

```php
$token = $pubnub->grantToken()
    ->ttl(15)
    ->authorizedUuid('my-authorized-uuid')
    ->addChannelResources([
        'channel-a' => ['read' => true],
        'channel-b' => ['read' => true, 'write' => true],
        'channel-c' => ['read' => true, 'write' => true],
        'channel-d' => ['read' => true, 'write' => true],
    ])
    ->addChannelGroupResources([
        'channel-group-b' => ['read' => true],
    ])
    ->addUuidResources([
        'uuid-c' => ['get' => true],
        'uuid-d' => ['get' => true, 'update' => true],
    ])
    ->addChannelPatterns([
        '^channel-[A-Za-z0-9]$' => ['read' => true],
    ])
    ->sync();
```

### Ruby

Before, a v2 grant limited to one permission set:

```ruby
pubnub.grant(
  ttl: 15,
  auth_keys: ['my-authorized-key'],
  channels: { 'channel-a': Pubnub::Permissions.res(read: true) },
  channel_groups: { 'channel-group-b': Pubnub::Permissions.res(read: true) },
  uuids: { 'uuid-c': Pubnub::Permissions.res(read: true) }
)
```

After, a v3 token covering multiple permission sets in one call:

```ruby
pubnub.grant_token(
    ttl: 15,
    authorized_uuid: "my-authorized-uuid",
    channels: {
        "channel-a": Pubnub::Permissions.res(read: true),
        "channel-b": Pubnub::Permissions.res(read: true, write: true),
        "channel-c": Pubnub::Permissions.res(read: true, write: true),
        "channel-d": Pubnub::Permissions.res(read: true, write: true),
        "^channel-[A-Za-z0-9]$": Pubnub::Permission.pat(read: true)
    },
    channel_groups: {
        "channel-group-b": Pubnub::Permissions.res(read: true)
    },
    uuids: {
        "uuid-c": Pubnub::Permissions.res(get: true),
        "uuid-d": Pubnub::Permissions.res(get: true, update: true)
    },
    http_sync: true
);
```

Your own v2 integration's exact call shape may differ slightly from the examples above. Keep your existing arguments and swap only the method and resource structure. For the full grant and revoke procedure, including RE2 pattern syntax, refer to [Grant, change, and revoke permissions](https://www.pubnub.com/docs/security/access-control/manage-permissions.md).

## Keep token grants within the request-size limit

Access Manager `grantToken()` requests over 32 KiB return HTTP 414 (URI Too Long).

A token that lists many individual resources by name can make the `grantToken()` request exceed the limit. A v2 integration that already grants many separate channels is the most likely to cross it after combining those grants into fewer, larger v3 calls.

Use an RE2 pattern instead of listing every resource. Keep a consistent naming convention for your channels, channel groups, and User IDs, so one pattern can cover many of them. Refer to [Grant, change, and revoke permissions](https://www.pubnub.com/docs/security/access-control/manage-permissions.md) for RE2 pattern syntax, or [contact support](mailto:support@pubnub.com) if your resource names can't be made to fit a pattern.

## What changes on your bill

Access Manager v3 counts transactions the same way v2 does. This migration changes only the number of grant calls. Access Manager v2 needs a separate `grant()` call for each distinct permission set on a client. Access Manager v3's `grantToken()` combines every resource-permission mapping for that client into one call. If your integration currently makes several `grant()` calls per client, combining them into one `grantToken()` call reduces the number of grant transactions your server makes for that client.

## Related tasks

* [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md). The v3 authorization flow, resources, and token model.
* [Grant, change, and revoke permissions](https://www.pubnub.com/docs/security/access-control/manage-permissions.md). Issue, change, and revoke tokens from your server.
* [Initialize a client SDK with an auth key](https://www.pubnub.com/docs/security/access-control/initialize-client-sdk.md). Set a token on the client at startup or after a refresh.
* [Permission model](https://www.pubnub.com/docs/security/access-control/permission-model.md). Every permission flag available on each resource type.
* [Available migration guides](https://www.pubnub.com/docs/migration-guides/overview.md). Every migration guide in the current documentation.

Last updated at: 2026-09-30T07:20:08.000Z
