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

# Stream expansion events from a permissions index

> The ReadExpansions API streams pre-computed permission expansion events and index freshness events from a permissions index.
Each expansion event represents the addition or removal of a resolved `(subject, relation, object)` tuple from the indexed FGA relationship.
When called without a `from` cursor, the stream first delivers the full current set of expansion events, then continues emitting events as the underlying index is updated. When resuming a previously-closed stream, clients should provide a `from` cursor. The ReadExpansions API will resume streaming from that previously checkpointed position, without replaying the full history.
Clients must provide the ID of the expected authorization model via the `authorization_model_id` parameter.
In addition to expansion events, the stream emits:
- Freshness events indicating how up-to-date the index is at a given point in the stream.
- Periodic heartbeat messages to keep the connection alive.
- A stream-closed message when the server terminates the connection, including the reason for closure.
The server will terminate streams periodically for traffic management. Clients must be prepared to reconnect in order to receive further expansions.
### Event deduplication
Each expansion event carries an `id` field. The same logical event always produces the same `id` regardless of how many times it is redelivered. Downstream consumers can use this field to deduplicate events.
## Examples
### Stream all expansions from the beginning
To stream all expansions for index `01YCP46JKYM8FJCQ37NMBYHE5X` in store `01G5JAVJ41T49E9TT3SKVS7X1J`, call the ReadExpansions API. The stream will emit events like
```json
{
  "result": {
    "event": {
      "id": "051c6d4d930b2810aa92c4711b31629a",
      "from": "eyJpZCI6IjAxWUNQNDZKS1lNOEZKQ1EzN05NQllIRTVYIn0=",
      "subject_type": "user",
      "subject_id": "anne",
      "object_type": "document",
      "object_id": "2021-budget",
      "relation": "viewer",
      "operation": "EXPANSION_OPERATION_INSERT",
      "tuple_written_at": "2026-04-27T17:00:00Z",
      "as_fresh_as": "2026-04-27T17:00:00Z"
    }
  }
}
{
  "result": {
    "freshness": {
      "as_fresh_as": "2026-04-27T17:36:00.282718Z"
    }
  }
}
{
  "result": {
    "heartbeat": {}
  }
}
{
  "result": {
    "closed": {
      "reason": "STREAM_CLOSED_REASON_CONNECTION_LIFETIME_EXCEEDED"
    }
  }
}
```
### Resume a stream from a checkpoint
To resume streaming without replaying events already processed, pass the `from` cursor returned in the last received event. The stream will pick up from that position.




## OpenAPI

````yaml oas/fga-api-oas.json GET /stores/{store_id}/indexes/{index_id}/expansions
openapi: 3.0.3
info:
  contact:
    name: Auth0
    url: https://docs.fga.dev/support
  description: >-
    Auth0 Fine-Grained Authorization (FGA) enables developers to design
    authorization models, from coarse grained to fine grained, in a way that's
    centralized, flexible, fast, scalable, and easy to use.
  title: Auth0 Fine-Grained Authorization (FGA)
  version: '1'
servers:
  - url: https://api.us1.fga.dev
    description: United States
  - url: https://api.eu1.fga.dev
    description: Europe
  - url: https://api.au1.fga.dev
    description: Australia
security: []
tags:
  - name: Auth0FGAService
