> ## Documentation Index
> Fetch the complete documentation index at: https://developer.box.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Annotator Tokens

export const Link = ({href, children, className, ...props}) => {
  const localizedHref = localizeLink(href);
  return <a href={localizedHref} className={className} {...props}>
      {children}
    </a>;
};

export const MultiRelatedLinks = ({sections = []}) => {
  if (!sections || sections.length === 0) {
    return null;
  }
  return <div className="space-y-8">
      {sections.map((section, index) => <RelatedLinks key={index} title={section.title} items={section.items} />)}
    </div>;
};

export const RelatedLinks = ({title, items = []}) => {
  const getBadgeClass = badge => {
    if (!badge) return "badge-default";
    const badgeType = badge.toLowerCase().replace(/\s+/g, "-");
    return `badge-${badge === "ガイド" ? "guide" : badgeType}`;
  };
  if (!items || items.length === 0) {
    return null;
  }
  return <div className="my-8">
      {}
      <h3 className="text-sm font-bold uppercase tracking-wider mb-4">{title}</h3>

      {}
      <div className="flex flex-col gap-3">
        {items.map((item, index) => <a key={index} href={item.href} className="py-2 px-3 rounded related_link hover:bg-[#f2f2f2] dark:hover:bg-[#111827] flex items-center gap-3 group no-underline hover:no-underline border-b-0">
            {}
            <span className={`px-2 py-1 rounded-full text-xs font-semibold uppercase tracking-wide flex-shrink-0 ${getBadgeClass(item.badge)}`}>
              {item.badge}
            </span>

            {}
            <span className="text-base">{item.label}</span>
          </a>)}
      </div>
    </div>;
};

Annotations is one of the key features supported by new Box View, that allows
developers to provide collaboration capabilities right from within the embedded
Box preview in their application.

Box View supports three annotation types: highlight only, highlight
annotation, and point annotation. Annotations are only supported on document
and image previews.

<Frame border>
  <img src="https://mintcdn.com/box/J_EwM_J-GUl8Mc67/guides/authentication/tokens/annotator-tokens.png?fit=max&auto=format&n=J_EwM_J-GUl8Mc67&q=85&s=bb0e2def10a12ea6862206c325021efc" alt="How annotator tokens work" width="1326" height="682" data-path="guides/authentication/tokens/annotator-tokens.png" />
</Frame>

## What is an Annotator Token

An Annotator Token is an Access Token that allows an application to create a
Preview Embed Link for a file that a user can make annotations on. As an
application might not create a new App User for every one of the application's
users, the Annotator token allows the application to track which of their
own users made the annotations.

The Annotator Token is used instead of a regular Access Token or File
Token to generate a preview session (an expiring embed link) that is linked to a
unique user ID and display name.

<Warning>
  Since a preview session generated using an annotator token is tied to a
  specific external user, it is strongly recommended that an application
  generates different preview sessions using different annotator tokens for
  different end users of an application.
</Warning>

## External user info

The external display name associated with an annotation is essentially a
stateless "label" appended to the annotation. This means that once an annotation
has been added, the display name is permanently associated with the annotation
and cannot be updated unless the annotation is deleted and added again with the
updated display name.

## Create without SDKs

To create an annotator token, follow the instructions for [manually
authenticating through JWT](/guides/authentication/jwt/without-sdk) but replace the
JWT claim with the following data.

<CodeGroup>
  ```csharp .Net theme={null}
  var claims = new List<Claim>{
      new Claim("sub", '[EXTERNAL_USER_ID]'),
      new Claim("name", '[EXTERNAL_USER_DISPLAY_NAME]'),
      new Claim("box_sub_type", "external"),
      new Claim("jti", jti),
  };
  ```

  ```java Java theme={null}
  JwtClaims claims = new JwtClaims();
  claims.setIssuer(config.boxAppSettings.clientID);
  claims.setAudience(authenticationUrl);
  claims.setSubject("[EXTERNAL_USER_ID]");
  claims.setName("[EXTERNAL_USER_DISPLAY_NAME]");
  claims.setClaim("box_sub_type", "external");
  claims.setGeneratedJwtId(64);
  claims.setExpirationTimeMinutesInTheFuture(0.75f);
  ```

  ```python Python theme={null}
  claims = {
      'iss': config['boxAppSettings']['clientID'],
      'sub': '[EXTERNAL_USER_ID]',
      'name': '[EXTERNAL_USER_DISPLAY_NAME]',
      'box_sub_type': 'external',
      'aud': authentication_url,
      'jti': secrets.token_hex(64),
      'exp': round(time.time()) + 45
  }
  ```

  ```js Node theme={null}
  let claims = {
      iss: config.boxAppSettings.clientID,
      sub: "[EXTERNAL_USER_ID]",
      name: "[EXTERNAL_USER_DISPLAY_NAME]",
      box_sub_type: "external",
      aud: authenticationUrl,
      jti: crypto.randomBytes(64).toString("hex"),
      exp: Math.floor(Date.now() / 1000) + 45
  };
  ```

  ```ruby Ruby theme={null}
  claims = {
    iss: config['boxAppSettings']['clientID'],
    sub: "[EXTERNAL_USER_ID]",
    name: "[EXTERNAL_USER_DISPLAY_NAME]",
    box_sub_type: 'external',
    aud: authentication_url,
    jti: SecureRandom.hex(64),
    exp: Time.now.to_i + 45
  }
  ```

  ```php PHP theme={null}
  $claims = [
    'iss' => $config->boxAppSettings->clientID,
    'sub' => '[EXTERNAL_USER_ID]',
    'name' => '[EXTERNAL_USER_DISPLAY_NAME]',
    'box_sub_type' => 'external',
    'aud' => $authenticationUrl,
    'jti' => base64_encode(random_bytes(64)),
    'exp' => time() + 45,
    'kid' => $config->boxAppSettings->appAuth->publicKeyID
  ];
  ```
