
## Managing API tokens

URL: https://docs.atlan.com/product/capabilities/build-apps/sdks/python/access-control/how-tos/manage-api-tokens

> Create, rotate, and delete API tokens in Atlan using the Python SDK. Tokens act as independent service principals — not personal access tokens — and the token value is available immediately after creation.

# ApiToken: create and manage API tokens

Use `ApiToken` in the Atlan Python SDK to programmatically create and manage API tokens.

[API tokens](https://docs.atlan.com/get-started/references/api-access) are a way to provide programmatic access to Atlan without relying on a user's own credentials or permissions.

:::warning[API tokens are **not** personal access tokens]
API tokens are commonly thought to be synonymous with personal access tokens (PAT). In other words, many developers assume that an API token will have the same privileges and permissions as the user who created them. **This isn't the case.** API tokens are their own unique actor and carry entirely their own set of permissions, completely independent from the user who created or otherwise maintains them.
:::

## Create API token

To create a new API token:

### Java

```java showLineNumbers title="Create an API token"
ApiToken token = ApiToken.create(client, "token-name"); // (1)
String tokenValue = token.getAttributes().getAccessToken(); // (2)
```

1. You can use the `ApiToken.create()` method to create a new API token. Because this operation will create the token in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.
2. The actual value of the API token will _only_ be available in this immediate response of the creation, under `.getAttributes().getAccessToken()`.

 :::warning[Can't be accessed again later]
You won't be able to retrieve the actual value of the API token again at a later point, for example when retrieving or updating the API token.
 :::

### Python

```python showLineNumbers title="Create an API token"
from pyatlan.client.atlan import AtlanClient

client = AtlanClient()
token = client.token.create("token-name") # (1)
token_value = token.attributes.access_token # (2)
```

1. You can use the `token.create()` method to create a new API token.
2. The actual value of the API token will _only_ be available in this immediate response of the creation, under `.attributes.access_token`.

 :::warning[Can't be accessed again later]
You won't be able to retrieve the actual value of the API token again at a later point, for example when retrieving or updating the API token.
 :::

### Kotlin

```kotlin showLineNumbers title="Create an API token"
val token = ApiToken.create(client, "token-name") // (1)
val tokenValue = token.attributes.accessToken // (2)
```

1. You can use the `ApiToken.create()` method to create a new API token. Because this operation will create the token in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.
2. The actual value of the API token will _only_ be available in this immediate response of the creation, under `.attributes.accessToken`.

 :::warning[Can't be accessed again later]
You won't be able to retrieve the actual value of the API token again at a later point, for example when retrieving or updating the API token.
 :::

### Go

```go showLineNumbers title="Create an API token"
displayName := "token-name"
token, atlanErr := ctx.TokenClient.Create(&displayName, nil, nil, nil) // (1)
if atlanErr != nil {
 logger.Log.Errorf("Error : %v", atlanErr)
}
tokenValue := *token.Attributes.AccessToken // (2)
```

1. You can use the `TokenClient.Create()` method to create a new API token.
2. The actual value of the API token will _only_ be available in this immediate response of the creation, under `.Attributes.AccessToken`.

 :::warning[Can't be accessed again later]
You won't be able to retrieve the actual value of the API token again at a later point, for example when retrieving or updating the API token.
 :::

### Raw REST API

```json showLineNumbers title="POST /api/service/apikeys"
{
 "displayName": "token-name", // (1)
 "description": "", // (2)
 "personas": [], // (3)
 "personaQualifiedNames": [], // (4)
 "validitySeconds": 409968000 // (5)
}
```

1. You must provide a name for the token when creating it.
2. You can optionally provide a description, but even if you don't want to provide a description must send an empty string for it in the request.
3. You must always send an empty `personas` array (this is now unused, but still required).
4. You can optionally list personas to link the API token with, but even if you don't send any must send an empty list. If you are linking the API token to one or more personas, use their `qualified_name` in the list.

 :::warning[Must be complete set you want linked to the API token]
Any personas you leave out of the set will be _removed_ from the API token, if they're already associated with it. (Further, if you send an empty set or no value at all for `personas` in the update, then ALL linked personas will be removed from the token.)
 :::
5. You also need to specify how long the API token should last before automatically expiring. The value of `409968000` will be interpreted as never expiring.

## Retrieve API token

You can retrieve an API token either by its name or its client ID (the name used when it's associated with some other object).

### Java

```java showLineNumbers title="Retrieve an API token by name"
ApiToken token = ApiToken.retrieveByName(client, "token-name"); // (1)
```

1. The `retrieveByName()` method handles fetching the API token based on its name. Because this operation will retrieve information from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

```java showLineNumbers title="Retrieve an API token by ID"
ApiToken token = client.apiTokens.getById("apikey-ac69de56-6529-4c8f-b53c-791cb5346308"); // (1)
```

1. The `getById()` method on the `apiTokens` member of any client handles fetching the API token based on its client ID. This is the same as the username that will be captured when an API token is assigned to an asset without the `service-account-` prefix.

### Python

```python showLineNumbers title="Retrieve an API token by name"
from pyatlan.client.atlan import AtlanClient

client = AtlanClient()
token = client.token.get_by_name("token-name") # (1)
```

1. The `token.get_by_name()` method handles fetching the API token based on its name.

```python showLineNumbers title="Retrieve an API token by ID"
from pyatlan.client.atlan import AtlanClient

client = AtlanClient()
token = client.token.get_by_id("apikey-ac69de56-6529-4c8f-b53c-791cb5346308") # (1)
```

1. The `token.get_by_id()` method handles fetching the API token based on its client ID. This is the same as the username that will be captured when an API token is assigned to an asset without the `service-account-` prefix.

### Kotlin

```kotlin showLineNumbers title="Retrieve an API token by name"
val token = ApiToken.retrieveByName(client, "token-name") // (1)
```

1. The `retrieveByName()` method handles fetching the API token based on its name. Because this operation will retrieve information from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

```kotlin showLineNumbers title="Retrieve an API token by ID"
val token = client.apiTokens.getById("apikey-ac69de56-6529-4c8f-b53c-791cb5346308") // (1)
```

1. The `getById()` method on the `apiTokens` member of any client handles fetching the API token based on its client ID. This is the same as the username that will be captured when an API token is assigned to an asset without the `service-account-` prefix.

### Go

```go showLineNumbers title="Retrieve an API token by name"
token, atlanErr := ctx.TokenClient.GetByName("token-name") // (1)
```

1. The `TokenClient.GetByName()` method handles fetching the API token based on its name.

```go showLineNumbers title="Retrieve an API token by ID"
token, atlanErr := ctx.TokenClient.GetByID("apikey-ac69de56-6529-4c8f-b53c-791cb5346308") // (1)
```

1. The `TokenClient.GetByID()` method handles fetching the API token based on its client ID. This is the same as the username that will be captured when an API token is assigned to an asset without the `service-account-` prefix.

### Raw REST API

```json showLineNumbers title='GET /api/service/apikeys?limit=2&offset=0&sort=-createdAt&filter={"displayName":"token-name"}'
// (1)
```

1. The search criteria for retrieving an API token by its name is embedded as query parmeters in the request URL.

```json showLineNumbers title='GET /api/service/apikeys?limit=2&offset=0&sort=-createdAt&filter={"clientId":"apikey-ac69de56-6529-4c8f-b53c-791cb5346308"}'
// (1)
```

1. The search criteria for retrieving an API token by its name is embedded as query parmeters in the request URL.

## Update API token

There is limited information to update on an API token:

### Java

```java showLineNumbers title="Update an API token"
ApiToken token = ApiToken.retrieveByName(client, "token-name"); // (1)
ApiToken revised = token.toBuilder() // (2)
 .attributes(token.getAttributes().toBuilder()
 .description("Now with a description.") // (3)
 .build())
 .build();
ApiToken updated = revised.update(client); // (4)
```

1. You are best first retrieving the API token you want to update. Because this operation will retrieve information from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.
2. You can then use the builder pattern to update information within that token, such as its description.
3. Note that some information, like description, is embedded within the attributes of the token.
4. You can then send the revised token information to Atlan to be updated. Because this operation will persist the token in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Python

```python showLineNumbers title="Delete an API token"
from pyatlan.client.atlan import AtlanClient

client = AtlanClient()
token = client.token.get_by_name("token-name") # (1)
client.token.update( # (2)
 token.guid,
 token.display_name,
 description="Now with a description.",
)
```

1. You are best first retrieving the API token you want to update.
2. You can then send the revised token information to Atlan to be updated using the `token.update()` method, and passing the updated information (like the new description).

### Kotlin

```java showLineNumbers title="Update an API token"
val token = ApiToken.retrieveByName(client, "token-name") // (1)
val revised = token.toBuilder() // (2)
 .attributes(token.attributes.toBuilder()
 .description("Now with a description.") // (3)
 .build())
 .build()
val updated = revised.update(client) // (4)
```

1. You are best first retrieving the API token you want to update. Because this operation will retrieve information from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.
2. You can then use the builder pattern to update information within that token, such as its description.
3. Note that some information, like description, is embedded within the attributes of the token.
4. You can then send the revised token information to Atlan to be updated. Because this operation will persist the token in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

### Go

```go showLineNumbers title="Delete an API token"
token, _ := ctx.TokenClient.GetByName("token-name") // (1)
displayName := "Updated name"
description := "Now with a description."
ctx.TokenClient.Update(token.GUID, &displayName, &description, nil) // (2)
```

1. You are best first retrieving the API token you want to update.
2. You can then send the revised token information to Atlan to be updated using the `TokenClient.Update` method, and passing the updated information (like the new description and display name).

### Raw REST API

```json showLineNumbers title="POST /api/service/apikeys/98fb61da-eb8f-455e-b5ea-c022ee390044"
{
 "displayName": "token-name", // (1)
 "description": "Now with a revised description.", // (2)
 "personas": [], // (3)
 "personaQualifiedNames": [] // (4)
}
```

1. You must provide the name for the token when updating it, in addition to its ID (GUID) in the request URL itself.
2. You can provide updates to its description, for example.
3. You must always send an empty `personas` array (this is now unused, but still required).
4. You must also list personas to link the API token with, even if you don't send any you must send an empty list. If you do specify personas to link to the API token, use the `qualified_name` of the personas.

 :::warning[Must be complete set you want linked to the API token]
Any personas you leave out of the set will be _removed_ from the API token, if they're already associated with it. (Further, if you send an empty set or no value at all for `personas` in the update, then ALL linked personas will be removed from the token.)
 :::

## Delete API token

You can delete an API token by its GUID:

### Java

```java showLineNumbers title="Delete an API token"
ApiToken.delete(client, "98fb61da-eb8f-455e-b5ea-c022ee390044"); // (1)
```

1. Note that the GUID of an API token isn't the same as its client ID. From an API token object, the `getId()` method will give you its GUID. Because this operation will remove the token from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

 :::warning[Irreversible]
Once deleted, the API token will be permanently removed and no longer usable.
 :::

### Python

```python showLineNumbers title="Delete an API token"
from pyatlan.client.atlan import AtlanClient

client = AtlanClient()
token = client.token.purge("98fb61da-eb8f-455e-b5ea-c022ee390044") # (1)
```

1. Note that the GUID of an API token isn't the same as its client ID. From an API token object, the `guid` property will give you its GUID.

 :::warning[Irreversible]
Once deleted, the API token will be permanently removed and no longer usable.
 :::

### Kotlin

```kotlin showLineNumbers title="Delete an API token"
ApiToken.delete(client, "98fb61da-eb8f-455e-b5ea-c022ee390044") // (1)
```

1. Note that the GUID of an API token isn't the same as its client ID. From an API token object, the `getId()` method will give you its GUID. Because this operation will remove the token from Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

 :::warning[Irreversible]
Once deleted, the API token will be permanently removed and no longer usable.
 :::

### Go

```go showLineNumbers title="Delete an API token"
ctx.TokenClient.Purge("a853f1d5-f1f4-4cdb-b86d-c61df3ecade6") // (1)
```

1. Note that the GUID of an API token isn't the same as its client ID. From an API token object, the `guid` property will give you its GUID.

 :::warning[Irreversible]
Once deleted, the API token will be permanently removed and no longer usable.
 :::

### Raw REST API

```json showLineNumbers title="DELETE /api/service/apikeys/98fb61da-eb8f-455e-b5ea-c022ee390044"
// (1)
```

1. The criteria for deleting an API token is entirely contained in the request URL. Note that the GUID of an API token isn't the same as its client ID. From an API token response, the `id` property will give you its GUID.

 :::warning[Irreversible]
Once deleted, the API token will be permanently removed and no longer usable.
 :::

## Give permissions to API token

As called out at the top of this page, API tokens are unique actors with their own privileges and permissions. For an API token to be able to interact with certain objects, they must be granted such permissions directly.

### Link API token to persona

You can link an API token to a persona to give the API token all of the permissions granted by the policies within that persona.

### Java

```java showLineNumbers title="Link an API token to persona(s)"
client.apiTokens.update( // (1)
 "98fb61da-eb8f-455e-b5ea-c022ee390044", // (2)
 "token-name",
 null,
 Set.of("default/aQi5KHtGwZYvxGnTSAYO8J")); // (3)
```

1. Use the `update()` method on the `apiTokens` member of any client to link an API token with personas.
2. You will need to provide both the GUID and the display name for the API token.
3. You must also provide the complete set of personas that should be linked to the API token, using their `qualifiedName`s.

 :::warning[Must be complete set you want linked to the API token]
Any personas you leave out of the set will be _removed_ from the API token, if they're already associated with it. (Further, if you send an empty set or no value at all for `personas` in the update, then ALL linked personas will be removed from the token.)
 :::

### Python

```python showLineNumbers title="Link an API token to persona(s)"
from pyatlan.client.atlan import AtlanClient

client = AtlanClient()
client.token.update( # (1)
 guid="98fb61da-eb8f-455e-b5ea-c022ee390044", # (2)
 display_name="token-name",
 personas={"default/aQi5KHtGwZYvxGnTSAYO8J"}, # (3)
)
```

1. Use `token.update()` to link an API token with personas.
2. You will need to provide both the GUID and the display name for the API token.
3. You must also provide the complete set of personas that should be linked to the API token, using their `qualified_name`s.

 :::warning[Must be complete set you want linked to the API token]
Any personas you leave out of the set will be _removed_ from the API token, if they're already associated with it. (Further, if you send an empty set or no value at all for `personas` in the update, then ALL linked personas will be removed from the token.)
 :::

### Kotlin

```kotlin showLineNumbers title="Link an API token to persona(s)"
client.apiTokens.update( // (1)
 "98fb61da-eb8f-455e-b5ea-c022ee390044", // (2)
 "token-name",
 null,
 setOf("default/aQi5KHtGwZYvxGnTSAYO8J")) // (3)
```

1. Use the `update()` method on the `apiTokens` member of any client to link an API token with personas.
2. You will need to provide both the GUID and the display name for the API token.
3. You must also provide the complete set of personas that should be linked to the API token, using their `qualifiedName`s.

 :::warning[Must be complete set you want linked to the API token]
Any personas you leave out of the set will be _removed_ from the API token, if they're already associated with it. (Further, if you send an empty set or no value at all for `personas` in the update, then ALL linked personas will be removed from the token.)
 :::

### Go

```go showLineNumbers title="Link an API token to persona(s)"
displayName := "token-name"
tokenGUID := "98fb61da-eb8f-455e-b5ea-c022ee390044"
ctx.TokenClient.Update( // (1)
 &tokenGUID, // (2)
 &displayName,
 nil,
 []string{"default/aQi5KHtGwZYvxGnTSAYO8J"}, // (3)
)
```

1. Use `TokenClient.Update` to link an API token with personas.
2. You will need to provide both the GUID and the display name for the API token.
3. You must also provide the complete set of personas that should be linked to the API token, using their `qualifiedName`s.

 :::warning[Must be complete set you want linked to the API token]
Any personas you leave out of the set will be _removed_ from the API token, if they're already associated with it. (Further, if you send an empty set or no value at all for `personas` in the update, then ALL linked personas will be removed from the token.)
 :::

### Raw REST API

```json showLineNumbers title="POST /api/service/apikeys/98fb61da-eb8f-455e-b5ea-c022ee390044"
{
 "displayName": "token-name", // (1)
 "description": "", // (2)
 "personas": [], // (3)
 "personaQualifiedNames": ["default/aQi5KHtGwZYvxGnTSAYO8J"] // (4)
}
```

1. You must provide the name for the token when updating it, in addition to its ID (GUID) in the request URL itself.
2. You must provide the description for the token. Even if you don't want it to have any description, you need to send an empty string.
3. You must always send an empty `personas` array (this is now unused, but still required).
4. You must also provide the complete set of personas that should be linked to the API token, using their `qualified_name`s.

 :::warning[Must be complete set you want linked to the API token]
Any personas you leave out of the set will be _removed_ from the API token, if they're already associated with it. (Further, if you send an empty set or no value at all for `personas` in the update, then ALL linked personas will be removed from the token.)
 :::

### Add API token as connection admin

For any actor to manage policies for a connection, that actor must be a connection admin on the connection. You must therefore add the API token as a connection admin to any connection you want it to be able to manage policies for.[^1]

:::warning[Must first obtain a user's bearer token]
To carry out this operation, you must first [obtain a user's bearer token](#obtain-users-bearer-token). Specifically, you must obtain the bearer token for a user who is _already_ a connection admin on the connection.
:::

### Java

```java showLineNumbers title="Add an API token as connection admin"
List connections = Connection.findByName(
 client,
 "development",
 AtlanConnectorType.BIGQUERY);
Connection connection = connections.get(0); // (1)
String impersonationToken = "eyNnCJd2T9Y8fEsbdx..."; // (2)
AssetMutationResponse response = connection.addApiTokenAsAdmin(client, impersonationToken); // (3)
```

1. You will need to start by retrieving the connection you want to add the API token to as a connection admin. In this example, we use a search to retrieve the connection.
2. You must [use a user's bearer token](#obtain-users-bearer-token) as an impersonation token. (We would recommend capturing this in something like an environment variable rather than embedding directly in the code.)
3. You can use the `.addApiTokenAsAdmin()` method to add the API token as a connection admin. Because this operation will update the connection in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

 :::warning[Will add the API token, not impersonation token, as connection admin]
Note that you are providing a user's bearer token as the `impersonationToken` _only_ to give sufficient privileges to add the API token configured for the SDK as a connection admin. It's the API token configured for the SDK that you're adding as connection admin, not the user's bearer token. (The user's bearer token must _already_ have connection admin permissions on this connection for the operation to succeed.)
 :::

### Python

```python showLineNumbers title="Add an API token as connection admin"
from pyatlan.client.atlan import AtlanClient

connections = client.asset.find_connections_by_name(
 "development", AtlanConnectorType.BIGQUERY
)
connection = connections[0] # (1)
impersonation_token = "eyNnCJd2T9Y8fEsbdx..." # (2)
response = client.user.add_as_admin( # (3)
 asset_guid=connection.guid,
 impersonation_token=impersonation_token,
)
```

1. You will need to start by retrieving the connection you want to add the API token to as a connection admin. In this example, we use a search to retrieve the connection.
2. You must [use a user's bearer token](#obtain-users-bearer-token) as an impersonation token. (We would recommend capturing this in something like an environment variable rather than embedding directly in the code.)
3. You can use the `user.add_as_admin()` method to add the API token as a connection admin, providing the GUID of the connection and the impersonation token.

 :::warning[Will add the API token, not impersonation token, as connection admin]
Note that you are providing a user's bearer token as the `impersonation_token` _only_ to give sufficient privileges to add the API token configured for the SDK as a connection admin. It's the API token configured for the SDK that you're adding as connection admin, not the user's bearer token. (The user's bearer token must _already_ have connection admin permissions on this connection for the operation to succeed.)
 :::

### Kotlin

```kotlin showLineNumbers title="Add an API token as connection admin"
val connections = Connection.findByName(
 client,
 "development",
 AtlanConnectorType.BIGQUERY)
val connection = connections[0] // (1)
val impersonationToken = "eyNnCJd2T9Y8fEsbdx..." // (2)
val response = connection.addApiTokenAsAdmin(client, impersonationToken) // (3)
```

1. You will need to start by retrieving the connection you want to add the API token to as a connection admin. In this example, we use a search to retrieve the connection.
2. You must [use a user's bearer token](#obtain-users-bearer-token) as an impersonation token. (We would recommend capturing this in something like an environment variable rather than embedding directly in the code.)
3. You can use the `.addApiTokenAsAdmin()` method to add the API token as a connection admin. Because this operation will update the connection in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

 :::warning[Will add the API token, not impersonation token, as connection admin]
Note that you are providing a user's bearer token as the `impersonationToken` _only_ to give sufficient privileges to add the API token configured for the SDK as a connection admin. It's the API token configured for the SDK that you're adding as connection admin, not the user's bearer token. (The user's bearer token must _already_ have connection admin permissions on this connection for the operation to succeed.)
 :::

### Raw REST API

:::warning[Not recommended]
We don't recommend attempting this change through the Raw REST APIs as it requires a number of different API calls, carefully parsing the responses and combining elements, and has high potential for minor typos or copy/paste errors to cause mistakes.
:::

### Add API token as collection editor

As with connections, if you want an API token to be able to manage a query collection or the queries within it, you must add the API token as an editor on the collection.

:::warning[Must first obtain a user's bearer token]
To carry out this operation, you must first [obtain a user's bearer token](#obtain-users-bearer-token). Specifically, you must obtain the bearer token for a user who is _already_ an editor on (or owner of) the query collection.
:::

### Java

```java showLineNumbers title="Add an API token as collection editor"
List collections = AtlanCollection.findByName(
 client,
 "My query collection");
AtlanCollection collection = collections.get(0); // (1)
String impersonationToken = "eyNnCJd2T9Y8fEsbdx..."; // (2)
AssetMutationResponse response = collection.addApiTokenAsAdmin(client, impersonationToken); // (3)
```

1. You will need to start by retrieving the query collection you want to add the API token to as an editor. In this example, we use a search to retrieve the collection.
2. You must [use a user's bearer token](#obtain-users-bearer-token) as an impersonation token. (We would recommend capturing this in something like an environment variable rather than embedding directly in the code.)
3. You can use the `.addApiTokenAsAdmin()` method to add the API token as a collection editor. Because this operation will update the collection in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

 :::warning[Will add the API token, not impersonation token, as collection editor]
Note that you are providing a user's bearer token as the `impersonationToken` _only_ to give sufficient privileges to add the API token configured for the SDK as a collection editor. It's the API token configured for the SDK that you're adding as collection editor, not the user's bearer token. (The user's bearer token must _already_ have collection editor permissions on this collection for the operation to succeed.)
 :::

### Python

```python showLineNumbers title="Add an API token as collection editor"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import Collection
from pyatlan.model.fluent_search import FluentSearch

request = (
 FluentSearch()
 .where(FluentSearch.asset_type(Collection))
 .where(Collection.NAME.eq("My query collection"))
).to_request()
response = client.asset.search(request)
collection = response.current_page()[0] # (1)
impersonation_token = "eyNnCJd2T9Y8fEsbdx..." # (2)
response = client.user.add_as_admin( # (3)
 asset_guid=collection.guid,
 impersonation_token=impersonation_token,
)
```

1. You will need to start by retrieving the query collection you want to add the API token to as a collection editor. In this example, we use a search to retrieve the collection.
2. You must [use a user's bearer token](#obtain-users-bearer-token) as an impersonation token. (We would recommend capturing this in something like an environment variable rather than embedding directly in the code.)
3. You can use the `user.add_as_admin()` method to add the API token as a collection editor, providing the GUID of the collection and the impersonation token.

 :::warning[Will add the API token, not impersonation token, as collection editor]
Note that you are providing a user's bearer token as the `impersonation_token` _only_ to give sufficient privileges to add the API token configured for the SDK as a collection editor. It's the API token configured for the SDK that you're adding as collection editor, not the user's bearer token. (The user's bearer token must _already_ have collection editor permissions on this collection for the operation to succeed.)
 :::

### Kotlin

```kotlin showLineNumbers title="Add an API token as collection editor"
val collections = AtlanCollection.findByName(
 client,
 "My query collection")
val collection = collections[0] // (1)
val impersonationToken = "eyNnCJd2T9Y8fEsbdx..." // (2)
val response = collection.addApiTokenAsAdmin(client, impersonationToken) // (3)
```

1. You will need to start by retrieving the query collection you want to add the API token to as an editor. In this example, we use a search to retrieve the collection.
2. You must [use a user's bearer token](#obtain-users-bearer-token) as an impersonation token. (We would recommend capturing this in something like an environment variable rather than embedding directly in the code.)
3. You can use the `.addApiTokenAsAdmin()` method to add the API token as a collection editor. Because this operation will update the collection in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

 :::warning[Will add the API token, not impersonation token, as collection editor]
Note that you are providing a user's bearer token as the `impersonationToken` _only_ to give sufficient privileges to add the API token configured for the SDK as a collection editor. It's the API token configured for the SDK that you're adding as collection editor, not the user's bearer token. (The user's bearer token must _already_ have collection editor permissions on this collection for the operation to succeed.)
 :::

### Raw REST API

:::warning[Not recommended]
We don't recommend attempting this change through the Raw REST APIs as it requires a number of different API calls, carefully parsing the responses and combining elements, and has high potential for minor typos or copy/paste errors to cause mistakes.
:::

### Add API token as collection viewer

Alternatively, if you only want the API token to be able to view and run queries within a collection (but not change them), you can add the API token as a viewer on the collection.

:::warning[Must first obtain a user's bearer token]
To carry out this operation, you must first [obtain a user's bearer token](#obtain-users-bearer-token). Specifically, you must obtain the bearer token for a user who is _already_ an editor on (or owner of) the query collection.
:::

### Java

```java showLineNumbers title="Add an API token as collection viewer"
List collections = AtlanCollection.findByName(
 client,
 "My query collection");
AtlanCollection collection = collections.get(0); // (1)
String impersonationToken = "eyNnCJd2T9Y8fEsbdx..."; // (2)
AssetMutationResponse response = collection.addApiTokenAsViewer(client, impersonationToken); // (3)
```

1. You will need to start by retrieving the query collection you want to add the API token to as an viewer. In this example, we use a search to retrieve the collection.
2. You must [use a user's bearer token](#obtain-users-bearer-token) as an impersonation token. (We would recommend capturing this in something like an environment variable rather than embedding directly in the code.)
3. You can use the `.addApiTokenAsViewer()` method to add the API token as a collection viewer. Because this operation will update the collection in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

 :::warning[Will add the API token, not impersonation token, as collection viewer]
Note that you are providing a user's bearer token as the `impersonationToken` _only_ to give sufficient privileges to add the API token configured for the SDK as a collection viewer. It's the API token configured for the SDK that you're adding as collection viewer, not the user's bearer token. (The user's bearer token must _already_ have collection editor permissions on this collection for the operation to succeed.)
 :::

### Python

```python showLineNumbers title="Add an API token as collection viewer"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import Collection
from pyatlan.model.fluent_search import FluentSearch

request = (
 FluentSearch()
 .where(FluentSearch.asset_type(Collection))
 .where(Collection.NAME.eq("My query collection"))
).to_request()
response = client.asset.search(request)
collection = response.current_page()[0] # (1)
impersonation_token = "eyNnCJd2T9Y8fEsbdx..." # (2)
response = client.user.add_as_viewer( # (3)
 asset_guid=collection.guid,
 impersonation_token=impersonation_token,
)
```

1. You will need to start by retrieving the query collection you want to add the API token to as a collection viewer. In this example, we use a search to retrieve the collection.
2. You must [use a user's bearer token](#obtain-users-bearer-token) as an impersonation token. (We would recommend capturing this in something like an environment variable rather than embedding directly in the code.)
3. You can use the `user.add_as_admin()` method to add the API token as a collection viewer, providing the GUID of the collection and the impersonation token.

 :::warning[Will add the API token, not impersonation token, as collection viewer]
Note that you are providing a user's bearer token as the `impersonation_token` _only_ to give sufficient privileges to add the API token configured for the SDK as a collection viewer. It's the API token configured for the SDK that you're adding as collection viewer, not the user's bearer token. (The user's bearer token must _already_ have collection editor permissions on this collection for the operation to succeed.)
 :::

### Kotlin

```kotlin showLineNumbers title="Add an API token as collection viewer"
val collections = AtlanCollection.findByName(
 client,
 "My query collection")
val collection = collections[0] // (1)
val impersonationToken = "eyNnCJd2T9Y8fEsbdx..." // (2)
val response = collection.addApiTokenAsViewer(client, impersonationToken) // (3)
```

1. You will need to start by retrieving the query collection you want to add the API token to as an viewer. In this example, we use a search to retrieve the collection.
2. You must [use a user's bearer token](#obtain-users-bearer-token) as an impersonation token. (We would recommend capturing this in something like an environment variable rather than embedding directly in the code.)
3. You can use the `.addApiTokenAsViewer()` method to add the API token as a collection viewer. Because this operation will update the collection in Atlan, you must [provide it an `AtlanClient`](https://docs.atlan.com/llms/platform/python/set-up-sdk/llms.txt) through which to connect to the tenant.

 :::warning[Will add the API token, not impersonation token, as collection viewer]
Note that you are providing a user's bearer token as the `impersonationToken` _only_ to give sufficient privileges to add the API token configured for the SDK as a collection viewer. It's the API token configured for the SDK that you're adding as collection viewer, not the user's bearer token. (The user's bearer token must _already_ have collection editor permissions on this collection for the operation to succeed.)
 :::

### Raw REST API

:::warning[Not recommended]
We don't recommend attempting this change through the Raw REST APIs as it requires a number of different API calls, carefully parsing the responses and combining elements, and has high potential for minor typos or copy/paste errors to cause mistakes.
:::

## Obtain user's bearer token

For a few of the operations above, there is a bit of a "catch-22". For example:

- An API token can't manage policies for a connection if it'sn't the connection admin.
- An API token can't be used to add itself as a connection admin.[^2]
- So how do you get an API token set up as a connection admin, without using an API token?

The answer is that you must use a user's own bearer token (temporarily)—specifically a user who already has the appropriate authority on the object. (In this example, a user who is already a connection admin on that connection.)

To do this, you'll need to[^3]:

1. Log in to Atlan as the user with the level of access required (for example, the user who is a connection admin).
2. Open the **Developer Tools** view of Chrome:
 1. Open the **View** menu,
 2. then **Developer**,
 3. then **Developer Tools**.
3. Leave the Developer Tools window open, but change back to your original Atlan window.
4. Navigate to the **Assets** page where you can discover assets.
5. Return to the Developer Tools window and find any **indexsearch** item along the left:
 1. Right-click the **indexsearch** item,
 2. then **Copy**,
 3. then **Copy as cURL**.

```text title="Copied cURL" showLineNumbers hl_lines="5"
curl 'https://tenant.atlan.com/api/meta/search/indexsearch' \
 -H 'authority: tenant.atlan.com' \
 -H 'accept: application/json, text/plain, */*' \
 -H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8' \
 -H 'authorization: Bearer eyNnCJd2T9Y8fEsbdxTgKQqyWFm7uNBvw55EjAIakh9Yrg3cdd1YoNMXr1LtFmreHfVrYrSRxCzUYcoJKASXovfBO5PnGZOWe8hAdxb7WqesNZS.TPFcWzwRLA2Aeb38iWBIAG3rrsTz7iyufecbPeLBLTZ2RjaweLji7PGIVz5Mj8G2bAIPM7tguCGbz...' \
 -H 'content-type: application/json' \
 ...
```

The details you need are on the highligted line 5, that begins with `-H 'authorization: Bearer`. You specifically want everything _after_ the word `Bearer`, up to the final single quote (`'`) at the end of the line. So in this example you would copy:

```text
eyNnCJd2T9Y8fEsbdxTgKQqyWFm7uNBvw55EjAIakh9Yrg3cdd1YoNMXr1LtFmreHfVrYrSRxCzUYcoJKASXovfBO5PnGZOWe8hAdxb7WqesNZS.TPFcWzwRLA2Aeb38iWBIAG3rrsTz7iyufecbPeLBLTZ2RjaweLji7PGIVz5Mj8G2bAIPM7tguCGbz...
```

This is the user's own bearer token.

:::warning[Has a limited lifespan]
Be aware that the user's own bearer token will have a limited lifespan. At some point the user will be automatically logged out and will be asked to log in again, at which point this bearer token will have expired and need to be re-retrieved.
:::
[^1]: The non-obvious exception to this rule is where the API token was used to create a connection in the first place. If you use an API token to create a connection, the API token is _automatically_ set up as a connection admin for that connection as part of its creation.
[^2]: Hopefully the reason is self-apparent, but for clarity, if this _were_ possible then API tokens could be used to bypass the security inherent in connection administration (policy management for the connection and its assets).
[^3]: The steps shown assume you are doing this via the Chrome browser. Similar methods should exist in other browsers; if you are facing difficulty, please reach out with which browser you are using and we can help you through.
[^4]: While this example bearer token may "look" real, it's entirely made up of random numbers and letters. Don't try to use this example bearer token, as it's guaranteed not to work.

---
