diff --git a/docs/content/getting-started/setup-openfga/configuration.mdx b/docs/content/getting-started/setup-openfga/configuration.mdx
index 42e8798ec3..e6dcd0e29f 100644
--- a/docs/content/getting-started/setup-openfga/configuration.mdx
+++ b/docs/content/getting-started/setup-openfga/configuration.mdx
@@ -101,7 +101,7 @@ docker run docker.io/openfga/openfga:latest run \
## List of options
-The following table lists the configuration options for the OpenFGA server [v1.8.9](https://github.com/openfga/openfga/releases/tag/v1.8.9), based on the [config-schema.json](https://raw.githubusercontent.com/openfga/openfga/refs/tags/v1.8.9/.config-schema.json).
+The following table lists the configuration options for the OpenFGA server [v1.14.1](https://github.com/openfga/openfga/releases/tag/v1.14.1), based on the [config-schema.json](https://raw.githubusercontent.com/openfga/openfga/refs/tags/v1.14.1/.config-schema.json).
| Config File | Env Var | Flag Name | Type | Description | Default Value |
|-------------|---------|-----------|------|-------------|---------------|
@@ -116,29 +116,39 @@ The following table lists the configuration options for the OpenFGA server [v1.8
| `maxConditionEvaluationCost` |
OPENFGA_MAX_CONDITION_EVALUATION_COST
| `max-condition-evaluation-cost` | integer | The maximum cost for CEL condition evaluation before a request returns an error (default is 100). | `100` |
| `changelogHorizonOffset` |
OPENFGA_CHANGELOG_HORIZON_OFFSET
| `changelog-horizon-offset` | integer | The offset (in minutes) from the current time. Changes that occur after this offset will not be included in the response of ReadChanges. | |
| `resolveNodeLimit` |
OPENFGA_RESOLVE_NODE_LIMIT
| `resolve-node-limit` | integer | Maximum resolution depth to attempt before throwing an error (defines how deeply nested an authorization model can be before a query errors out). | `25` |
-| `resolveNodeBreadthLimit` |
OPENFGA_RESOLVE_NODE_BREADTH_LIMIT
| `resolve-node-breadth-limit` | integer | Defines how many nodes on a given level can be evaluated concurrently in a Check resolution tree. | `100` |
+| `resolveNodeBreadthLimit` |
OPENFGA_RESOLVE_NODE_BREADTH_LIMIT
| `resolve-node-breadth-limit` | integer | Defines how many nodes on a given level can be evaluated concurrently in a Check resolution tree. | `10` |
| `listObjectsDeadline` |
OPENFGA_LIST_OBJECTS_DEADLINE
| `list-objects-deadline` | string (duration) | The timeout deadline for serving ListObjects requests | `3s` |
| `listObjectsMaxResults` |
OPENFGA_LIST_OBJECTS_MAX_RESULTS
| `list-objects-max-results` | integer | The maximum results to return in the non-streaming ListObjects API response. If 0, all results can be returned | `1000` |
+| `listObjectsPipelineEnabled` |
OPENFGA_LIST_OBJECTS_PIPELINE_ENABLED
| `list-objects-pipeline-enabled` | boolean | Enables the ListObjects pipeline optimization algorithm, which can significantly improve the latency of ListObjects requests. When enabled, the server will attempt to resolve intermediate nodes in the ListObjects resolution tree concurrently. This optimization is most effective for workloads with large and complex authorization models, but may not suit all cases. Can be disabled if it causes increased resource usage. | `true` |
| `listUsersDeadline` |
OPENFGA_LIST_USERS_DEADLINE
| `list-users-deadline` | string (duration) | The timeout deadline for serving ListUsers requests. If 0s, there is no deadline | `3s` |
| `listUsersMaxResults` |
OPENFGA_LIST_USERS_MAX_RESULTS
| `list-users-max-results` | integer | The maximum results to return in ListUsers API response. If 0, all results can be returned | `1000` |
+| `readChangesMaxPageSize` |
OPENFGA_READ_CHANGES_MAX_PAGE_SIZE
| `read-changes-max-page-size` | integer | The maximum page size allowed for ReadChanges API requests | `100` |
| `requestDurationDatastoreQueryCountBuckets` |
| `request-duration-datastore-query-count-buckets` | []integer | Datastore query count buckets used to label the histogram metric for measuring request duration. | `50,200` |
| `requestDurationDispatchCountBuckets` |
OPENFGA_REQUEST_DURATION_DISPATCH_COUNT_BUCKETS
| `request-duration-dispatch-count-buckets` | []integer | Dispatch count buckets used to label the histogram metric for measuring request duration. | `50,200` |
| `contextPropagationToDatastore` |
OPENFGA_CONTEXT_PROPAGATION_TO_DATASTORE
| `context-propagation-to-datastore` | boolean | Propagate a requests context to the datastore implementation. Settings this parameter can result in connection pool draining on request aborts and timeouts. | `false` |
-| `experimentals` |
OPENFGA_EXPERIMENTALS
| `experimentals` | []string (enum=[`enable-check-optimizations`, `enable-list-objects-optimizations`, `enable-access-control`]) | a list of experimental features to enable | `` |
+| `experimentals` |
OPENFGA_EXPERIMENTALS
| `experimentals` | []string (enum=[`enable-check-optimizations`, `enable-list-objects-optimizations`, `enable-access-control`, `datastore_throttling`, `pipeline_list_objects`, `authzen`]) | a comma-separated list of experimental features to enable | `pipeline_list_objects` |
+| `authzen.baseURL` |
OPENFGA_AUTHZEN_BASE_URL
| `authzen-base-url` | string | The canonical absolute base URL published in AuthZEN discovery metadata. It may include an optional path prefix. | |
| `accessControl.enabled` |
OPENFGA_ACCESS_CONTROL_ENABLED
| `access-control-enabled` | boolean | Enable/disable the access control store. | `false` |
| `accessControl.storeId` |
OPENFGA_ACCESS_CONTROL_STORE_ID
| `access-control-store-id` | string | The storeId to be used for the access control store. | |
| `accessControl.modelId` |
OPENFGA_ACCESS_CONTROL_MODEL_ID
| `access-control-model-id` | string | The modelId to be used for the access control store. | |
-| `playground.enabled` |
| `playground-port` | integer | The port to serve the local OpenFGA Playground on. | `3000` |
+| `playground.enabled` |
OPENFGA_PLAYGROUND_ENABLED
| `playground-enabled` | boolean | Enable/disable the OpenFGA Playground. The Playground can only be run when the authentication method is set to 'none'. Note that the built-in Playground is intended for local development and testing purposes, and is not recommended for production use. It has been deprecated and will be removed in a subsequent release. | `false` |
+| `playground.port` |
OPENFGA_PLAYGROUND_PORT
| `playground-port` | integer | Deprecated: The port to serve the local OpenFGA Playground on. Use 'addr' instead. | `3000` |
+| `playground.addr` |
OPENFGA_PLAYGROUND_ADDR
| `playground-addr` | string | The host:port address to serve the local OpenFGA Playground on. | |
| `profiler.enabled` |
| `profiler-addr` | string | The host:port address to serve the pprof profiler server on. | `:3001` |
| `datastore.engine` |
OPENFGA_DATASTORE_ENGINE
| `datastore-engine` | string (enum=[`memory`, `postgres`, `mysql`, `sqlite`]) | The datastore engine that will be used for persistence. | `memory` |
| `datastore.uri` |
OPENFGA_DATASTORE_URI
| `datastore-uri` | string | The connection uri to use to connect to the datastore (for any engine other than 'memory'). | |
+| `datastore.secondaryUri` |
OPENFGA_DATASTORE_SECONDARY_URI
| `datastore-secondary-uri` | string | The connection uri to use to connect to the secondary datastore (for postgres only). | |
| `datastore.username` |
OPENFGA_DATASTORE_USERNAME
| `datastore-username` | string | The connection username to connect to the datastore (overwrites any username provided in the connection uri). | |
+| `datastore.secondaryUsername` |
OPENFGA_DATASTORE_SECONDARY_USERNAME
| `datastore-secondary-username` | string | The connection username to connect to the secondary datastore (overwrites any username provided in the connection uri). | |
| `datastore.password` |
OPENFGA_DATASTORE_PASSWORD
| `datastore-password` | string | The connection password to connect to the datastore (overwrites any password provided in the connection uri). | |
+| `datastore.secondaryPassword` |
OPENFGA_DATASTORE_SECONDARY_PASSWORD
| `datastore-secondary-password` | string | The connection password to connect to the secondary datastore (overwrites any password provided in the connection uri). | |
| `datastore.maxCacheSize` |
OPENFGA_DATASTORE_MAX_CACHE_SIZE
| `datastore-max-cache-size` | integer | The maximum number of authorization models that will be cached in memory | `100000` |
+| `datastore.maxTypesystemCacheSize` |
OPENFGA_DATASTORE_MAX_TYPESYSTEM_CACHE_SIZE
| `datastore-max-typesystem-cache-size` | integer | The maximum number of type system models that will be cached in memory | `100000` |
| `datastore.maxOpenConns` |
OPENFGA_DATASTORE_MAX_OPEN_CONNS
| `datastore-max-open-conns` | integer | The maximum number of open connections to the datastore. | `30` |
+| `datastore.minOpenConns` |
OPENFGA_DATASTORE_MIN_OPEN_CONNS
| `datastore-min-open-conns` | integer | The minimum number of open connections to the datastore. This is only available for PostgreSQL. | `0` |
| `datastore.maxIdleConns` |
OPENFGA_DATASTORE_MAX_IDLE_CONNS
| `datastore-max-idle-conns` | integer | the maximum number of connections to the datastore in the idle connection pool. | `10` |
+| `datastore.minIdleConns` |
OPENFGA_DATASTORE_MIN_IDLE_CONNS
| `datastore-min-idle-conns` | integer | the minimum number of connections to the datastore in the idle connection pool. This is only available for PostgreSQL. | `0` |
| `datastore.connMaxIdleTime` |
OPENFGA_DATASTORE_CONN_MAX_IDLE_TIME
| `datastore-conn-max-idle-time` | string (duration) | the maximum amount of time a connection to the datastore may be idle | `0s` |
| `datastore.connMaxLifetime` |
OPENFGA_DATASTORE_CONN_MAX_LIFETIME
| `datastore-conn-max-lifetime` | string (duration) | the maximum amount of time a connection to the datastore may be reused | `0s` |
| `datastore.metrics.enabled` |
OPENFGA_DATASTORE_METRICS_ENABLED
| `datastore-metrics-enabled` | boolean | enable/disable sql metrics for the datastore | `false` |
@@ -150,6 +160,7 @@ The following table lists the configuration options for the OpenFGA server [v1.8
| `authn.oidc.subjects` |
OPENFGA_AUTHN_OIDC_SUBJECTS
| `authn-oidc-subjects` | []string | the OIDC subject names that will be accepted as valid when verifying the `sub` field of the JWTs. If empty, every `sub` will be allowed | |
| `authn.oidc.clientIdClaims` |
OPENFGA_AUTHN_OIDC_CLIENT_ID_CLAIMS
| `authn-oidc-client-id-claims` | []string | the OIDC client id claims that will be used to parse the clientID - configure in order of priority (first is highest). Defaults to [`azp`, `client_id`] | |
| `grpc.addr` |
OPENFGA_GRPC_ADDR
| `grpc-addr` | string | The host:port address to serve the grpc server on. | `0.0.0.0:8081` |
+| `grpc.maxRecvMsgBytes` |
OPENFGA_GRPC_MAX_RECV_MSG_BYTES
| `grpc-max-recv-msg-bytes` | integer | The maximum size, in bytes, of a received gRPC message. | `616448` |
| `grpc.tls.enabled` |
OPENFGA_GRPC_TLS_ENABLED
| `grpc-tls-enabled` | boolean | Enables or disables transport layer security (TLS). | `false` |
| `grpc.tls.cert` |
OPENFGA_GRPC_TLS_CERT
| `grpc-tls-cert` | string | The (absolute) file path of the certificate to use for the TLS connection. | |
| `grpc.tls.key` |
OPENFGA_GRPC_TLS_KEY
| `grpc-tls-key` | string | The (absolute) file path of the TLS key that should be used for the TLS connection. | |
@@ -165,10 +176,11 @@ The following table lists the configuration options for the OpenFGA server [v1.8
| `log.level` |
OPENFGA_LOG_LEVEL
| `log-level` | string (enum=[`none`, `debug`, `info`, `warn`, `error`, `panic`, `fatal`]) | The log level to set. For production we recommend 'info' format. | `info` |
| `log.timestampFormat` |
OPENFGA_LOG_TIMESTAMP_FORMAT
| `log-timestamp-format` | string (enum=[`Unix`, `ISO8601`]) | The timestamp format to use for the log output. | `Unix` |
| `trace.enabled` |
| `trace-sample-ratio,otel-traces-sampler-arg` | number | The fraction of traces to sample. 1 means all, 0 means none. | `0.2` |
+| `trace.serviceName` |
OPENFGA_TRACE_SERVICE_NAME,OTEL_SERVICE_NAME
| `trace-service-name,otel-service-name` | string | The service name included in sampled traces. | `openfga` |
+| `trace.resourceAttributes` |
OTEL_RESOURCE_ATTRIBUTES
| `otel-resource-attributes` | string | Key-value pairs to be used as resource attributes | |
| `metrics.enabled` |
OPENFGA_METRICS_ENABLED
| `metrics-enabled` | boolean | enable/disable prometheus metrics on the '/metrics' endpoint | `true` |
| `metrics.addr` |
OPENFGA_METRICS_ADDR
| `metrics-addr` | string | the host:port address to serve the prometheus metrics server on | `0.0.0.0:2112` |
| `metrics.enableRPCHistograms` |
OPENFGA_METRICS_ENABLE_RPC_HISTOGRAMS
| `metrics-enable-rpc-histograms` | boolean | enables prometheus histogram metrics for RPC latency distributions | `false` |
@@ -179,21 +191,36 @@ The following table lists the configuration options for the OpenFGA server [v1.8
| `checkQueryCache.enabled` |
OPENFGA_CHECK_QUERY_CACHE_ENABLED
| `check-query-cache-enabled` | boolean | enable caching of Check requests. The key is a string representing a query, and the value is a boolean. For example, if you have a relation `define viewer: owner or editor`, and the query is Check(user:anne, viewer, doc:1), we'll evaluate the `owner` relation and the `editor` relation and cache both results: (user:anne, viewer, doc:1) -> allowed=true and (user:anne, owner, doc:1) -> allowed=true. The cache is stored in-memory; the cached values are overwritten on every change in the result, and cleared after the configured TTL. This flag improves latency, but turns Check and ListObjects into eventually consistent APIs. If the request's consistency is HIGHER_CONSISTENCY, this cache is not used. | `false` |
| `checkQueryCache.limit` |
OPENFGA_CHECK_QUERY_CACHE_LIMIT
| `check-query-cache-limit` | integer | DEPRECATED use OPENFGA_CHECK_CACHE_LIMIT. If caching of Check and ListObjects calls is enabled, this is the size limit (in items) of the cache | `10000` |
| `checkQueryCache.ttl` |
OPENFGA_CHECK_QUERY_CACHE_TTL
| `check-query-cache-ttl` | string (duration) | if caching of Check and ListObjects is enabled, this is the TTL of each value | `10s` |
-| `cacheController.enabled` |
OPENFGA_CACHE_CONTROLLER_ENABLED
| `cache-controller-enabled` | boolean | enabling dynamic invalidation of check query cache and check iterator cache based on whether there are recent tuple writes. If enabled, cache will be invalidated when either 1) there are tuples written to the store OR 2) the check query cache or check iterator cache TTL has expired. | `false` |
-| `cacheController.ttl` |
OPENFGA_CACHE_CONTROLLER_TTL
| `cache-controller-ttl` | string (duration) | if cache controller is enabled, control how frequent read changes are invoked internally to query for recent tuple writes to the store. | `10s` |
+| `cacheController.enabled` |
OPENFGA_CACHE_CONTROLLER_ENABLED
| `cache-controller-enabled` | boolean | enable invalidation of check query cache and iterator cache based on recent tuple writes. Invalidation is triggered by Check and List Objects requests, which periodically check the datastore's changelog table for writes and invalidate cache entries earlier than recent writes. Invalidations from Check requests are rate-limited by cache-controller-ttl, whereas List Objects requests invalidate every time if list objects iterator cache is enabled. | `false` |
+| `cacheController.ttl` |
OPENFGA_CACHE_CONTROLLER_TTL
| `cache-controller-ttl` | string (duration) | if cache controller is enabled, this is the minimum time interval for Check requests to trigger cache invalidation. List Objects requests may trigger invalidation even sooner if list objects iterator cache is enabled. | `10s` |
| `checkDispatchThrottling.enabled` |
OPENFGA_CHECK_DISPATCH_THROTTLING_ENABLED
| `check-dispatch-throttling-enabled` | boolean | enable throttling when check request's number of dispatches is high | `false` |
| `checkDispatchThrottling.frequency` |
OPENFGA_CHECK_DISPATCH_THROTTLING_FREQUENCY
| `check-dispatch-throttling-frequency` | string (duration) | the frequency period that the deprioritized throttling queue is evaluated for a check request. A higher value will result in more aggressive throttling | `10µs` |
| `checkDispatchThrottling.threshold` |
OPENFGA_CHECK_DISPATCH_THROTTLING_THRESHOLD
| `check-dispatch-throttling-threshold` | integer | define the number of recursive operations to occur before getting throttled for a check request | `100` |
| `checkDispatchThrottling.maxThreshold` |
OPENFGA_CHECK_DISPATCH_THROTTLING_MAX_THRESHOLD
| `check-dispatch-throttling-max-threshold` | integer | define the maximum dispatch threshold beyond above which requests will be throttled. 0 will use the 'dispatchThrottling.threshold' value as maximum | `0` |
-| `listObjectsDispatchThrottling.enabled` |
OPENFGA_LIST_OBJECTS_DISPATCH_THROTTLING_ENABLED
| `list-objects-dispatch-throttling-enabled` | boolean | enable throttling when list objects request's number of dispatches is high | `false` |
-| `listObjectsDispatchThrottling.frequency` |
| `list-objects-dispatch-throttling-frequency` | string (duration) | the frequency period that the deprioritized throttling queue is evaluated for a list objects request. A higher value will result in more aggressive throttling | `10µs` |
-| `listObjectsDispatchThrottling.threshold` |
| `list-objects-dispatch-throttling-threshold` | integer | define the number of recursive operations to occur before getting throttled for a list objects request | `100` |
-| `listObjectsDispatchThrottling.maxThreshold` |
| `list-objects-dispatch-throttling-max-threshold` | integer | define the maximum dispatch threshold beyond above which requests will be throttled for a list objects request. 0 will use the 'dispatchThrottling.threshold' value as maximum | `0` |
+| `listObjectsIteratorCache.enabled` |
OPENFGA_LIST_OBJECTS_ITERATOR_CACHE_ENABLED
| `list-objects-iterator-cache-enabled` | boolean | enable caching of datastore iterators in ListObjects. The key is a string representing a database query, and the value is a list of tuples. Each iterator is the result of a database query, for example usersets related to a specific object, or objects related to a specific user, up to a certain number of tuples per iterator. If the request's consistency is HIGHER_CONSISTENCY, this cache is not used. | `false` |
+| `listObjectsIteratorCache.maxResults` |
OPENFGA_LIST_OBJECTS_ITERATOR_CACHE_MAX_RESULTS
| `list-objects-iterator-cache-max-results` | integer | if caching of datastore iterators of ListObjects requests is enabled, this is the limit of tuples to cache per key | `10000` |
+| `listObjectsIteratorCache.ttl` |
OPENFGA_LIST_OBJECTS_ITERATOR_CACHE_TTL
| `list-objects-iterator-cache-ttl` | string (duration) | if caching of datastore iterators of ListObjects requests is enabled, this is the TTL of each value | `10s` |
+| `listObjectsDispatchThrottling.enabled` |
OPENFGA_LIST_OBJECTS_DISPATCH_THROTTLING_ENABLED
| `list-objects-dispatch-throttling-enabled` | boolean | enable throttling when ListObjects request's number of dispatches is high. Only applies when pipeline is disabled. | `false` |
+| `listObjectsDispatchThrottling.frequency` |
| `list-objects-dispatch-throttling-frequency` | string (duration) | the frequency period that the deprioritized throttling queue is evaluated for a ListObjects request. A higher value will result in more aggressive throttling | `10µs` |
+| `listObjectsDispatchThrottling.threshold` |
| `list-objects-dispatch-throttling-threshold` | integer | define the number of recursive operations to occur before getting throttled for a ListObjects request | `100` |
+| `listObjectsDispatchThrottling.maxThreshold` |
| `list-objects-dispatch-throttling-max-threshold` | integer | define the maximum dispatch threshold beyond above which requests will be throttled for a ListObjects request. 0 will use the 'dispatchThrottling.threshold' value as maximum | `0` |
| `listUsersDispatchThrottling.enabled` |
OPENFGA_LIST_USERS_DISPATCH_THROTTLING_ENABLED
| `list-users-dispatch-throttling-enabled` | boolean | enable throttling when list users request's number of dispatches is high | `false` |
| `listUsersDispatchThrottling.frequency` |
OPENFGA_LIST_USERS_DISPATCH_THROTTLING_FREQUENCY
| `list-users-dispatch-throttling-frequency` | string (duration) | the frequency period that the deprioritized throttling queue is evaluated for a list users request. A higher value will result in more aggressive throttling | `10µs` |
| `listUsersDispatchThrottling.threshold` |
OPENFGA_LIST_USERS_DISPATCH_THROTTLING_THRESHOLD
| `list-users-dispatch-throttling-threshold` | integer | define the number of recursive operations to occur before getting throttled for a list users request | `100` |
| `listUsersDispatchThrottling.maxThreshold` |
| `list-users-dispatch-throttling-max-threshold` | integer | define the maximum dispatch threshold beyond above which requests will be throttled for a list users request. 0 will use the 'dispatchThrottling.threshold' value as maximum | `0` |
+| `checkDatastoreThrottle.threshold` |
OPENFGA_CHECK_DATASTORE_THROTTLE_THRESHOLD
| `check-datastore-throttle-threshold` | integer | define the number of datastore requests allowed before being throttled. A value of 0 means throttling is disabled. | |
+| `checkDatastoreThrottle.duration` |
OPENFGA_CHECK_DATASTORE_THROTTLE_DURATION
| `check-datastore-throttle-duration` | string (duration) | defines the time for which the datastore request will be suspended for being throttled. | `0s` |
+| `listObjectsDatastoreThrottle.threshold` |
OPENFGA_LIST_OBJECTS_DATASTORE_THROTTLE_THRESHOLD
| `list-objects-datastore-throttle-threshold` | integer | define the number of datastore requests allowed before being throttled. A value of 0 means throttling is disabled. | |
+| `listObjectsDatastoreThrottle.duration` |
OPENFGA_LIST_OBJECTS_DATASTORE_THROTTLE_DURATION
| `list-objects-datastore-throttle-duration` | string (duration) | defines the time for which the datastore request will be suspended for being throttled. | `0s` |
+| `listUsersDatastoreThrottle.threshold` |
OPENFGA_LIST_USERS_DATASTORE_THROTTLE_THRESHOLD
| `list-users-datastore-throttle-threshold` | integer | define the number of datastore requests allowed before being throttled. A value of 0 means throttling is disabled. | |
+| `listUsersDatastoreThrottle.duration` |
OPENFGA_LIST_USERS_DATASTORE_THROTTLE_DURATION
| `list-users-datastore-throttle-duration` | string (duration) | defines the time for which the datastore request will be suspended for being throttled. | `0s` |
+| `sharedIterator.enabled` |
OPENFGA_SHARED_ITERATOR_ENABLED
| `shared-iterator-enabled` | boolean | enabling sharing of datastore iterators with different consumers. Each iterator is the result of a database query, for example usersets related to a specific object, or objects related to a specific user, up to a certain number of tuples per iterator. | `false` |
+| `sharedIterator.limit` |
OPENFGA_SHARED_ITERATOR_LIMIT
| `shared-iterator-limit` | integer | if shared-iterator-enabled is enabled, this is the limit of the number of iterators that can be shared. | `1000000` |
| `requestTimeout` |
OPENFGA_REQUEST_TIMEOUT
| `request-timeout` | string (duration) | The timeout duration for a request. | `3s` |
+| `shutdownTimeout` |
OPENFGA_SHUTDOWN_TIMEOUT
| `shutdown-timeout` | string (duration) | The timeout duration for a graceful shutdown. | `10s` |
+| `planner.initialGuess` |
OPENFGA_PLANNER_INITIAL_GUESS
| `planner-initial-guess` | string (duration) | The initial guess for the planners estimation. | `10ms` |
+| `planner.evictionThreshold` |
OPENFGA_PLANNER_EVICTION_THRESHOLD
| `planner-eviction-threshold` | | How long a planner key can be unused before being evicted. | `0` |
+| `planner.cleanupInterval` |
OPENFGA_PLANNER_CLEANUP_INTERVAL
| `planner-cleanup-interval` | string (duration) | How often the planner checks for stale keys. | `0` |
## Related Sections
diff --git a/docs/content/interacting/overview.mdx b/docs/content/interacting/overview.mdx
index 9b45bf0c8f..4e9cb8fd6a 100644
--- a/docs/content/interacting/overview.mdx
+++ b/docs/content/interacting/overview.mdx
@@ -52,5 +52,10 @@ This section helps you integrate
diff --git a/docs/content/interacting/raw-api-requests.mdx b/docs/content/interacting/raw-api-requests.mdx
new file mode 100644
index 0000000000..9258d48848
--- /dev/null
+++ b/docs/content/interacting/raw-api-requests.mdx
@@ -0,0 +1,110 @@
+---
+title: Calling Other Endpoints
+sidebar_position: 9
+slug: /interacting/raw-api-requests
+description: Making raw HTTP calls to OpenFGA endpoints not yet wrapped by the SDK
+---
+
+import {
+ DocumentationNotice,
+ ExecuteApiRequestViewer,
+ ExecuteApiRequestPathParamsViewer,
+ ExecuteApiRequestDecodeViewer,
+ ExecuteApiRequestStreamingViewer,
+ ExecuteApiRequestErrorViewer,
+ ProductName,
+ ProductNameFormat,
+ RelatedSection,
+ SdkSetupPrerequisite,
+} from '@components/Docs';
+
+# Calling Other Endpoints
+
+
+
+In certain cases you may want to call API endpoints that are not yet available as dedicated methods in the SDK. Every SDK provides an **API Executor** that lets you make raw HTTP calls to any endpoint while still honoring the client configuration — including authentication, telemetry, retries, and error handling.
+
+## When to use
+
+This is useful when:
+
+- You want to call a **new endpoint** that is not yet supported by the SDK.
+- You are using an **earlier version** of the SDK that doesn't support a particular endpoint, and can't update.
+- You have a **custom endpoint** deployed that extends the API.
+
+## Before you start
+
+
+
+## Making a request
+
+Initialize the SDK client as usual (see [Setup SDK Client](../getting-started/setup-sdk-client.mdx)), then use the API Executor to build and send a request.
+
+The examples below call a hypothetical custom endpoint (`/stores/{store_id}/custom-endpoint`), but you can use the same pattern for any API path.
+
+### Calling a custom endpoint with POST
+
+
+
+:::note
+The examples above use `POST`, but the API Executor supports any HTTP method (`GET`, `POST`, `PUT`, `DELETE`, etc.). For `GET` requests, simply omit the `body` parameter.
+:::
+
+### Using path parameters
+
+Path parameters are specified in the path using `{param_name}` syntax and must be provided via the path parameters option. They are URL-encoded automatically.
+
+
+
+### Decoding the response into a typed object
+
+Some SDKs let you decode the raw response directly into a typed struct or class, avoiding manual JSON parsing.
+
+
+
+## Streaming requests
+
+For endpoints that stream results (such as `StreamedListObjects`), use the streaming variant of the API Executor. Streaming endpoints return results incrementally as they are computed, rather than waiting for all results before responding.
+
+
+
+## Error handling
+
+Always check the response status and handle errors appropriately. The API Executor preserves the SDK's built-in error handling, but you may want to inspect status codes for custom endpoints.
+
+
+
+## SDK examples
+
+For complete working examples, refer to the API Executor examples in each SDK repository:
+
+- [Node.js](https://github.com/openfga/js-sdk/tree/main/example/api-executor)
+- [Go](https://github.com/openfga/go-sdk/tree/main/example/api_executor)
+- [.NET](https://github.com/openfga/dotnet-sdk/tree/main/example/ApiExecutorExample)
+- [Python](https://github.com/openfga/python-sdk/tree/main/example/execute-api-request)
+- [Java](https://github.com/openfga/java-sdk/tree/main/examples/api-executor)
+
+
+
diff --git a/docs/sidebars.js b/docs/sidebars.js
index 03a5f542c7..f6cd14e2da 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -425,6 +425,11 @@ const sidebars = {
label: 'AuthZEN API',
id: 'content/interacting/authzen',
},
+ {
+ type: 'doc',
+ label: 'Calling Other Endpoints',
+ id: 'content/interacting/raw-api-requests',
+ },
],
},
diff --git a/src/components/Docs/SnippetViewer/ExecuteApiRequestViewer.tsx b/src/components/Docs/SnippetViewer/ExecuteApiRequestViewer.tsx
new file mode 100644
index 0000000000..d4174296b2
--- /dev/null
+++ b/src/components/Docs/SnippetViewer/ExecuteApiRequestViewer.tsx
@@ -0,0 +1,685 @@
+import { getFilteredAllowedLangs, SupportedLanguage } from './SupportedLanguage';
+import { defaultOperationsViewer } from './DefaultTabbedViewer';
+import assertNever from 'assert-never/index';
+
+// ---------------------------------------------------------------------------
+// Shared opts
+// ---------------------------------------------------------------------------
+interface ExecuteApiRequestViewerOpts {
+ skipSetup?: boolean;
+ allowedLanguages?: SupportedLanguage[];
+}
+
+// ---------------------------------------------------------------------------
+// 1. ExecuteApiRequestViewer – POST custom endpoint (full setup)
+// ---------------------------------------------------------------------------
+
+function executeApiRequestViewer(lang: SupportedLanguage, opts: ExecuteApiRequestViewerOpts): string {
+ void opts;
+ switch (lang) {
+ case SupportedLanguage.JS_SDK:
+ return `const { OpenFgaClient } = require("@openfga/sdk");
+
+const fgaClient = new OpenFgaClient({
+ apiUrl: process.env.FGA_API_URL,
+ storeId: process.env.FGA_STORE_ID,
+});
+
+// Call a custom endpoint using path parameters
+const response = await fgaClient.executeApiRequest({
+ operationName: "CustomEndpoint", // For telemetry/logging
+ method: "POST",
+ path: "/stores/{store_id}/custom-endpoint",
+ pathParams: { store_id: process.env.FGA_STORE_ID },
+ body: {
+ user: "user:bob",
+ action: "custom_action",
+ resource: "resource:123",
+ },
+ queryParams: {
+ page_size: 20,
+ },
+});
+
+console.log("Response:", response);`;
+ case SupportedLanguage.GO_SDK:
+ return `import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+
+ openfga "github.com/openfga/go-sdk"
+ . "github.com/openfga/go-sdk/client"
+)
+
+func main() {
+ ctx := context.Background()
+ storeID := os.Getenv("FGA_STORE_ID")
+
+ fgaClient, err := NewSdkClient(&ClientConfiguration{
+ ApiUrl: os.Getenv("FGA_API_URL"),
+ StoreId: storeID,
+ })
+ if err != nil {
+ log.Fatalf("Failed to create client: %v", err)
+ }
+
+ // Get the generic API executor
+ executor := fgaClient.GetAPIExecutor()
+
+ requestBody := map[string]interface{}{
+ "user": "user:bob",
+ "action": "custom_action",
+ "resource": "resource:123",
+ }
+
+ // Build and execute the request
+ request := openfga.NewAPIExecutorRequestBuilder(
+ "CustomEndpoint", http.MethodPost, "/stores/{store_id}/custom-endpoint",
+ ).
+ WithPathParameter("store_id", storeID).
+ WithQueryParameter("page_size", "20").
+ WithBody(requestBody).
+ Build()
+
+ rawResponse, err := executor.Execute(ctx, request)
+ if err != nil {
+ log.Fatalf("Request failed: %v", err)
+ }
+
+ var result map[string]interface{}
+ if err := json.Unmarshal(rawResponse.Body, &result); err != nil {
+ log.Fatalf("Failed to decode: %v", err)
+ }
+
+ fmt.Printf("Status Code: %d\\n", rawResponse.StatusCode)
+ fmt.Printf("Response: %+v\\n", result)
+}`;
+ case SupportedLanguage.DOTNET_SDK:
+ return `using OpenFga.Sdk.Client;
+using OpenFga.Sdk.ApiClient;
+
+var configuration = new ClientConfiguration() {
+ ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL") ?? "http://localhost:8080",
+ StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"),
+};
+
+var fgaClient = new OpenFgaClient(configuration);
+var executor = fgaClient.ApiExecutor;
+
+// Build the request using fluent API
+var request = RequestBuilder