</CodeGroup>

| Parameter      | Type   | Description                                                                                             |
| -------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `sub`          | String | The external user ID to tie this annotation to. This can be any arbitrary ID tracked by the application |
| `box_sub_type` | String | `external` to signify an external user ID                                                               |
| `box_sub_type` | String | The external user name to tie this annotation to. This will be displayed in the Box UI                  |

Then, convert this claim to an assertion according to the guide and pass this
assertion to the <Link href="/reference/post-oauth2-token">`POST /oauth2/token`</Link>
endpoint together with an existing valid Access Token or File Token,
as well as a set of scopes, and the resource for which to create the token.

<CodeGroup>
  ```csharp .Net theme={null}
  var content = new FormUrlEncodedContent(new[]
  {
      new KeyValuePair<string, string>(
          "grant_type", "urn:ietf:params:oauth:grant-type:token-exchange"),
      new KeyValuePair<string, string>(
          "resource", "https://api.box.com/2.0/files/123456"),
      new KeyValuePair<string, string>(
          "subject_token", "[ACCESS_TOKEN]"),
      new KeyValuePair<string, string>(
          "subject_token_type", "urn:ietf:params:oauth:token-type:access_token"),
      new KeyValuePair<string, string>(
          "scope", "item_preview"),
      new KeyValuePair<string, string>(
          "actor_token", "[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]"),
      new KeyValuePair<string, string>(
          "actor_token_type", "urn:ietf:params:oauth:token-type:id_token"),
  });
  ```

  ```java Java theme={null}
  List<NameValuePair> params = new ArrayList<NameValuePair>();

  params.add(new BasicNameValuePair(
      "grant_type", "urn:ietf:params:oauth:grant-type:token-exchange"));
  params.add(new BasicNameValuePair(
      "resource", "https://api.box.com/2.0/files/123456"));
  params.add(new BasicNameValuePair(
      "subject_token", "[ACCESS_TOKEN]"));
  params.add(new BasicNameValuePair(
      "subject_token_type", "urn:ietf:params:oauth:token-type:access_token"));
  params.add(new BasicNameValuePair(
      "scope", "item_preview"));
  params.add(new BasicNameValuePair(
      "actor_token", "[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]"));
  params.add(new BasicNameValuePair(
      "actor_token_type", "urn:ietf:params:oauth:token-type:id_token"));
  ```

  ```python Python theme={null}
  params = urlencode({
      'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
      'resource': 'https://api.box.com/2.0/files/123456',
      'subject_token': '[ACCESS_TOKEN]',
      'subject_token_type': 'urn:ietf:params:oauth:token-type:access_token',
      'scope': 'item_preview',
      'actor_token': '[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]',
      'actor_token_type': 'urn:ietf:params:oauth:token-type:id_token'
  }).encode()
  ```

  ```js Node theme={null}
  let accessToken = await axios
      .post(
          authenticationUrl,
          querystring.stringify({
              grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
              resource: "https://api.box.com/2.0/files/123456",
              subject_token: "[ACCESS_TOKEN]",
              subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
              scope: "item_preview",
              actor_token: "[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]",
              actor_token_type: "urn:ietf:params:oauth:token-type:id_token"
          })
      )
      .then(response => response.data.access_token);
  ```

  ```ruby Ruby theme={null}
  params = URI.encode_www_form({
    grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
    resource: 'https://api.box.com/2.0/files/123456',
    subject_token: '[ACCESS_TOKEN]',
    subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',
    scope: 'item_preview',
    actor_token: '[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]',
    actor_token_type: 'urn:ietf:params:oauth:token-type:id_token'
  })
  ```

  ```php PHP theme={null}
  $params = [
    'grant_type' => 'urn:ietf:params:oauth:grant-type:token-exchange',
    'resource' => 'https://api.box.com/2.0/files/123456',
    'subject_token' => '[ACCESS_TOKEN]',
    'subject_token_type' => 'urn:ietf:params:oauth:token-type:access_token',
    'scope' => 'item_preview',
    'actor_token' => '[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]',
    'actor_token_type' => 'urn:ietf:params:oauth:token-type:id_token'
  ];
  ```
</CodeGroup>

| Parameter          | Description                                                              |
| ------------------ | ------------------------------------------------------------------------ |
| `resource`         | An optional full URL path to the file the token should be restricted to. |
| `actor_token`      | The JWT assertion created earlier                                        |
| `actor_token_type` | Always set to `urn:ietf:params:oauth:token-type:id_token`                |

## Create with SDKs

To create a JWT annotator token with an SDK an application can exchange any
active token for another token.

<CodeGroup>
  ```js Node theme={null}
  var options = {
      actor: {
          id: "[EXTERNAL_USER_ID]",
          name: "[EXTERNAL_USER_DISPLAY_NAME"
      }
  };

  client
      .exchangeToken(
          "item_preview",
          "https://api.box.com/2.0/files/123456",
          options
      )
      .then(tokenInfo => {
          //=> tokenInfo.accessToken
      });
  ```
</CodeGroup>

<RelatedLinks
  title="RELATED APIS"
  items={[
{ label: translate("Request access token"), href: "/reference/post-oauth2-token", badge: "POST" }
]}
/>

<RelatedLinks
  title="RELATED GUIDES"
  items={[
{ label: translate("JWT without SDKs"), href: "/guides/authentication/jwt/without-sdk", badge: "GUIDE" }
]}
/>
