# Personal Access Token

A *PersonalAccessToken* is a long-lived token a Weegloo platform account (Weegloo User) uses to call the CMA, Upload, and CDA with its own privileges from a server, CI, or script. Unlike the short-lived tokens that the browser login flow issues each time, once issued it stays valid until you delete it.

A *PersonalAccessToken* is bound to a user account rather than to a *Space*. That is why its endpoint lives at the top-level `/personal-access-tokens` path instead of under `/spaces/{spaceId}`, and why there is no `spaceId` path variable. A caller using this token acts in every *Space* the user belongs to, with exactly the privileges each *Space*'s *SpaceRole* grants.

## Resource structure {#resource-structure}

The following is the response when a *PersonalAccessToken* is created. The token value and scope live in `sys` (system properties), while `name` is a body property.

```json
{
  "sys": {
    "id": "5KmQ2pVnRb8sTfWcXd3LhJ9gAe",
    "type": "PersonalAccessToken",
    "createdBy": { "sys": { "id": "2bN7kRpQ9mWx4Lt6Vy0Cf3Hs8", "type": "Refer", "targetType": "User" } },
    "createdAt": "2026-06-18T11:41:47.409Z",
    "updatedBy": { "sys": { "id": "2bN7kRpQ9mWx4Lt6Vy0Cf3Hs8", "type": "Refer", "targetType": "User" } },
    "updatedAt": "2026-06-18T11:41:47.409Z",
    "accessToken": "PSNAT5SCq8Lm2vK9pXfR1Zt0Nc4Wd6Hg5Ua2Ee9Ck3PoYx8Bj6Hg5Ua2Ee9Ck3Po…",
    "scopes": ["PERSONAL_ACCESS_TOKEN"]
  },
  "name": "Product sync server"
}
```

Key properties:

- `sys.id`: The unique identifier of the *PersonalAccessToken*. It goes into `{personalAccessTokenId}` in the single-read and delete paths.
- `sys.accessToken`: The secret token value used to call the API. The same value is returned not only in the create response but also in subsequent read responses, so handle it with care about exposure (see the security section below).
- `sys.scopes`: The token's permission scope. A *PersonalAccessToken* is always `["PERSONAL_ACCESS_TOKEN"]` at issue time.
- `name`: The token name you specify on creation (e.g. `Product sync server`).

The `accessToken` in the example above is a secret value, so it is replaced with an example string. In reality it is a long, opaque string that starts with `PSNAT`, and reading it again after issuance returns the same value.

## System properties (sys) {#system-properties-sys}

Every *PersonalAccessToken* carries common system properties and token-specific properties in the `sys` object. `createdBy` and `updatedBy` are in the `Refer` shape (`{ "sys": { "id", "type": "Refer", "targetType" } }`). This resource has no `space`, because it is a user-level token bound to a user account rather than to a *Space*.

| Property | Type | Description |
|---|---|---|
| `id` | string | Unique resource identifier. |
| `type` | string | Resource kind. For a *PersonalAccessToken* this is always `"PersonalAccessToken"`. |
| `createdBy` | Refer&lt;User&gt; | The user who created it. |
| `createdAt` | string (date-time) | Creation time. |
| `updatedBy` | Refer&lt;User&gt; | The user who last updated it. |
| `updatedAt` | string (date-time) | Last update time. |
| `accessToken` | string | The secret token value used to call the API. **Since it is returned as-is in both create and read responses,** handle it so it is not exposed externally. |
| `scopes` | string array | The token's permission scope. A *PersonalAccessToken* is always `["PERSONAL_ACCESS_TOKEN"]`. |

Body properties:

| Property | Type | Description |
|---|---|---|
| `name` | string (1-64) | Token name. Specified on creation. |

## Security: do not expose it {#security-do-not-expose-it}

A *PersonalAccessToken* carries that user's identity as-is. Calling with this token can invoke the CMA, Upload, and CDA with the *SpaceRole* privileges of every *Space* membership the user holds. Because its scope is broad and its lifetime is long, once it leaks the user's entire set of privileges is exposed.

- **Treat it as a secret so it is never exposed anywhere.** The moment it leaks, the user's entire set of privileges is handed over externally. A *PersonalAccessToken* is for trusted environments only, such as servers and CI, and must never be left in plain text in code, logs, storage, or error messages.
- **Do not put it in browser or client code.** A value sent down to the browser is something the user can inspect directly, so it is effectively public. Admin screens that run in the browser use the short-lived token obtained through the content studio login flow (scoped to the screen session).
- For read-only delivery exposed to visitors, use a *DeliveryAccessToken* bound to a least-privilege *SpaceRole*. Do not use a *PersonalAccessToken* in a public client.
- `accessToken` returns the same value not only at creation but also in read responses. Take care not to leave read results as-is in logs, on screen, or in external storage.
- **There is no update API** (no PUT or PATCH). To rotate a token, delete the existing one and issue a new one. If you suspect exposure, delete it immediately to invalidate it and switch to a new token.

## API {#api}

The base URL for all endpoints below is `https://cma.weegloo.com/v1`, and a Bearer token that authenticates against CMA is required in the `Authorization` header. Because a *PersonalAccessToken* is a user-level resource, there is no `spaceId` in the path. There is also no update API, so only four operations exist: list, create, single-read, and delete.

