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

# Permission Changes

> Read a chronological list of permission changes (writes and deletes) for a Sync

Use this endpoint to read a chronological feed of permission changes (writes and deletes) for a Sync. This is useful if you maintain your own copy of permission data, for example in your search index metadata, and want to keep it up to date incrementally instead of re-listing all objects on every update.

## Paginating with continuation tokens

Every response includes a `continuationToken`. Pass it on your next request to read the next page of changes.

When you reach the end of the feed, the response contains an empty `changes` array and the same `continuationToken`. Detect the end of the feed by the empty array, not by an absent token. Store the token and reuse it later to poll for new changes.

Keep these rules in mind when paginating:

* A continuation token takes precedence over `startTime` when both are supplied.
* A continuation token is issued against the `type` filter that produced it. Send the same `type` on every page of a paginated read, or omit it on every page. Changing it returns a 400 with the code `PERMISSION_CHANGES_CURSOR_TYPE_MISMATCH`.
* If a token is no longer valid, the API returns a 400 with the code `PERMISSION_CHANGES_CURSOR_INVALID`. Restart the read from the beginning or from a `startTime`.

## Error codes

A rejected continuation token returns a 400 response with a stable `code` field that you can branch on:

| Code                                      | Meaning                                                              | How to recover                                         |
| ----------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------ |
| `PERMISSION_CHANGES_CURSOR_INVALID`       | The continuation token is not valid for this Sync.                   | Read again from the beginning, or from a `startTime`.  |
| `PERMISSION_CHANGES_CURSOR_TYPE_MISMATCH` | The `type` filter does not match the one the token was issued under. | Retry with the same `type` the token was issued under. |


## OpenAPI

````yaml get /api/permissions/{syncId}/changes
openapi: 3.0.0
info:
  title: Paragon Sync API
  description: API for managing Syncs and permissions for Connected Users
  version: 1.0.0
servers:
  - url: https://sync.useparagon.com
    description: Production server
security:
  - bearerAuth: []
paths:
  /api/permissions/{syncId}/changes:
    get:
      summary: Permission Changes
      description: >-
        Read a chronological list of permission changes (writes and deletes) for
        a Sync
      parameters:
        - name: syncId
          in: path
          required: true
          schema:
            type: string
          description: >-
            UUID of the Sync to query, returned from the [Enable
            Sync](/managed-sync/api/enable-a-sync) endpoint.
        - name: type
          in: query
          required: false
          schema:
            type: string
          description: >-
            Object type to filter changes by (e.g. `file`). A continuation token
            is issued against the `type` that produced it, so this value must
            stay identical for every page of a paginated read, including when it
            is omitted.
        - name: startTime
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: >-
            Only return changes that occurred at or after this time. Must be a
            full RFC 3339 timestamp with a time component (e.g.
            `2026-07-16T00:00:00Z`); a date-only value is rejected with a 400.
            Ignored when `continuationToken` is supplied.
          example: '2026-07-16T00:00:00Z'
        - name: pageSize
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
          description: Number of changes to return per page, from 1 to 100. Defaults to 50.
        - name: continuationToken
          in: query
          required: false
          schema:
            type: string
          description: >-
            Token from a previous response, used to read the next page of
            changes. Takes precedence over `startTime` when both are supplied.
      responses:
        '200':
          description: A page of permission changes in chronological order
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PermissionChangesResponse'
        '400':
          description: >-
            Validation error or a rejected continuation token. A rejected
            continuation token includes a stable `code` of
            `PERMISSION_CHANGES_CURSOR_INVALID` or
            `PERMISSION_CHANGES_CURSOR_TYPE_MISMATCH`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PermissionChangesError'
        '401':
          description: Unauthorized
components:
  schemas:
    PermissionChangesResponse:
      type: object
      required:
        - changes
      properties:
        changes:
          type: array
          description: >-
            Permission changes in chronological order. An empty array indicates
            that you have reached the end of the feed.
          items:
            $ref: '#/components/schemas/PermissionChange'
        continuationToken:
          type: string
          description: >-
            Token to pass on the next request to read the next page. Repeated
            unchanged when there are no new changes, so detect the end of the
            feed by an empty `changes` array rather than by an absent token.
    PermissionChangesError:
      type: object
      required:
        - statusCode
        - error
        - message
      properties:
        statusCode:
          type: integer
          example: 400
        error:
          type: string
          example: Bad Request
        code:
          type: string
          enum:
            - PERMISSION_CHANGES_CURSOR_INVALID
            - PERMISSION_CHANGES_CURSOR_TYPE_MISMATCH
          description: >-
            Stable code identifying a rejected continuation token.
            `PERMISSION_CHANGES_CURSOR_INVALID` means the token is not valid for
            this Sync; read again from the beginning or from a `startTime`.
            `PERMISSION_CHANGES_CURSOR_TYPE_MISMATCH` means the `type` filter
            does not match the one the token was issued under.
        message:
          type: string
    PermissionChange:
      type: object
      required:
        - tupleKey
        - operation
        - timestamp
      properties:
        tupleKey:
          $ref: '#/components/schemas/PermissionChangeTupleKey'
        operation:
          type: string
          enum:
            - write
            - delete
          description: >-
            Whether the permission relationship was added (`write`) or removed
            (`delete`).
        timestamp:
          type: string
          format: date-time
          description: Time at which the change occurred.
    PermissionChangeTupleKey:
      type: object
      required:
        - user
        - relation
        - object
      properties:
        user:
          type: string
          description: >-
            The user or group that the permission relationship applies to (e.g.
            `user:email@example.com`).
        relation:
          type: string
          description: >-
            The role or relation that changed (e.g. `can_read`, `can_write`,
            `is_owner`).
        object:
          type: string
          description: >-
            The object that the permission relationship applies to (e.g.
            `file:[File UUID]`).
        condition:
          type: object
          description: Condition attached to the permission relationship, if any.
          required:
            - name
          properties:
            name:
              type: string
              description: Name of the condition.
            context:
              type: object
              description: Context values associated with the condition.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Paragon User Token. Add to the Authorization header of your requests.

````