paths:
  /stores/{store_id}/indexes/{index_id}/expansions:
    get:
      tags:
        - Permissions Index
      summary: Stream expansion events from a permissions index
      description: >
        The ReadExpansions API streams pre-computed permission expansion events
        and index freshness events from a permissions index.

        Each expansion event represents the addition or removal of a resolved
        `(subject, relation, object)` tuple from the indexed FGA relationship.

        When called without a `from` cursor, the stream first delivers the full
        current set of expansion events, then continues emitting events as the
        underlying index is updated. When resuming a previously-closed stream,
        clients should provide a `from` cursor. The ReadExpansions API will
        resume streaming from that previously checkpointed position, without
        replaying the full history.

        Clients must provide the ID of the expected authorization model via the
        `authorization_model_id` parameter.

        In addition to expansion events, the stream emits:

        - Freshness events indicating how up-to-date the index is at a given
        point in the stream.

        - Periodic heartbeat messages to keep the connection alive.

        - A stream-closed message when the server terminates the connection,
        including the reason for closure.

        The server will terminate streams periodically for traffic management.
        Clients must be prepared to reconnect in order to receive further
        expansions.

        ### Event deduplication

        Each expansion event carries an `id` field. The same logical event
        always produces the same `id` regardless of how many times it is
        redelivered. Downstream consumers can use this field to deduplicate
        events.

        ## Examples

        ### Stream all expansions from the beginning

        To stream all expansions for index `01YCP46JKYM8FJCQ37NMBYHE5X` in store
        `01G5JAVJ41T49E9TT3SKVS7X1J`, call the ReadExpansions API. The stream
        will emit events like

        ```json

        {
          "result": {
            "event": {
              "id": "051c6d4d930b2810aa92c4711b31629a",
              "from": "eyJpZCI6IjAxWUNQNDZKS1lNOEZKQ1EzN05NQllIRTVYIn0=",
              "subject_type": "user",
              "subject_id": "anne",
              "object_type": "document",
              "object_id": "2021-budget",
              "relation": "viewer",
              "operation": "EXPANSION_OPERATION_INSERT",
              "tuple_written_at": "2026-04-27T17:00:00Z",
              "as_fresh_as": "2026-04-27T17:00:00Z"
            }
          }
        }

        {
          "result": {
            "freshness": {
              "as_fresh_as": "2026-04-27T17:36:00.282718Z"
            }
          }
        }

        {
          "result": {
            "heartbeat": {}
          }
        }

        {
          "result": {
            "closed": {
              "reason": "STREAM_CLOSED_REASON_CONNECTION_LIFETIME_EXCEEDED"
            }
          }
        }

        ```

        ### Resume a stream from a checkpoint

        To resume streaming without replaying events already processed, pass the
        `from` cursor returned in the last received event. The stream will pick
        up from that position.
      operationId: ReadExpansions
      parameters:
        - in: path
          name: store_id
          required: true
          schema:
            type: string
        - in: path
          name: index_id
          required: true
          schema:
            type: string
        - in: query
          name: authorization_model_id
          required: true
          schema:
            type: string
        - description: Optional continuation token for resuming stream
          in: query
          name: from
          required: false
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  error:
                    $ref: '#/components/schemas/Status'
                  result:
                    $ref: '#/components/schemas/ReadExpansionsResponse'
                title: Stream result of ReadExpansionsResponse
                type: object
          description: A successful response.(streaming responses)
        '400':
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/errormessages.ValidationErrorMessageResponse
          description: Request failed due to invalid input.
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthenticationErrorMessageResponse'
          description: Request failed due to authentication errors.
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthenticationErrorMessageResponse'
          description: Request failed due to forbidden permission.
        '404':
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/errormessages.PathUnknownErrorMessageResponse
          description: Request failed due to incorrect path.
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/errormessages.AbortedMessageResponse'
          description: Request was aborted due a transaction conflict.
        '422':
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/errormessages.UnprocessableContentMessageResponse
          description: Request timeout due to throttling on complex query.
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResourceExhaustedErrorMessageResponse'
          description: Request failed due to too many requests.
        '500':
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/errormessages.InternalErrorMessageResponse
          description: Request failed due to internal server error.
      security:
        - ClientCredentials:
            - read:expansions
      x-codeSamples:
        - lang: bash
          label: curl
          source: >-
            # Set FGA_API_URL to your store region (https://api.us1.fga.dev, or
            eu1/au1).

            # Set FGA_STORE_ID and obtain FGA_BEARER_TOKEN with the client
            credentials grant.

            # See /integration/getting-your-api-keys; never put client secrets
            in source code.

            # Set FGA_MODEL_ID to the authorization model ID.

            # Set FGA_INDEX_ID to the permissions index ID.


            curl -X GET
            "$FGA_API_URL/stores/$FGA_STORE_ID/indexes/$FGA_INDEX_ID/expansions?authorization_model_id=$FGA_MODEL_ID"
            \
              -H "authorization: Bearer $FGA_BEARER_TOKEN"
