# Go SDK for OpenFGA

[![Go Reference](https://pkg.go.dev/badge/github.com/openfga/go-sdk.svg)](https://pkg.go.dev/github.com/openfga/go-sdk)
[![Socket Badge](https://badge.socket.dev/go/package/github.com/openfga/go-sdk)](https://socket.dev/go/package/github.com/openfga/go-sdk)
[![DeepWiki](https://img.shields.io/badge/DeepWiki-openfga%2Fgo--sdk-blue.svg?logo=data:image/png;base64, )](https://deepwiki.com/openfga/go-sdk)
[![Release](https://img.shields.io/github/v/release/openfga/go-sdk?sort=semver&color=green)](https://github.com/openfga/go-sdk/releases)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fopenfga%2Fgo-sdk.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fopenfga%2Fgo-sdk?ref=badge_shield)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/openfga/go-sdk/badge)](https://securityscorecards.dev/viewer/?uri=github.com/openfga/go-sdk)
[![Join our community](https://img.shields.io/badge/slack-cncf_%23openfga-40abb8.svg?logo=slack)](https://openfga.dev/community)
[![X](https://img.shields.io/twitter/follow/openfga?color=%23179CF0&logo=x&style=flat-square "@openfga on X")](https://x.com/openfga)

This is an autogenerated Go SDK for OpenFGA. It provides a wrapper around the [OpenFGA API definition](https://openfga.dev/api).

## Table of Contents

- [About OpenFGA](#about)
- [Resources](#resources)
- [Installation](#installation)
- [Getting Started](#getting-started)
  - [Initializing the API Client](#initializing-the-api-client)
  - [Custom Headers](#custom-headers)
  - [Get your Store ID](#get-your-store-id)
  - [Calling the API](#calling-the-api)
    - [Stores](#stores)
      - [List All Stores](#list-stores)
      - [Create a Store](#create-store)
      - [Get a Store](#get-store)
      - [Delete a Store](#delete-store)
    - [Authorization Models](#authorization-models)
      - [Read Authorization Models](#read-authorization-models)
      - [Write Authorization Model](#write-authorization-model)
      - [Read a Single Authorization Model](#read-a-single-authorization-model)
      - [Read the Latest Authorization Model](#read-the-latest-authorization-model)
    - [Relationship Tuples](#relationship-tuples)
      - [Read Relationship Tuple Changes (Watch)](#read-relationship-tuple-changes-watch)
      - [Read Relationship Tuples](#read-relationship-tuples)
      - [Write (Create and Delete) Relationship Tuples](#write-create-and-delete-relationship-tuples)
    - [Relationship Queries](#relationship-queries)
      - [Check](#check)
      - [Batch Check](#batch-check)
      - [Expand](#expand)
      - [List Objects](#list-objects)
      - [Streamed List Objects](#streamed-list-objects)
      - [List Relations](#list-relations)
      - [List Users](#list-users)
    - [Assertions](#assertions)
      - [Read Assertions](#read-assertions)
      - [Write Assertions](#write-assertions)
  - [Calling other endpoints](#calling-other-endpoints)
  - [Retries](#retries)
  - [API Endpoints](#api-endpoints)
  - [Models](#models)
  - [OpenTelemetry](#opentelemetry)
- [Contributing](#contributing)
  - [Issues](#issues)
  - [Pull Requests](#pull-requests)
- [License](#license)

## About

[OpenFGA](https://openfga.dev) is an open source Fine-Grained Authorization solution inspired by [Google's Zanzibar paper](https://research.google/pubs/pub48190/). It was created by the FGA team at [Auth0](https://auth0.com) based on [Auth0 Fine-Grained Authorization (FGA)](https://fga.dev), available under [a permissive license (Apache-2)](https://github.com/openfga/rfcs/blob/main/LICENSE) and welcomes community contributions.

OpenFGA is designed to make it easy for application builders to model their permission layer, and to add and integrate fine-grained authorization into their applications. OpenFGA’s design is optimized for reliability and low latency at a high scale.


## Resources

- [OpenFGA Documentation](https://openfga.dev/docs)
- [OpenFGA API Documentation](https://openfga.dev/api/service)
- [X](https://x.com/openfga)
- [OpenFGA Community](https://openfga.dev/community)
- [Zanzibar Academy](https://zanzibar.academy)
- [Google's Zanzibar Paper (2019)](https://research.google/pubs/pub48190/)

## Installation

To install:

```
go get -u github.com/openfga/go-sdk
```

In your code, import the module and use it:

```go
import "github.com/openfga/go-sdk"

func Main() {
	configuration, err := openfga.NewConfiguration(openfga.Configuration{})
}
```

You can then run

```shell
go mod tidy
```

to update `go.mod` and `go.sum` if you are using them.


## Getting Started

### Initializing the API Client

[Learn how to initialize your SDK](https://openfga.dev/docs/getting-started/setup-sdk-client)

We strongly recommend you initialize the `OpenFgaClient` only once and then re-use it throughout your app, otherwise you will incur the cost of having to re-initialize multiple times or at every request, the cost of reduced connection pooling and re-use, and would be particularly costly in the client credentials flow, as that flow will be preformed on every request.

> The `openfgaClient` will by default retry API requests up to 3 times on 429 and 5xx errors.

#### No Credentials

```golang
import (
    . "github.com/openfga/go-sdk/client"
    "os"
)

func main() {
    fgaClient, err := NewSdkClient(&ClientConfiguration{
        ApiUrl:  os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
        StoreId: os.Getenv("FGA_STORE_ID"), // not needed when calling `CreateStore` or `ListStores`
        AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // optional, recommended to be set for production
    })

	if err != nil {
        // .. Handle error
    }
}
```

#### API Token

```golang
import (
    . "github.com/openfga/go-sdk/client"
    "github.com/openfga/go-sdk/credentials"
    "os"
)

func main() {
    fgaClient, err := NewSdkClient(&ClientConfiguration{
        ApiUrl:      os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
        StoreId:     os.Getenv("FGA_STORE_ID"), // not needed when calling `CreateStore` or `ListStores`
        AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // optional, recommended to be set for production
        Credentials: &credentials.Credentials{
            Method: credentials.CredentialsMethodApiToken,
            Config: &credentials.Config{
                ApiToken: os.Getenv("FGA_API_TOKEN"), // will be passed as the "Authorization: Bearer ${ApiToken}" request header
            },
        },
    })

    if err != nil {
        // .. Handle error
    }
}
```

#### Client Credentials

```golang
import (
    openfga "github.com/openfga/go-sdk"
    . "github.com/openfga/go-sdk/client"
    "github.com/openfga/go-sdk/credentials"
    "os"
)

func main() {
    fgaClient, err := NewSdkClient(&ClientConfiguration{
        ApiUrl:               os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
        StoreId:              os.Getenv("FGA_STORE_ID"), // not needed when calling `CreateStore` or `ListStores`
        AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // optional, recommended to be set for production
        Credentials: &credentials.Credentials{
            Method: credentials.CredentialsMethodClientCredentials,
            Config: &credentials.Config{
                ClientCredentialsClientId:       os.Getenv("FGA_CLIENT_ID"),
                ClientCredentialsClientSecret:   os.Getenv("FGA_CLIENT_SECRET"),
                ClientCredentialsApiAudience:    os.Getenv("FGA_API_AUDIENCE"),
                ClientCredentialsApiTokenIssuer: os.Getenv("FGA_API_TOKEN_ISSUER"),
            },
        },
    })

    if err != nil {
        // .. Handle error
    }
}
```

#### OAuth2 Client Credentials

```golang
import (
    openfga "github.com/openfga/go-sdk"
    . "github.com/openfga/go-sdk/client"
    "github.com/openfga/go-sdk/credentials"
    "os"
)

func main() {
    fgaClient, err := NewSdkClient(&ClientConfiguration{
        ApiUrl:               os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
        StoreId:              os.Getenv("FGA_STORE_ID"), // not needed when calling `CreateStore` or `ListStores`
        AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // optional, recommended to be set for production
        Credentials: &credentials.Credentials{
            Method: credentials.CredentialsMethodClientCredentials,
            Config: &credentials.Config{
                ClientCredentialsClientId:       os.Getenv("FGA_CLIENT_ID"),
                ClientCredentialsClientSecret:   os.Getenv("FGA_CLIENT_SECRET"),
                ClientCredentialsScopes:         os.Getenv("FGA_API_SCOPES"), // optional space separated scopes
                ClientCredentialsApiTokenIssuer: os.Getenv("FGA_API_TOKEN_ISSUER"),
            },
        },
    })

    if err != nil {
        // .. Handle error
    }
}
```

### Custom Headers

#### Default Headers
You can set default headers that will be sent with every request during client initialization:

```golang
fgaClient, err := client.NewSdkClient(&client.ClientConfiguration{
    ApiUrl:               os.Getenv("FGA_API_URL"),
    StoreId:              os.Getenv("FGA_STORE_ID"),
    AuthorizationModelId: os.Getenv("FGA_MODEL_ID"),
    DefaultHeaders: map[string]string{
        "X-Custom-Header": "default-value",
        "X-Request-Source": "my-app",
    },
})
```

#### Per-Request Headers

You can also send custom headers on a per-request basis by using the `Options` parameter. Custom headers will override any default headers set in the client configuration.

```golang
// Add custom headers to a specific request
checkResponse, err := fgaClient.Check(context.Background()).
    Body(client.ClientCheckRequest{
        User:     "user:anne",
        Relation: "viewer",
        Object:   "document:roadmap",
    }).
    Options(client.ClientCheckOptions{
        RequestOptions: client.RequestOptions{
            Headers: map[string]string{
                "X-Request-ID": "123e4567-e89b-12d3-a456-426614174000",
                "X-Custom-Header": "custom-value", // these override any default headers set
            },
        },
    }).
    Execute()
```


### Get your Store ID

You need your store id to call the OpenFGA API (unless it is to call the [CreateStore](#create-store) or [ListStores](#list-stores) methods).

If your server is configured with [authentication enabled](https://openfga.dev/docs/getting-started/setup-openfga#configuring-authentication), you also need to have your credentials ready.

### Calling the API

#### Stores

##### List Stores

Get a paginated list of stores.

[API Documentation](https://openfga.dev/api/service#/Stores/ListStores)

```golang
options := ClientListStoresOptions{
  PageSize:          openfga.PtrInt32(10),
  ContinuationToken: openfga.PtrString("..."),
  Name:              openfga.PtrString("FGA Demo Store"), // Optional: filter stores by name
}
stores, err := fgaClient.ListStores(context.Background()).Options(options).Execute()

// stores = [{ "id": "01FQH7V8BEG3GPQW93KTRFR8JB", "name": "FGA Demo Store", "created_at": "2022-01-01T00:00:00.000Z", "updated_at": "2022-01-01T00:00:00.000Z" }]
```

##### Create Store

Create and initialize a store.

[API Documentation](https://openfga.dev/api/service#/Stores/CreateStore)

```golang
body := ClientCreateStoreRequest{Name: "FGA Demo"}
store, err := fgaClient.CreateStore(context.Background()).Body(body).Execute()
if err != nil {
    // handle error
}

// store.Id = "01FQH7V8BEG3GPQW93KTRFR8JB"

// store store.Id in database
// update the storeId of the current instance
fgaClient.SetStoreId(store.Id)
// continue calling the API normally, scoped to this store
```

##### Get Store

Get information about the current store.

[API Documentation](https://openfga.dev/api/service#/Stores/GetStore)

```golang
options := ClientGetStoreOptions{
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
store,  err := fgaClient.GetStore(context.Background()).Options(options)Execute()
if err != nil {
    // handle error
}

// store = { "id": "01FQH7V8BEG3GPQW93KTRFR8JB", "name": "FGA Demo Store", "created_at": "2022-01-01T00:00:00.000Z", "updated_at": "2022-01-01T00:00:00.000Z" }
```

##### Delete Store

Delete a store.

[API Documentation](https://openfga.dev/api/service#/Stores/DeleteStore)

```golang
options := ClientDeleteStoreOptions{
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
_,  err := fgaClient.DeleteStore(context.Background()).Options(options).Execute()
if err != nil {
    // handle error
}
```
#### Authorization Models

##### Read Authorization Models

Read all authorization models in the store.

[API Documentation](https://openfga.dev/api/service#/Authorization%20Models/ReadAuthorizationModels)

```golang
options := ClientReadAuthorizationModelsOptions{
    PageSize: openfga.PtrInt32(10),
    ContinuationToken: openfga.PtrString("..."),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"),
}
data, err := fgaClient.ReadAuthorizationModels(context.Background()).Options(options).Execute()

// data.AuthorizationModels = [
// { Id: "01GXSA8YR785C4FYS3C0RTG7B1", SchemaVersion: "1.1", TypeDefinitions: [...] },
// { Id: "01GXSBM5PVYHCJNRNKXMB4QZTW", SchemaVersion: "1.1", TypeDefinitions: [...] }];
```

##### Write Authorization Model

Create a new authorization model.

[API Documentation](https://openfga.dev/api/service#/Authorization%20Models/WriteAuthorizationModel)

> Note: To learn how to build your authorization model, check the Docs at https://openfga.dev/docs.

> Learn more about [the OpenFGA configuration language](https://openfga.dev/docs/configuration-language).

> You can use the [OpenFGA Syntax Transformer](https://github.com/openfga/syntax-transformer) to convert between the friendly DSL and the JSON authorization model.

###### Instantiating the model inline

You can instantiate the model inline by constructing a WriteAuthorizationModelRequest with [TypeDefinition](model_type_definition.go) entries:

```golang
body := ClientWriteAuthorizationModelRequest{
  SchemaVersion: "1.1",
  TypeDefinitions: []openfga.TypeDefinition{
    {Type: "user", Relations: &map[string]openfga.Userset{}},
    {
      Type: "document",
      Relations: &map[string]openfga.Userset{
        "writer": {
          This: &map[string]interface{}{},
        },
        "viewer": {Union: &openfga.Usersets{
          Child: &[]openfga.Userset{
            {This: &map[string]interface{}{}},
            {ComputedUserset: &openfga.ObjectRelation{
              Object:   openfga.PtrString(""),
              Relation: openfga.PtrString("writer"),
            }},
          },
        }},
      },
      Metadata: &openfga.Metadata{
        Relations: &map[string]openfga.RelationMetadata{
          "writer": {
            DirectlyRelatedUserTypes: &[]openfga.RelationReference{
              {Type: "user"},
            },
          },
          "viewer": {
            DirectlyRelatedUserTypes: &[]openfga.RelationReference{
              {Type: "user"},
            },
          },
        },
      },
    }},
}
options := ClientWriteAuthorizationModelOptions{
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
data, err := fgaClient.WriteAuthorizationModel(context.Background()).Options(options).Body(body).Execute()

fmt.Printf("%s", data.AuthorizationModelId) // 01GXSA8YR785C4FYS3C0RTG7B1
```

###### Instantiating the model from a JSON file

You can instantiate the model by loading it from a JSON file:

```golang
jsonContent, _ := os.ReadFile("model.json")
var body openfga.WriteAuthorizationModelRequest
json.Unmarshal(jsonContent, &body)

options := ClientWriteAuthorizationModelOptions{
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}

data, err := fgaClient.WriteAuthorizationModel(context.Background()).Options(options).Body(body).Execute()

fmt.Printf("%s", data.AuthorizationModelId) // 01GXSA8YR785C4FYS3C0RTG7B1
```

###### Instantiating the model from a DSL file

You can also load the model from an `.fga` file. To achieve that, you need to use the [github.com/openfga/language/pkg/go/transformer](https://github.com/openfga/language/tree/main/pkg/go) module:

```golang
import (
    "github.com/openfga/language/pkg/go/transformer"
)
```

```golang
dslContent, _ := os.ReadFile("model.fga")
jsonModel, _ := transformer.TransformDSLToJSON(string(dslContent))
var body openfga.WriteAuthorizationModelRequest
json.Unmarshal([]byte(jsonModel), &body)

options := ClientWriteAuthorizationModelOptions{
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}

data, err := fgaClient.WriteAuthorizationModel(context.Background()).Options(options).Body(body).Execute()

fmt.Printf("%s", data.AuthorizationModelId) // 01GXSA8YR785C4FYS3C0RTG7B1

```

##### Read a Single Authorization Model

Read a particular authorization model.

[API Documentation](https://openfga.dev/api/service#/Authorization%20Models/ReadAuthorizationModel)

```golang
options := ClientReadAuthorizationModelOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString(modelId),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
data, err := fgaClient.ReadAuthorizationModel(context.Background()).Options(options).Execute()

// data = {"authorization_model":{"id":"01GXSA8YR785C4FYS3C0RTG7B1","schema_version":"1.1","type_definitions":[{"type":"document","relations":{"writer":{"this":{}},"viewer":{ ... }}},{"type":"user"}]}} // JSON

fmt.Printf("%s", data.AuthorizationModel.Id) // 01GXSA8YR785C4FYS3C0RTG7B1
```

##### Read the Latest Authorization Model

Reads the latest authorization model (note: this ignores the model id in configuration).

[API Documentation](https://openfga.dev/api/service#/Authorization%20Models/ReadAuthorizationModel)

```golang
options := ClientReadLatestAuthorizationModelOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString(modelId),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
data, err := fgaClient.ReadLatestAuthorizationModel(context.Background()).Options(options)Execute()

// data.AuthorizationModel.Id = "01GXSA8YR785C4FYS3C0RTG7B1"
// data.AuthorizationModel.SchemaVersion = "1.1"
// data.AuthorizationModel.TypeDefinitions = [{ "type": "document", "relations": { ... } }, { "type": "user", "relations": { ... }}]

fmt.Printf("%s", (*data.AuthorizationModel).GetId()) // 01GXSA8YR785C4FYS3C0RTG7B1
```

#### Relationship Tuples

##### Read Relationship Tuple Changes (Watch)

Reads the list of historical relationship tuple writes and deletes.

[API Documentation](https://openfga.dev/api/service#/Relationship%20Tuples/ReadChanges)

```golang
body := ClientReadChangesRequest{
    Type: "document",
    StartTime: openfga.PtrString("2022-01-01T00:00:00Z"),
}
options := ClientReadChangesOptions{
    PageSize: openfga.PtrInt32(10),
    ContinuationToken: openfga.PtrString("eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ=="),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
data, err := fgaClient.ReadChanges(context.Background()).Body(body).Options(options).Execute()

// data.ContinuationToken = ...
// data.Changes = [
//   { TupleKey: { User, Relation, Object }, Operation: TupleOperation.WRITE, Timestamp: ... },
//   { TupleKey: { User, Relation, Object }, Operation: TupleOperation.DELETE, Timestamp: ... }
// ]
```

##### Read Relationship Tuples

Reads the relationship tuples stored in the database. It does not evaluate nor exclude invalid tuples according to the authorization model.

[API Documentation](https://openfga.dev/api/service#/Relationship%20Tuples/Read)

```golang
// Find if a relationship tuple stating that a certain user is a viewer of a certain document
body := ClientReadRequest{
    User:     openfga.PtrString("user:81684243-9356-4421-8fbf-a4f8d36aa31b"),
    Relation: openfga.PtrString("viewer"),
    Object:   openfga.PtrString("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"),
}

// Find all relationship tuples where a certain user has a relationship as any relation to a certain document
body := ClientReadRequest{
    User:     openfga.PtrString("user:81684243-9356-4421-8fbf-a4f8d36aa31b"),
    Object:   openfga.PtrString("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"),
}

// Find all relationship tuples where a certain user is a viewer of any document
body := ClientReadRequest{
    User:     openfga.PtrString("user:81684243-9356-4421-8fbf-a4f8d36aa31b"),
    Relation: openfga.PtrString("viewer"),
    Object:   openfga.PtrString("document:"),
}

// Find all relationship tuples where any user has a relationship as any relation with a particular document
body := ClientReadRequest{
    Object:   openfga.PtrString("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"),
}

// Read all stored relationship tuples
body := ClientReadRequest{}

options := ClientReadOptions{
    PageSize: openfga.PtrInt32(10),
    ContinuationToken: openfga.PtrString("eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ=="),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
data, err := fgaClient.Read(context.Background()).Body(requestBody).Options(options).Execute()

// In all the above situations, the response will be of the form:
// data = { Tuples: [{ Key: { User, Relation, Object }, Timestamp }, ...]}
```

##### Write (Create and Delete) Relationship Tuples

Create and/or delete relationship tuples to update the system state.

[API Documentation](https://openfga.dev/api/service#/Relationship%20Tuples/Write)

###### Transaction mode (default)

By default, write runs in a transaction mode where any invalid operation (deleting a non-existing tuple, creating an existing tuple, one of the tuples was invalid) or a server error will fail the entire operation.

```golang
body := ClientWriteRequest{
    Writes: &[]ClientTupleKey{ {
        User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation: "viewer",
        Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    }, {
        User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation: "viewer",
        Object:   "document:0192ab2d-d36e-7cb3-a4a8-5d1d67a300c5",
    } },
    Deletes: &[]ClientTupleKeyWithoutCondition{ {
        User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation: "writer",
        Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    } }
}

options := ClientWriteOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
data, err := fgaClient.Write(context.Background()).Body(body).Options(options).Execute()
```

Convenience `WriteTuples` and `DeleteTuples` methods are also available.

###### Non-transaction mode

The SDK will split the writes into separate chunks and send them in separate requests. Each chunk is a transaction. By default, each chunk is set to 1, but you may override that.

```golang
body := ClientWriteRequest{
    Writes: &[]ClientTupleKey{ {
        User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation: "viewer",
        Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    }, {
        User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation: "viewer",
        Object:   "document:0192ab2d-d36e-7cb3-a4a8-5d1d67a300c5",
    } },
	  Deletes: &[]ClientTupleKeyWithoutCondition{ {
      User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
      Relation: "writer",
      Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    } }
}

options := ClientWriteOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
    Transaction: &TransactionOptions{
        Disable: true,
        MaxParallelRequests: 5, // Maximum number of requests to issue in parallel
        MaxPerChunk: 1, // Maximum number of requests to be sent in a transaction in a particular chunk
    },
}
data, err := fgaClient.Write(context.Background()).Body(body).Options(options).Execute()

// data.Writes = [{
//   TupleKey: { User, Relation, Object },
//   Status: "CLIENT_WRITE_STATUS_SUCCESS
//   HttpResponse: ... // http response"
// }, {
//   TupleKey: { User, Relation, Object },
//   Status: "CLIENT_WRITE_STATUS_FAILURE
//   HttpResponse: ... // http response"
//   Error: ...
// }]
// data.Deletes = [{
//   TupleKey: { User, Relation, Object },
//   Status: "CLIENT_WRITE_STATUS_SUCCESS
//   HttpResponse: ... // http response"
// }]
```

#### Conflict Options for Write Operations

The SDK supports conflict options for write operations, allowing you to control how the API handles duplicate writes and missing deletes.

> Note: This requires OpenFGA [v1.10.0](https://github.com/openfga/openfga/releases/tag/v1.10.0) or later.

```go
options := ClientWriteOptions{
    Conflict: ClientWriteConflictOptions{
        // Control what happens when writing a tuple that already exists
        OnDuplicateWrites: CLIENT_WRITE_REQUEST_ON_DUPLICATE_WRITES_IGNORE, // or CLIENT_WRITE_REQUEST_ON_DUPLICATE_WRITES_ERROR (the current default behavior)

        // Control what happens when deleting a tuple that doesn't exist
        OnMissingDeletes: CLIENT_WRITE_REQUEST_ON_MISSING_DELETES_IGNORE, // or CLIENT_WRITE_REQUEST_ON_MISSING_DELETES_ERROR (the current default behavior)
    },
}

body := ClientWriteRequest{
    Writes: []ClientTupleKey{ {
        User:     "user:anne",
        Relation: "writer",
        Object:   "document:2021-budget",
    } },
    Deletes: []ClientTupleKeyWithoutCondition{ {
        User:     "user:bob",
        Relation: "reader",
        Object:   "document:2021-budget",
    } },
}

data, err := fgaClient.Write(context.Background()).
    Body(body).
    Options(options).
    Execute()
```

#### Relationship Queries

##### Check

Check if a user has a particular relation with an object.

[API Documentation](https://openfga.dev/api/service#/Relationship%20Queries/Check)

> Provide a tuple and ask the OpenFGA API to check for a relationship

```golang
body := ClientCheckRequest{
    User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
    Relation: "viewer",
    Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    ContextualTuples: &[]ClientTupleKey{ {
        User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation: "editor",
        Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    } },
}

options := ClientCheckOptions{
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
data, err := fgaClient.Check(context.Background()).Body(body).Options(options).Execute()

// data = {"allowed":true,"resolution":""} // in JSON

fmt.Printf("%t", data.GetAllowed()) // True

```

##### Batch Check

Similar to [Check](#Check), but instead of checking a single user-object relationship, accepts a list of relationships to check. Requires OpenFGA version 1.8.0 or greater.

[API Documentation](https://openfga.dev/api/service#/Relationship%20Queries/BatchCheck)

> **Note**: The order of `BatchCheck` results is not guaranteed to match the order of the checks provided. Use `correlationId` to pair responses with requests.

If you are using an OpenFGA version less than 1.8.0, you can use the `ClientBatchCheck` function, 
which calls `check` in parallel. It will return `allowed: false` if it encounters an error, and will return the error in the body.
If 429s or 5xxs are encountered, the underlying check will retry up to 3 times before giving up.

```golang
options := BatchCheckOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
    MaxParallelRequests: openfga.PtrInt32(5), // Max number of requests to issue in parallel, defaults to 10
}

body := ClientBatchCheckRequest{
	Checks: []ClientBatchCheckItem{ {
		CorrelationId: "f278708f-298c-4f43-a893-11a02bbf251c",
        User:          "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation:      "viewer",
        Object:        "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
        ContextualTuples: &[]ClientTupleKey{ {
            User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
            Relation: "editor",
            Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
        } },
    }, {
        CorrelationId: "9f7563d6-2573-4292-9ba2-62d59b97c4d",
        User:          "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation:      "admin",
        Object:        "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
        ContextualTuples: &[]ClientTupleKey{ {
            User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
            Relation: "editor",
            Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
        } },
    }, {
        CorrelationId: "dbb8311c-d991-488d-82e6-76b0d6a3d7a2",
        User:          "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation:      "creator",
        Object:        "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    }, {
        CorrelationId: "9b17215d-89e0-47b4-aaf4-6476dc49649a",
        User:          "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation:      "deleter",
        Object:        "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    } }
}

data, err := fgaClient.BatchCheck(context.Background()).Body(body).Options(options).Execute()

/*
// Results are a map keyed by correlationId
// Example:
data.GetResult() = {
  "f278708f-298c-4f43-a893-11a02bbf251c": {
     Allowed: false,
     Error: <FgaError ...>
  },
  "9f7563d6-2573-4292-9ba2-62d59b97c4d": {
     Allowed: false,
     Error: <FgaError ...>
  },
  "dbb8311c-d991-488d-82e6-76b0d6a3d7a2": {
     Allowed: false,
     Error: <FgaError ...>
  },
  "9b17215d-89e0-47b4-aaf4-6476dc49649a": {
     Allowed: false,
     Error: <FgaError ...>
  }
}
*/
```

##### Expand

Expands the relationships in userset tree format.

[API Documentation](https://openfga.dev/api/service#/Relationship%20Queries/Expand)

```golang
options := ClientExpandOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
body := ClientExpandRequest{
    Relation: "viewer",
    Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
}
data, err := fgaClient.Expand(context.Background()).Body(body).Options(options).Execute()

// data.Tree.Root = {"name":"document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a#viewer","leaf":{"users":{"users":["user:81684243-9356-4421-8fbf-a4f8d36aa31b","user:f52a4f7a-054d-47ff-bb6e-3ac81269988f"]}}}
```

You can also expand with contextual tuples:

```golang
options := ClientExpandOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"),
}
body := ClientExpandRequest{
    Relation: "viewer",
    Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    ContextualTuples: []ClientContextualTupleKey{ {
        User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation: "editor",
        Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    } },
}
data, err := fgaClient.Expand(context.Background()).Body(body).Options(options).Execute()

// data.Tree.Root = {"name":"document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a#viewer","leaf":{"users":{"users":["user:81684243-9356-4421-8fbf-a4f8d36aa31b","user:f52a4f7a-054d-47ff-bb6e-3ac81269988f"]}}}
```

#### List Objects

List the objects of a particular type a user has access to.

[API Documentation](https://openfga.dev/api/service#/Relationship%20Queries/ListObjects)

```golang
options := ClientListObjectsOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
body := ClientListObjectsRequest{
    User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
    Relation: "can_read",
    Type:     "document",
    ContextualTuples: &[]ClientTupleKey{ {
        User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation: "editor",
        Object:   "folder:product",
    }, {
        User:     "folder:product",
        Relation: "parent",
        Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    } },
}
data, err := fgaClient.ListObjects(context.Background()).
  Body(requestBody).
  Options(options).
  Execute()

// data.Objects = ["document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"]
```

##### Streamed List Objects

List objects of a particular type that the user has access to, using the streaming API.

The Streamed ListObjects API is very similar to the ListObjects API, with two key differences:
1. **Streaming Results**: Instead of collecting all objects before returning a response, it streams them to the client as they are collected.
2. **No Pagination Limit**: Returns all results without the 1000-object limit of the standard ListObjects API.

This is particularly useful when querying **computed relations** that may return large result sets.

[API Documentation](https://openfga.dev/api/service#/Relationship%20Queries/StreamedListObjects)

```golang
options := ClientStreamedListObjectsOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
}

body := ClientStreamedListObjectsRequest{
    User:     "user:anne",
    Relation: "can_read",
    Type:     "document",
}

response, err := fgaClient.StreamedListObjects(context.Background()).Body(body).Options(options).Execute()
if err != nil {
    // .. Handle error
}
defer response.Close()

// Consume objects from the stream
var objects []string
for obj := range response.Objects {
    objects = append(objects, obj.Object)
}

// Check for any errors during streaming
if err := <-response.Errors; err != nil {
    // .. Handle streaming error
}

// objects = ["document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"]
```

#### List Relations

List the relations a user has on an object.

```golang
options := ClientListRelationsOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
    // Max number of requests to issue in parallel, defaults to 10
    MaxParallelRequests: openfga.PtrInt32(5),
}
body := ClientListRelationsRequest{
    User:      "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
    Object:    "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    Relations: []string{"can_view", "can_edit", "can_delete", "can_rename"},
    ContextualTuples: &[]ClientTupleKey{ {
        User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation: "editor",
        Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    } },
}
data, err := fgaClient.ListRelations(context.Background()).
  Body(requestBody).
  Options(options).
  Execute()

// data.Relations = ["can_view", "can_edit"]
```

##### List Users

List the users who have a certain relation to a particular type.

[API Documentation](https://openfga.dev/api/service#/Relationship%20Queries/ListUsers)

```golang
options := ClientListRelationsOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
    // Max number of requests to issue in parallel, defaults to 10
    MaxParallelRequests: openfga.PtrInt32(5),
}

// Only a single filter is allowed by the API for the time being
userFilters := []openfga.UserTypeFilter{{ Type: "user" }}
// user filters can also be of the form
// userFilters := []openfga.UserTypeFilter{{ Type: "team", Relation: openfga.PtrString("member") }}

requestBody := ClientListUsersRequest{
    Object: openfga.FgaObject{
        Type: "document",
        Id:   "roadmap",
    },
    Relation: "can_read",
    UserFilters: userFilters,
    ContextualTuples: []ClientContextualTupleKey{{
        User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation: "editor",
        Object:   "folder:product",
    }, {
        User:     "folder:product",
        Relation: "parent",
        Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
    }},
    Context: &map[string]interface{}{"ViewCount": 100},
}
data, err := fgaClient.ListRelations(context.Background()).
  Body(requestBody).
  Options(options).
  Execute()

// response.users = [{object: {type: "user", id: "81684243-9356-4421-8fbf-a4f8d36aa31b"}}, {userset: { type: "user" }}, ...]
```

### Assertions

#### Read Assertions

Read assertions for a particular authorization model.

[API Documentation](https://openfga.dev/api/service#/Assertions/Read%20Assertions)

```golang
options := ClientReadAssertionsOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId: openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
data, err := fgaClient.ReadAssertions(context.Background()).
  Options(options).
  Execute()
```

#### Write Assertions

Update the assertions for a particular authorization model.

[API Documentation](https://openfga.dev/api/service#/Assertions/Write%20Assertions)

```golang
options := ClientWriteAssertionsOptions{
    // You can rely on the model id set in the configuration or override it for this specific request
    AuthorizationModelId: openfga.PtrString("01GAHCE4YVKPQEKZQHT2R89MQV"),
    // You can rely on the store id set in the configuration or override it for this specific request
    StoreId:openfga.PtrString("01FQH7V8BEG3GPQW93KTRFR8JB"), 
}
requestBody := ClientWriteAssertionsRequest{
    ClientAssertion{
        User:        "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
        Relation:    "can_view",
        Object:      "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
        Expectation: true,
        Context: &map[string]interface{}{
            "context": "value",
        },
        ContextualTuples: []ClientContextualTupleKey{
            {
                User:     "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
                Relation: "can_view",
                Object:   "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
            },
        },
    },
}
data, err := fgaClient.WriteAssertions(context.Background()).
  Body(requestBody).
  Options(options).
  Execute()
```

### Calling Other Endpoints

In certain cases you may want to call other APIs not yet wrapped by the SDK. You can do so by using the `APIExecutor` available from the `fgaClient`.
The `APIExecutor` allows you to make raw HTTP calls to any OpenFGA endpoint by specifying the operation name, HTTP method, path, parameters, body, and headers, while still honoring the client configuration (authentication, telemetry, retries, and error handling).

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 yet support a particular endpoint
- you have a custom endpoint deployed that extends the OpenFGA API

In all cases, you initialize the SDK the same way as usual, and then get the `APIExecutor` from the `fgaClient` instance.

```go
// Initialize the client, same as above
fgaClient, err := NewSdkClient(&ClientConfiguration{...})

// Get the generic API executor
executor := fgaClient.GetAPIExecutor()

// Custom new endpoint that doesn't exist in the SDK yet
requestBody := map[string]interface{}{
    "user":     "user:bob",
    "action":   "custom_action",
    "resource": "resource:123",
}

// Build the request
request := openfga.NewAPIExecutorRequestBuilder("CustomEndpoint", http.MethodPost, "/stores/{store_id}/custom-endpoint").
    WithPathParameter("store_id", storeID).
    WithQueryParameter("page_size", "20").
    WithQueryParameter("continuation_token", "eyJwayI6...").
    WithBody(requestBody).
    WithHeader("X-Experimental-Feature", "enabled").
    Build()
```

#### Example: Calling a new "Custom Endpoint" endpoint and handling raw response

```go
// Get raw response without automatic decoding
rawResponse, err := executor.Execute(ctx, request)

if err != nil {
    log.Fatalf("Custom endpoint failed: %v", err)
}

// Manually decode the response
var result map[string]interface{}
if err := json.Unmarshal(rawResponse.Body, &result); err != nil {
    log.Fatalf("Failed to decode: %v", err)
}

fmt.Printf("Response: %+v\n", result)

// You can access fields like headers, status code, etc. from rawResponse:
fmt.Printf("Status Code: %d\n", rawResponse.StatusCode)
fmt.Printf("Headers: %+v\n", rawResponse.Headers)
```

#### Example: Calling a new "Custom Endpoint" endpoint and decoding response into a struct

```go
// Define a struct to hold the response
type CustomEndpointResponse struct {
    Allowed bool   `json:"allowed"`
    Reason  string `json:"reason"`
}
var customEndpointResponse CustomEndpointResponse

// Get raw response decoded into CustomEndpointResponse struct
rawResponse, err := executor.ExecuteWithDecode(ctx, request, &customEndpointResponse) // Pass pointer to struct for decoding

if err != nil {
    log.Fatalf("Custom endpoint failed: %v", err)
}

fmt.Printf("Response: %+v\n", customEndpointResponse)

// You can access fields like headers, status code, etc. from rawResponse:
fmt.Printf("Status Code: %d\n", rawResponse.StatusCode)
fmt.Printf("Headers: %+v\n", rawResponse.Headers)
```

#### Example: Calling streaming endpoints (e.g., StreamedListObjects)

For streaming API endpoints, use the `ExecuteStreaming` method. This is useful for endpoints like `StreamedListObjects` that stream results as they are computed rather than waiting for all results before responding.

```go
// Get the generic API executor
executor := fgaClient.GetAPIExecutor()

// Build a streaming request for StreamedListObjects
request := openfga.NewAPIExecutorRequestBuilder("StreamedListObjects", http.MethodPost, "/stores/{store_id}/streamed-list-objects").
    WithPathParameter("store_id", storeID).
    WithBody(openfga.ListObjectsRequest{
        AuthorizationModelId: openfga.PtrString(modelID),
        Type:                 "document",
        Relation:             "viewer",
        User:                 "user:alice",
    }).
    Build()

// Execute the streaming request
// The bufferSize parameter controls how many results can be buffered in the channel
channel, err := executor.ExecuteStreaming(ctx, request, openfga.DefaultStreamBufferSize)
if err != nil {
    log.Fatalf("Streaming request failed: %v", err)
}
defer channel.Close() // Always close the channel when done

// Process results as they stream in
for {
    select {
    case result, ok := <-channel.Results:
        if !ok {
            // Results channel closed, stream completed
            // Check for any final errors
            select {
            case err := <-channel.Errors:
                if err != nil {
                    log.Fatalf("Stream error: %v", err)
                }
            default:
            }
            fmt.Println("Stream completed successfully")
            return
        }
        // Decode the raw JSON bytes into a typed response
        var response openfga.StreamedListObjectsResponse
        if err := json.Unmarshal(result, &response); err != nil {
            log.Fatalf("Failed to decode stream result: %v", err)
        }
        fmt.Printf("Received object: %s\n", response.Object)
    case err := <-channel.Errors:
        if err != nil {
            log.Fatalf("Stream error: %v", err)
        }
    }
}
```

The `ExecuteStreaming` method returns an `APIExecutorStreamingChannel` with:
- `Results chan []byte`: Raw JSON bytes for each streamed result
- `Errors chan error`: Any errors that occur during streaming
- `Close()`: Method to cancel streaming and cleanup resources


### Retries

If a network request fails with a 429 or 5xx error from the server, the SDK will automatically retry the request up to 3 times with a minimum wait time of 100 milliseconds between each attempt.

To customize this behavior, create an `openfga.RetryParams` struct and assign values to the `MaxRetry` and `MinWaitInMs` fields. `MaxRetry` determines the maximum number of retries (up to 15), while `MinWaitInMs` sets the minimum wait time between retries in milliseconds.

Apply your custom retry values by passing this struct to the `ClientConfiguration` struct's `RetryParams` parameter.

```golang
import (
	"os"

	openfga "github.com/openfga/go-sdk"
	. "github.com/openfga/go-sdk/client"
)

func main() {
	fgaClient, err := NewSdkClient(&ClientConfiguration{
		ApiUrl:               os.Getenv("FGA_API_URL"),                // required, e.g. https://api.fga.example
		StoreId:              os.Getenv("FGA_STORE_ID"),               // not needed when calling `CreateStore` or `ListStores`
		AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // optional, recommended to be set for production
		RetryParams: &openfga.RetryParams{
			MaxRetry:    3,   // retry up to 3 times on API requests
			MinWaitInMs: 250, // wait a minimum of 250 milliseconds between requests
		},
	})

	if err != nil {
		// .. Handle error
	}
}
```


### API Endpoints

Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
*OpenFgaApi* | [**BatchCheck**](docs/OpenFgaApi.md#batchcheck) | **Post** /stores/{store_id}/batch-check | Send a list of &#x60;check&#x60; operations in a single request
*OpenFgaApi* | [**Check**](docs/OpenFgaApi.md#check) | **Post** /stores/{store_id}/check | Check whether a user is authorized to access an object
*OpenFgaApi* | [**CreateStore**](docs/OpenFgaApi.md#createstore) | **Post** /stores | Create a store
*OpenFgaApi* | [**DeleteStore**](docs/OpenFgaApi.md#deletestore) | **Delete** /stores/{store_id} | Delete a store
*OpenFgaApi* | [**Expand**](docs/OpenFgaApi.md#expand) | **Post** /stores/{store_id}/expand | Expand all relationships in userset tree format, and following userset rewrite rules.  Useful to reason about and debug a certain relationship
*OpenFgaApi* | [**GetStore**](docs/OpenFgaApi.md#getstore) | **Get** /stores/{store_id} | Get a store
*OpenFgaApi* | [**ListObjects**](docs/OpenFgaApi.md#listobjects) | **Post** /stores/{store_id}/list-objects | List all objects of the given type that the user has a relation with
*OpenFgaApi* | [**ListStores**](docs/OpenFgaApi.md#liststores) | **Get** /stores | List all stores
*OpenFgaApi* | [**ListUsers**](docs/OpenFgaApi.md#listusers) | **Post** /stores/{store_id}/list-users | List the users matching the provided filter who have a certain relation to a particular type.
*OpenFgaApi* | [**Read**](docs/OpenFgaApi.md#read) | **Post** /stores/{store_id}/read | Get tuples from the store that matches a query, without following userset rewrite rules
*OpenFgaApi* | [**ReadAssertions**](docs/OpenFgaApi.md#readassertions) | **Get** /stores/{store_id}/assertions/{authorization_model_id} | Read assertions for an authorization model ID
*OpenFgaApi* | [**ReadAuthorizationModel**](docs/OpenFgaApi.md#readauthorizationmodel) | **Get** /stores/{store_id}/authorization-models/{id} | Return a particular version of an authorization model
*OpenFgaApi* | [**ReadAuthorizationModels**](docs/OpenFgaApi.md#readauthorizationmodels) | **Get** /stores/{store_id}/authorization-models | Return all the authorization models for a particular store
*OpenFgaApi* | [**ReadChanges**](docs/OpenFgaApi.md#readchanges) | **Get** /stores/{store_id}/changes | Return a list of all the tuple changes
*OpenFgaApi* | [**StreamedListObjects**](docs/OpenFgaApi.md#streamedlistobjects) | **Post** /stores/{store_id}/streamed-list-objects | Stream all objects of the given type that the user has a relation with
*OpenFgaApi* | [**Write**](docs/OpenFgaApi.md#write) | **Post** /stores/{store_id}/write | Add or delete tuples from the store
*OpenFgaApi* | [**WriteAssertions**](docs/OpenFgaApi.md#writeassertions) | **Put** /stores/{store_id}/assertions/{authorization_model_id} | Upsert assertions for an authorization model ID
*OpenFgaApi* | [**WriteAuthorizationModel**](docs/OpenFgaApi.md#writeauthorizationmodel) | **Post** /stores/{store_id}/authorization-models | Create a new authorization model


### Models

 - [AbortedMessageResponse](docs/AbortedMessageResponse.md)
 - [Any](docs/Any.md)
 - [Assertion](docs/Assertion.md)
 - [AssertionTupleKey](docs/AssertionTupleKey.md)
 - [AuthErrorCode](docs/AuthErrorCode.md)
 - [AuthorizationModel](docs/AuthorizationModel.md)
 - [BatchCheckItem](docs/BatchCheckItem.md)
 - [BatchCheckRequest](docs/BatchCheckRequest.md)
 - [BatchCheckResponse](docs/BatchCheckResponse.md)
 - [BatchCheckSingleResult](docs/BatchCheckSingleResult.md)
 - [CheckError](docs/CheckError.md)
 - [CheckRequest](docs/CheckRequest.md)
 - [CheckRequestTupleKey](docs/CheckRequestTupleKey.md)
 - [CheckResponse](docs/CheckResponse.md)
 - [Computed](docs/Computed.md)
 - [Condition](docs/Condition.md)
 - [ConditionMetadata](docs/ConditionMetadata.md)
 - [ConditionParamTypeRef](docs/ConditionParamTypeRef.md)
 - [ConsistencyPreference](docs/ConsistencyPreference.md)
 - [ContextualTupleKeys](docs/ContextualTupleKeys.md)
 - [CreateStoreRequest](docs/CreateStoreRequest.md)
 - [CreateStoreResponse](docs/CreateStoreResponse.md)
 - [Difference](docs/Difference.md)
 - [ErrorCode](docs/ErrorCode.md)
 - [ExpandRequest](docs/ExpandRequest.md)
 - [ExpandRequestTupleKey](docs/ExpandRequestTupleKey.md)
 - [ExpandResponse](docs/ExpandResponse.md)
 - [FgaObject](docs/FgaObject.md)
 - [ForbiddenResponse](docs/ForbiddenResponse.md)
 - [GetStoreResponse](docs/GetStoreResponse.md)
 - [InternalErrorCode](docs/InternalErrorCode.md)
 - [InternalErrorMessageResponse](docs/InternalErrorMessageResponse.md)
 - [Leaf](docs/Leaf.md)
 - [ListObjectsRequest](docs/ListObjectsRequest.md)
 - [ListObjectsResponse](docs/ListObjectsResponse.md)
 - [ListStoresResponse](docs/ListStoresResponse.md)
 - [ListUsersRequest](docs/ListUsersRequest.md)
 - [ListUsersResponse](docs/ListUsersResponse.md)
 - [Metadata](docs/Metadata.md)
 - [Node](docs/Node.md)
 - [Nodes](docs/Nodes.md)
 - [NotFoundErrorCode](docs/NotFoundErrorCode.md)
 - [NullValue](docs/NullValue.md)
 - [ObjectRelation](docs/ObjectRelation.md)
 - [PathUnknownErrorMessageResponse](docs/PathUnknownErrorMessageResponse.md)
 - [ReadAssertionsResponse](docs/ReadAssertionsResponse.md)
 - [ReadAuthorizationModelResponse](docs/ReadAuthorizationModelResponse.md)
 - [ReadAuthorizationModelsResponse](docs/ReadAuthorizationModelsResponse.md)
 - [ReadChangesResponse](docs/ReadChangesResponse.md)
 - [ReadRequest](docs/ReadRequest.md)
 - [ReadRequestTupleKey](docs/ReadRequestTupleKey.md)
 - [ReadResponse](docs/ReadResponse.md)
 - [RelationMetadata](docs/RelationMetadata.md)
 - [RelationReference](docs/RelationReference.md)
 - [RelationshipCondition](docs/RelationshipCondition.md)
 - [SourceInfo](docs/SourceInfo.md)
 - [Status](docs/Status.md)
 - [Store](docs/Store.md)
 - [StreamResultOfStreamedListObjectsResponse](docs/StreamResultOfStreamedListObjectsResponse.md)
 - [StreamedListObjectsResponse](docs/StreamedListObjectsResponse.md)
 - [Tuple](docs/Tuple.md)
 - [TupleChange](docs/TupleChange.md)
 - [TupleKey](docs/TupleKey.md)
 - [TupleKeyWithoutCondition](docs/TupleKeyWithoutCondition.md)
 - [TupleOperation](docs/TupleOperation.md)
 - [TupleToUserset](docs/TupleToUserset.md)
 - [TypeDefinition](docs/TypeDefinition.md)
 - [TypeName](docs/TypeName.md)
 - [TypedWildcard](docs/TypedWildcard.md)
 - [UnauthenticatedResponse](docs/UnauthenticatedResponse.md)
 - [UnprocessableContentErrorCode](docs/UnprocessableContentErrorCode.md)
 - [UnprocessableContentMessageResponse](docs/UnprocessableContentMessageResponse.md)
 - [User](docs/User.md)
 - [UserTypeFilter](docs/UserTypeFilter.md)
 - [Users](docs/Users.md)
 - [Userset](docs/Userset.md)
 - [UsersetTree](docs/UsersetTree.md)
 - [UsersetTreeDifference](docs/UsersetTreeDifference.md)
 - [UsersetTreeTupleToUserset](docs/UsersetTreeTupleToUserset.md)
 - [UsersetUser](docs/UsersetUser.md)
 - [Usersets](docs/Usersets.md)
 - [ValidationErrorMessageResponse](docs/ValidationErrorMessageResponse.md)
 - [WriteAssertionsRequest](docs/WriteAssertionsRequest.md)
 - [WriteAuthorizationModelRequest](docs/WriteAuthorizationModelRequest.md)
 - [WriteAuthorizationModelResponse](docs/WriteAuthorizationModelResponse.md)
 - [WriteRequest](docs/WriteRequest.md)
 - [WriteRequestDeletes](docs/WriteRequestDeletes.md)
 - [WriteRequestWrites](docs/WriteRequestWrites.md)


### OpenTelemetry

This SDK supports producing metrics that can be consumed as part of an [OpenTelemetry](https://opentelemetry.io/) setup. For more information, please see [the documentation](https://github.com/openfga/go-sdk/blob/main/docs/OpenTelemetry.md)

## Contributing

See [CONTRIBUTING](./CONTRIBUTING.md) for details.

## Author

[OpenFGA](https://github.com/openfga)

## License

This project is licensed under the Apache-2.0 license. See the [LICENSE](https://github.com/openfga/go-sdk/blob/main/LICENSE) file for more info.

The code in this repo was auto generated by [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator) from a template based on the [go template](https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator/src/main/resources/go), licensed under the [Apache License 2.0](https://github.com/OpenAPITools/openapi-generator/blob/master/LICENSE).

This repo bundles some code from the [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) package. You can find the code [here](./oauth2) and corresponding [BSD-3 License](./oauth2/LICENSE).