```api-endpoint
{
  "title": "List Personal Access Tokens",
  "method": "GET",
  "path": "/personal-access-tokens",
  "description": "Reads the current user's list of Personal Access Tokens, page by page. Because this resource is not under a Space, there is no spaceId in the path.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {},
  "queryParameterSchema": {
    "limit": { "type": "integer", "description": "Number of items to return per page (1-100)", "default": 15 },
    "skip": { "type": "integer", "description": "Number of items to skip", "default": 0 },
    "next": { "type": "string", "description": "Next page cursor (the value obtained from links.next in the previous response)" },
    "prev": { "type": "string", "description": "Previous page cursor (the value obtained from links.prev in the previous response)" },
    "order": { "type": "string", "description": "Sort criteria. Comma-separated for multi-level sorting (e.g. sys.createdAt,sys.id). Default sys.createdAt,sys.id" },
    "include": { "type": "integer", "description": "Related-resource inclusion level. 0=default, 1=related resources, 2=nested relations, 3=full", "default": 0 }
  },
  "additionalQueryParams": true,
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "sys": { "type": "TotalPageResponse" },
    "limit": 15,
    "totalCount": 1,
    "items": [
      {
        "sys": {
          "id": "5KmQ2pVnRb8sTfWcXd3LhJ9gAe",
          "type": "PersonalAccessToken",
          "createdBy": { "sys": { "id": "2bN7kRpQ9mWx4Lt6Vy0Cf3Hs8", "type": "Refer", "targetType": "User" } },
          "createdAt": "2026-06-18T11:41:47.409Z",
          "updatedBy": { "sys": { "id": "2bN7kRpQ9mWx4Lt6Vy0Cf3Hs8", "type": "Refer", "targetType": "User" } },
          "updatedAt": "2026-06-18T11:41:47.409Z",
          "accessToken": "PSNAT5SCq8Lm2vK9pXfR1Zt0Nc4Wd6Hg5Ua2Ee9Ck3PoYx8Bj6Hg5Ua2Ee9Ck3Po…",
          "scopes": ["PERSONAL_ACCESS_TOKEN"]
        },
        "name": "Product sync server"
      }
    ],
    "links": { "self": "/v1/personal-access-tokens" }
  }
}
```

```api-endpoint
{
  "title": "Create Personal Access Token",
  "method": "POST",
  "path": "/personal-access-tokens",
  "description": "Issues a new Personal Access Token for the current user. The body takes only the token name (name). The sys.accessToken in the response is the secret value used to call the API, and the same value is returned on subsequent reads, so handle it with care about exposure.",
  "responseStatus": 201,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {},
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "requestBodySchema": {
    "type": "object",
    "required": ["name"],
    "properties": {
      "name": { "type": "string", "minLength": 1, "maxLength": 64, "description": "Token name" }
    },
    "example": {
      "name": "Product sync server"
    }
  },
  "responseExample": {
    "sys": {
      "id": "5KmQ2pVnRb8sTfWcXd3LhJ9gAe",
      "type": "PersonalAccessToken",
      "createdBy": { "sys": { "id": "2bN7kRpQ9mWx4Lt6Vy0Cf3Hs8", "type": "Refer", "targetType": "User" } },
      "createdAt": "2026-06-18T11:41:47.409Z",
      "updatedBy": { "sys": { "id": "2bN7kRpQ9mWx4Lt6Vy0Cf3Hs8", "type": "Refer", "targetType": "User" } },
      "updatedAt": "2026-06-18T11:41:47.409Z",
      "accessToken": "PSNAT5SCq8Lm2vK9pXfR1Zt0Nc4Wd6Hg5Ua2Ee9Ck3PoYx8Bj6Hg5Ua2Ee9Ck3Po…",
      "scopes": ["PERSONAL_ACCESS_TOKEN"]
    },
    "name": "Product sync server"
  }
}
```

```api-endpoint
{
  "title": "Read a single Personal Access Token",
  "method": "GET",
  "path": "/personal-access-tokens/{personalAccessTokenId}",
  "description": "Reads one Personal Access Token by sys.id. The sys.accessToken in the response returns the secret token value from issue time as-is.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "personalAccessTokenId": { "type": "string", "description": "The sys.id of the Personal Access Token", "required": true }
  },
  "queryParameterSchema": {
    "include": { "type": "integer", "description": "Related-resource inclusion level. 0=default, 1=related resources, 2=nested relations, 3=full", "default": 0 }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "sys": {
      "id": "5KmQ2pVnRb8sTfWcXd3LhJ9gAe",
      "type": "PersonalAccessToken",
      "createdBy": { "sys": { "id": "2bN7kRpQ9mWx4Lt6Vy0Cf3Hs8", "type": "Refer", "targetType": "User" } },
      "createdAt": "2026-06-18T11:41:47.409Z",
      "updatedBy": { "sys": { "id": "2bN7kRpQ9mWx4Lt6Vy0Cf3Hs8", "type": "Refer", "targetType": "User" } },
      "updatedAt": "2026-06-18T11:41:47.409Z",
      "accessToken": "PSNAT5SCq8Lm2vK9pXfR1Zt0Nc4Wd6Hg5Ua2Ee9Ck3PoYx8Bj6Hg5Ua2Ee9Ck3Po…",
      "scopes": ["PERSONAL_ACCESS_TOKEN"]
    },
    "name": "Product sync server"
  }
}
```

```api-endpoint
{
  "title": "Delete Personal Access Token",
  "method": "DELETE",
  "path": "/personal-access-tokens/{personalAccessTokenId}",
  "description": "Deletes a Personal Access Token. On success it responds with 204 No Content and no response body. Once deleted, calls made with this token are no longer authenticated. Rotate a token by deleting and re-issuing, not by updating.",
  "responseStatus": 204,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "personalAccessTokenId": { "type": "string", "description": "The sys.id of the Personal Access Token", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  }
}
```

## Related documents {#related-documents}

- [Delivery Access Token](/api/reference/cma/delivery-access-token.md): A read-only delivery token exposed to visitors (for the browser).
- [SpaceRole](/api/reference/cma/space-role.md): The scope of privileges this user holds in each *Space*.