components:
  schemas:
    Status:
      properties:
        code:
          format: int32
          type: integer
        details:
          items:
            $ref: '#/components/schemas/Any'
          type: array
        message:
          type: string
      type: object
    ReadExpansionsResponse:
      properties:
        closed:
          $ref: '#/components/schemas/ReadExpansionsStreamClosed'
        event:
          $ref: '#/components/schemas/ReadExpansionsEvent'
        freshness:
          $ref: '#/components/schemas/ReadExpansionsFreshness'
        heartbeat:
          $ref: '#/components/schemas/ReadExpansionsHeartbeat'
      title: >-
        Response containing expansion events, heartbeats, or stream closed
        notifications
      type: object
    errormessages.ValidationErrorMessageResponse:
      example:
        code: validation_error
        message: Generic validation error
      properties:
        code:
          $ref: '#/components/schemas/errormessages.ErrorCode'
        message:
          type: string
      type: object
    AuthenticationErrorMessageResponse:
      example:
        code: unauthorized_principal
        message: Missing permissions to perform operation
      properties:
        code:
          $ref: '#/components/schemas/errormessages.AuthErrorCode'
        message:
          type: string
      type: object
    errormessages.PathUnknownErrorMessageResponse:
      example:
        code: undefined_endpoint
        message: Endpoint not enabled
      properties:
        code:
          $ref: '#/components/schemas/errormessages.NotFoundErrorCode'
        message:
          type: string
      type: object
    errormessages.AbortedMessageResponse:
      example:
        code: '10'
        message: transaction conflict
      properties:
        code:
          type: string
        message:
          type: string
      type: object
    errormessages.UnprocessableContentMessageResponse:
      example:
        code: throttled_timeout_error
        message: timeout due to throttling on complex request
      properties:
        code:
          $ref: '#/components/schemas/errormessages.UnprocessableContentErrorCode'
        message:
          type: string
      type: object
    ResourceExhaustedErrorMessageResponse:
      example:
        code: rate_limit_exceeded
        message: Rate Limit exceeded
      properties:
        code:
          $ref: '#/components/schemas/ResourceExhaustedErrorCode'
        message:
          type: string
      type: object
    errormessages.InternalErrorMessageResponse:
      example:
        code: internal_error
        message: Internal Server Error
      properties:
        code:
          $ref: '#/components/schemas/errormessages.InternalErrorCode'
        message:
          type: string
      type: object
    Any:
      additionalProperties: {}
      properties:
        '@type':
          type: string
      type: object
    ReadExpansionsStreamClosed:
      properties:
        reason:
          $ref: '#/components/schemas/StreamClosedReason'
      title: Stream closed message indicating reason for closure
      type: object
    ReadExpansionsEvent:
      properties:
        as_fresh_as:
          description: >-
            Freshness guarantee for this event. All tuple changes with
            timestamps strictly

            before this value have been fully materialized into the index.
          format: date-time
          type: string
        from:
          type: string
        id:
          type: string
        object_id:
          type: string
        object_type:
          type: string
        operation:
          $ref: '#/components/schemas/ExpansionOperation'
        relation:
          type: string
        subject_id:
          type: string
        subject_relation:
          type: string
        subject_type:
          type: string
        tuple_written_at:
          description: >-
            Deprecated: Use as_fresh_as instead. This field will be removed in a
            future release.
          format: date-time
          type: string
      title: Expansion event containing authorization data
      type: object
    ReadExpansionsFreshness:
      properties:
        as_fresh_as:
          format: date-time
          type: string
      title: Freshness event indicating how up-to-date the index is
      type: object
    ReadExpansionsHeartbeat:
      title: >-
        Heartbeat message to keep connection alive while actively processing
        tuple changes
      type: object
    errormessages.ErrorCode:
      default: no_error
      description: |-
        - no_error: no error
         - validation_error: generic validation error.
         - authorization_model_not_found: authorization model not found.
         - authorization_model_resolution_too_complex: too many rewrite rules to be resolved.
         - invalid_write_input: invalid write input.
         - cannot_allow_duplicate_tuples_in_one_request: duplicate tuples in one request.
         - cannot_allow_duplicate_types_in_one_request: duplicate types in one request.
         - cannot_allow_multiple_references_to_one_relation: cannot use a relation to define itself.
         - invalid_continuation_token: invalid continuation token.
         - invalid_tuple_set: invalid tuple set.
         - invalid_check_input: invalid check input.
         - invalid_expand_input: invalid expand input.
         - unsupported_user_set: unsupported user set.
         - invalid_object_format: invalid object format.
         - token_issuer_already_registered: token issuers already registered.
         - write_failed_due_to_invalid_input: write request failed due to invalid input.
         - authorization_model_assertions_not_found: no assertions found for authorization model.
         - latest_authorization_model_not_found: latest authorization model not found.
         - type_not_found: type not found.
         - relation_not_found: relation not found.
         - empty_relation_definition: empty relation definition.
         - invalid_user: invalid user.
         - invalid_token_issuer: invalid token issuer.
         - invalid_tuple: invalid tuple.
         - unknown_relation: unknown relation.
         - store_id_invalid_length: store id has invalid length.
         - issuer_url_invalid_uri: issuer url has invalid URI.
         - issuer_url_required_absolute_path: issuer url is not absolute path.
         - assertions_too_many_items: assertions have too many items.
         - id_too_long: ID is too long.
         - invalid_environment: invalid environment is specified.
         - authorization_model_id_too_long: authorization model id is too long.
         - tuple_key_value_not_specified: tuple key value is not specified.
         - tuple_keys_too_many_or_too_few_items: tuple keys have too few or too many items.
         - page_size_invalid: page size is outside of acceptable range.
         - param_missing_value: params value is missing.
         - difference_base_missing_value: difference's base value is missing.
         - subtract_base_missing_value: subtract base value is missing.
         - object_too_long: object length is too long.
         - relation_too_long: relation length is too long.
         - type_definitions_too_few_items: type definitions do not have enough item.
         - type_invalid_length: type length is invalid.
         - type_invalid_pattern: type does not match expected pattern.
         - relations_too_few_items: relations have too few items.
         - relations_too_long: relations' length is too long.
         - relations_invalid_pattern: relations do not match expected pattern.
         - object_invalid_pattern: object does not match expected pattern.
         - query_string_type_continuation_token_mismatch: type in the query string and the continuation token don't match.
         - invalid_contextual_tuple: Invalid contextual tuple.
         - duplicate_contextual_tuple: Duplicate contextual tuple.
      enum:
        - no_error
        - validation_error
        - authorization_model_not_found
        - authorization_model_resolution_too_complex
        - invalid_write_input
        - cannot_allow_duplicate_tuples_in_one_request
        - cannot_allow_duplicate_types_in_one_request
        - cannot_allow_multiple_references_to_one_relation
        - invalid_continuation_token
        - invalid_tuple_set
        - invalid_check_input
        - invalid_expand_input
        - unsupported_user_set
        - invalid_object_format
        - token_issuer_already_registered
        - write_failed_due_to_invalid_input
        - authorization_model_assertions_not_found
        - latest_authorization_model_not_found
        - type_not_found
        - relation_not_found
        - empty_relation_definition
        - invalid_user
        - invalid_token_issuer
        - invalid_tuple
        - unknown_relation
        - store_id_invalid_length
        - issuer_url_invalid_uri
        - issuer_url_required_absolute_path
        - assertions_too_many_items
        - id_too_long
        - invalid_environment
        - authorization_model_id_too_long
        - tuple_key_value_not_specified
        - tuple_keys_too_many_or_too_few_items
        - page_size_invalid
        - param_missing_value
        - difference_base_missing_value
        - subtract_base_missing_value
        - object_too_long
        - relation_too_long
        - type_definitions_too_few_items
        - type_invalid_length
        - type_invalid_pattern
        - relations_too_few_items
        - relations_too_long
        - relations_invalid_pattern
        - object_invalid_pattern
        - query_string_type_continuation_token_mismatch
        - invalid_contextual_tuple
        - duplicate_contextual_tuple
      title: ErrorCodeEnums
      type: string
    errormessages.AuthErrorCode:
      default: no_auth_error
      description: |-
        - no_auth_error: no error
         - auth_failure: generic authentication error. Details available via message field.
         - auth_failed_invalid_subject: authentication failure due to invalid subject.
         - auth_failed_invalid_audience: authentication failure due to invalid audience.
         - auth_failed_invalid_issuer: authentication failure due to invalid issuer.
         - invalid_claims: authentication failure due to invalid claims.
         - auth_failed_invalid_bearer_token: authentication failure due to invalid bearer token.
         - missing_customer_in_bearer_token: authentication failure with customer id missing in bearer token.
         - missing_store_in_bearer_token: authentication failure with store id missing in bearer token.
         - store_mismatch_in_bearer_token: authentication failure due to store mismatch from bearer token.
         - customer_mismatch_in_bearer_token: authentication failure due to customer id in the request being different from the customer id in the bearer token.
         - bearer_token_missing: bearer token missing in request.
         - unauthenticated: unauthenticated.
         - insufficient_permissions: insufficient permissions.
         - unauthorized_principal: authentication denial due to unauthorized principal.
      enum:
        - no_auth_error
        - auth_failure
        - auth_failed_invalid_subject
        - auth_failed_invalid_audience
        - auth_failed_invalid_issuer
        - invalid_claims
        - auth_failed_invalid_bearer_token
        - missing_customer_in_bearer_token
        - missing_store_in_bearer_token
        - store_mismatch_in_bearer_token
        - customer_mismatch_in_bearer_token
        - bearer_token_missing
        - unauthenticated
        - insufficient_permissions
        - unauthorized_principal
      title: AuthErrorCodeEnums
      type: string
    errormessages.NotFoundErrorCode:
      default: no_not_found_error
      description: |-
        - no_not_found_error: no error
         - undefined_endpoint: undefined endpoint.
         - store_id_not_found: store ID not found
         - resource_not_found: generic not found.
         - unimplemented: method is unimplemented
      enum:
        - no_not_found_error
        - undefined_endpoint
        - store_id_not_found
        - resource_not_found
        - unimplemented
      title: NotFoundErrorCodeEnums
      type: string
    errormessages.UnprocessableContentErrorCode:
      default: no_throttled_error_code
      enum:
        - no_throttled_error_code
        - throttled_timeout_error
      title: |-
        - no_throttled_error_code: no error
         - throttled_timeout_error: 3500 level errors are timeout due to throttling
      type: string
    ResourceExhaustedErrorCode:
      default: no_resource_exhausted_error
      description: |-
        - no_resource_exhausted_error: no error
         - rate_limit_exceeded: operation failed due to exceeding rate limit.
         - auth_rate_limit_exceeded: rate limit error during authentication.
      enum:
        - no_resource_exhausted_error
        - rate_limit_exceeded
        - auth_rate_limit_exceeded
      title: ResourceExhaustedErrorCodeEnums
      type: string
    errormessages.InternalErrorCode:
      default: no_internal_error
      description: |-
        - no_internal_error: no error
         - internal_error: generic internal error.
         - auth_internal_error: internal error due to authentication error.
         - auth_failed_error_fetching_well_known_jwks: authentication failure due to internal error in fetching well-known/jwks.json.
         - cancelled: internal error - request being cancelled.
         - deadline_exceeded: internal error - deadline exceeded.
         - already_exists: internal error - already exists.
         - resource_exhausted: internal error - resource exhausted.
         - failed_precondition: internal error - failed precondition.
         - aborted: internal error - aborted.
         - out_of_range: internal error - out of range.
         - unavailable: internal error - unavailable.
         - data_loss: internal error - data loss.
      enum:
        - no_internal_error
        - internal_error
        - auth_internal_error
        - auth_failed_error_fetching_well_known_jwks
        - cancelled
        - deadline_exceeded
        - already_exists
        - resource_exhausted
        - failed_precondition
        - aborted
        - out_of_range
        - unavailable
        - data_loss
      title: InternalErrorCodeEnums
      type: string
    StreamClosedReason:
      default: STREAM_CLOSED_REASON_UNSPECIFIED
      enum:
        - STREAM_CLOSED_REASON_UNSPECIFIED
        - STREAM_CLOSED_REASON_CONNECTION_LIFETIME_EXCEEDED
      title: Stream closed reason
      type: string
    ExpansionOperation:
      default: EXPANSION_OPERATION_UNSPECIFIED
      enum:
        - EXPANSION_OPERATION_UNSPECIFIED
        - EXPANSION_OPERATION_INSERT
        - EXPANSION_OPERATION_DELETE
      title: Expansion operation type
      type: string
  securitySchemes:
    ClientCredentials:
      flows:
        clientCredentials:
          scopes:
            check:tuples: Allows checking tuples
            create:stores: Allows creation of stores
            delete:stores: Allows deletion of stores
            expand:tuples: Allows expanding tuples
            list:objects: Allows reading results of list objects
            list:users: Allows reading results of list users
            read:assertions: Allows reading assertions
            read:authorization_models: Allows reading authorization models
            read:changes: Allows reading changes
            read:expansions: Allows reading index expansions
            read:indexes: Allows reading permission indexes
            read:stores: Allows reading of stores
            read:tuples: Allows reading tuples
            search:logs: Allows searching store logs
            write:assertions: Allows updating assertions
            write:authorization_models: Allows writing authorization models
            write:tuples: Allows writing tuples
          tokenUrl: https://auth.fga.dev/oauth/token
      type: oauth2

````