const { CredentialsMethod, OpenFgaClient, ConsistencyPreference, ClientWriteRequestOnDuplicateWrites, ClientWriteRequestOnMissingDeletes } = require('@openfga/sdk');
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // https://api.us1.fga.dev, or your store's eu1/au1 region
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: {
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER, // auth.fga.dev
apiAudience: process.env.FGA_API_AUDIENCE, // URL for your store's region
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
},
});
async function main() {
const body = {
"writes": [
{
"user": "user:anne",
"relation": "reader",
"object": "document:budget"
}
]
};
const options = {
};
const response = await fgaClient.write(body, options);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});package main
import (
"context"
"os"
. "github.com/openfga/go-sdk/client"
"github.com/openfga/go-sdk/credentials"
)
// Configure FGA_API_URL and FGA_API_AUDIENCE for your store's us1/eu1/au1 region.
// Set FGA_STORE_ID, FGA_MODEL_ID, FGA_API_TOKEN_ISSUER, FGA_CLIENT_ID, and FGA_CLIENT_SECRET.
func main() {
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"),
StoreId: os.Getenv("FGA_STORE_ID"),
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"),
Credentials: &credentials.Credentials{
Method: credentials.CredentialsMethodClientCredentials,
Config: &credentials.Config{
ClientCredentialsApiTokenIssuer: os.Getenv("FGA_API_TOKEN_ISSUER"),
ClientCredentialsApiAudience: os.Getenv("FGA_API_AUDIENCE"),
ClientCredentialsClientId: os.Getenv("FGA_CLIENT_ID"),
ClientCredentialsClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
},
},
})
if err != nil { panic(err) }
body := ClientWriteRequest{
Writes: []ClientTupleKey{
ClientTupleKey{
User: "user:anne",
Relation: "reader",
Object: "document:budget",
},
},
}
options := ClientWriteOptions{
}
data, err := fgaClient.Write(context.Background()).Body(body).Options(options).Execute()
if err != nil {
panic(err)
}
_ = data
}using System.Collections.Generic;
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Configuration;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
// FGA_API_URL and FGA_API_AUDIENCE must match the store's us1/eu1/au1 region.
var fgaClient = new OpenFgaClient(new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"),
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"),
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"),
Credentials = new Credentials() {
Method = CredentialsMethod.ClientCredentials,
Config = new CredentialsConfig() {
ApiTokenIssuer = Environment.GetEnvironmentVariable("FGA_API_TOKEN_ISSUER"),
ApiAudience = Environment.GetEnvironmentVariable("FGA_API_AUDIENCE"),
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
}
}
});
var body = new ClientWriteRequest {
Writes = new List<ClientTupleKey> {
new ClientTupleKey {
User = "user:anne",
Relation = "reader",
Object = "document:budget",
},
},
};
var options = new ClientWriteOptions {
};
var response = await fgaClient.Write(body, options);import asyncio
import os
from openfga_sdk.client import OpenFgaClient, ClientConfiguration
from openfga_sdk.client.models import ClientWriteRequest, ClientTuple
from openfga_sdk.models import RelationshipCondition
from openfga_sdk.client.models import ConflictOptions, ClientWriteRequestOnDuplicateWrites, ClientWriteRequestOnMissingDeletes
from openfga_sdk.credentials import Credentials, CredentialConfiguration
# FGA_API_URL and FGA_API_AUDIENCE must match the store's us1/eu1/au1 region.
credentials = Credentials(
method='client_credentials',
configuration=CredentialConfiguration(
api_issuer=os.environ.get('FGA_API_TOKEN_ISSUER'),
api_audience=os.environ.get('FGA_API_AUDIENCE'),
client_id=os.environ.get('FGA_CLIENT_ID'),
client_secret=os.environ.get('FGA_CLIENT_SECRET'),
),
)
async def main():
configuration = ClientConfiguration(
api_url=os.environ.get('FGA_API_URL'),
store_id=os.environ.get('FGA_STORE_ID'),
authorization_model_id=os.environ.get('FGA_MODEL_ID'),
credentials=credentials,
)
async with OpenFgaClient(configuration) as fga_client:
body = ClientWriteRequest(
writes=[
ClientTuple(
user="user:anne",
relation="reader",
object="document:budget",
)
],
)
options = {
}
response = await fga_client.write(body, options)
asyncio.run(main())import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.ClientCredentials;
import dev.openfga.sdk.api.configuration.Credentials;
import dev.openfga.sdk.api.configuration.ClientCheckOptions;
import dev.openfga.sdk.api.configuration.ClientBatchCheckOptions;
import dev.openfga.sdk.api.configuration.ClientWriteOptions;
import dev.openfga.sdk.api.configuration.ClientListObjectsOptions;
import dev.openfga.sdk.api.configuration.ClientListUsersOptions;
import dev.openfga.sdk.api.client.model.*;
import dev.openfga.sdk.api.model.*;
import java.util.List;
import java.util.Map;
// FGA_API_URL and FGA_API_AUDIENCE must match the store's us1/eu1/au1 region.
public class Example {
public static void main(String[] args) throws Exception {
var config = new ClientConfiguration()
.apiUrl(System.getenv("FGA_API_URL"))
.storeId(System.getenv("FGA_STORE_ID"))
.authorizationModelId(System.getenv("FGA_MODEL_ID"))
.credentials(new Credentials(new ClientCredentials()
.apiTokenIssuer(System.getenv("FGA_API_TOKEN_ISSUER"))
.apiAudience(System.getenv("FGA_API_AUDIENCE"))
.clientId(System.getenv("FGA_CLIENT_ID"))
.clientSecret(System.getenv("FGA_CLIENT_SECRET"))));
var fgaClient = new OpenFgaClient(config);
var body = new ClientWriteRequest()
.writes(java.util.Arrays.asList(new ClientTupleKey()
.user("user:anne")
.relation("reader")
._object("document:budget")));
var options = new ClientWriteOptions();
var response = fgaClient.write(body, options).get();
}
}# 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.
curl -X POST "$FGA_API_URL/stores/$FGA_STORE_ID/write" \
-H "authorization: Bearer $FGA_BEARER_TOKEN" \
-H "content-type: application/json" \
-d '{
"writes": {
"tuple_keys": [
{
"user": "user:anne",
"relation": "reader",
"object": "document:budget"
}
]
}
}'{}{
"code": "validation_error",
"message": "Generic validation error"
}{
"code": "unauthorized_principal",
"message": "Missing permissions to perform operation"
}{
"code": "unauthorized_principal",
"message": "Missing permissions to perform operation"
}{
"code": "undefined_endpoint",
"message": "Endpoint not enabled"
}{
"code": "10",
"message": "transaction conflict"
}{
"code": "throttled_timeout_error",
"message": "timeout due to throttling on complex request"
}{
"code": "rate_limit_exceeded",
"message": "Rate Limit exceeded"
}{
"code": "internal_error",
"message": "Internal Server Error"
}Add or delete tuples from the store
The Write API will transactionally update the tuples for a certain store. Tuples and type definitions allow Auth0 FGA to determine whether a relationship exists between an object and an user.
In the body, writes adds new tuples and deletes removes existing tuples. When deleting a tuple, any condition specified with it is ignored.
The API is not idempotent by default: if, later on, you try to add the same tuple key (even if the condition is different), or if you try to delete a non-existing tuple, it will throw an error.
To allow writes when an identical tuple already exists in the database, set "on_duplicate": "ignore" on the writes object.
To allow deletes when a tuple was already removed from the database, set "on_missing": "ignore" on the deletes object.
If a Write request contains both idempotent (ignore) and non-idempotent (error) operations, the most restrictive action (error) will take precedence. If a condition fails for a sub-request with an error flag, the entire transaction will be rolled back. This gives developers explicit control over the atomicity of the requests.
The API will not allow you to write tuples such as document:2021-budget#viewer@document:2021-budget#viewer, because they are implicit.
An authorization_model_id may be specified in the body. If it is, it will be used to assert that each written tuple (not deleted) is valid for the model specified. If it is not specified, the latest authorization model ID will be used.
- Each Write API call allows at most 40 tuples.
Example
Adding relationships
To add user:anne as a writer for document:2021-budget, call Write API with the following
{
"writes": {
"tuple_keys": [
{
"user": "user:anne",
"relation": "writer",
"object": "document:2021-budget"
}
],
"on_duplicate": "ignore"
},
"authorization_model_id": "01G50QVV17PECNVAHX1GG4Y5NC"
}
Removing relationships
To remove user:bob as a reader for document:2021-budget, call Write API with the following
{
"deletes": {
"tuple_keys": [
{
"user": "user:bob",
"relation": "reader",
"object": "document:2021-budget"
}
],
"on_missing": "ignore"
}
}
const { CredentialsMethod, OpenFgaClient, ConsistencyPreference, ClientWriteRequestOnDuplicateWrites, ClientWriteRequestOnMissingDeletes } = require('@openfga/sdk');
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // https://api.us1.fga.dev, or your store's eu1/au1 region
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: {
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER, // auth.fga.dev
apiAudience: process.env.FGA_API_AUDIENCE, // URL for your store's region
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
},
});
async function main() {
const body = {
"writes": [
{
"user": "user:anne",
"relation": "reader",
"object": "document:budget"
}
]
};
const options = {
};
const response = await fgaClient.write(body, options);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});package main
import (
"context"
"os"
. "github.com/openfga/go-sdk/client"
"github.com/openfga/go-sdk/credentials"
)
// Configure FGA_API_URL and FGA_API_AUDIENCE for your store's us1/eu1/au1 region.
// Set FGA_STORE_ID, FGA_MODEL_ID, FGA_API_TOKEN_ISSUER, FGA_CLIENT_ID, and FGA_CLIENT_SECRET.
func main() {
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"),
StoreId: os.Getenv("FGA_STORE_ID"),
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"),
Credentials: &credentials.Credentials{
Method: credentials.CredentialsMethodClientCredentials,
Config: &credentials.Config{
ClientCredentialsApiTokenIssuer: os.Getenv("FGA_API_TOKEN_ISSUER"),
ClientCredentialsApiAudience: os.Getenv("FGA_API_AUDIENCE"),
ClientCredentialsClientId: os.Getenv("FGA_CLIENT_ID"),
ClientCredentialsClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
},
},
})
if err != nil { panic(err) }
body := ClientWriteRequest{
Writes: []ClientTupleKey{
ClientTupleKey{
User: "user:anne",
Relation: "reader",
Object: "document:budget",
},
},
}
options := ClientWriteOptions{
}
data, err := fgaClient.Write(context.Background()).Body(body).Options(options).Execute()
if err != nil {
panic(err)
}
_ = data
}using System.Collections.Generic;
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Configuration;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
// FGA_API_URL and FGA_API_AUDIENCE must match the store's us1/eu1/au1 region.
var fgaClient = new OpenFgaClient(new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"),
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"),
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"),
Credentials = new Credentials() {
Method = CredentialsMethod.ClientCredentials,
Config = new CredentialsConfig() {
ApiTokenIssuer = Environment.GetEnvironmentVariable("FGA_API_TOKEN_ISSUER"),
ApiAudience = Environment.GetEnvironmentVariable("FGA_API_AUDIENCE"),
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
}
}
});
var body = new ClientWriteRequest {
Writes = new List<ClientTupleKey> {
new ClientTupleKey {
User = "user:anne",
Relation = "reader",
Object = "document:budget",
},
},
};
var options = new ClientWriteOptions {
};
var response = await fgaClient.Write(body, options);import asyncio
import os
from openfga_sdk.client import OpenFgaClient, ClientConfiguration
from openfga_sdk.client.models import ClientWriteRequest, ClientTuple
from openfga_sdk.models import RelationshipCondition
from openfga_sdk.client.models import ConflictOptions, ClientWriteRequestOnDuplicateWrites, ClientWriteRequestOnMissingDeletes
from openfga_sdk.credentials import Credentials, CredentialConfiguration
# FGA_API_URL and FGA_API_AUDIENCE must match the store's us1/eu1/au1 region.
credentials = Credentials(
method='client_credentials',
configuration=CredentialConfiguration(
api_issuer=os.environ.get('FGA_API_TOKEN_ISSUER'),
api_audience=os.environ.get('FGA_API_AUDIENCE'),
client_id=os.environ.get('FGA_CLIENT_ID'),
client_secret=os.environ.get('FGA_CLIENT_SECRET'),
),
)
async def main():
configuration = ClientConfiguration(
api_url=os.environ.get('FGA_API_URL'),
store_id=os.environ.get('FGA_STORE_ID'),
authorization_model_id=os.environ.get('FGA_MODEL_ID'),
credentials=credentials,
)
async with OpenFgaClient(configuration) as fga_client:
body = ClientWriteRequest(
writes=[
ClientTuple(
user="user:anne",
relation="reader",
object="document:budget",
)
],
)
options = {
}
response = await fga_client.write(body, options)
asyncio.run(main())import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.ClientCredentials;
import dev.openfga.sdk.api.configuration.Credentials;
import dev.openfga.sdk.api.configuration.ClientCheckOptions;
import dev.openfga.sdk.api.configuration.ClientBatchCheckOptions;
import dev.openfga.sdk.api.configuration.ClientWriteOptions;
import dev.openfga.sdk.api.configuration.ClientListObjectsOptions;
import dev.openfga.sdk.api.configuration.ClientListUsersOptions;
import dev.openfga.sdk.api.client.model.*;
import dev.openfga.sdk.api.model.*;
import java.util.List;
import java.util.Map;
// FGA_API_URL and FGA_API_AUDIENCE must match the store's us1/eu1/au1 region.
public class Example {
public static void main(String[] args) throws Exception {
var config = new ClientConfiguration()
.apiUrl(System.getenv("FGA_API_URL"))
.storeId(System.getenv("FGA_STORE_ID"))
.authorizationModelId(System.getenv("FGA_MODEL_ID"))
.credentials(new Credentials(new ClientCredentials()
.apiTokenIssuer(System.getenv("FGA_API_TOKEN_ISSUER"))
.apiAudience(System.getenv("FGA_API_AUDIENCE"))
.clientId(System.getenv("FGA_CLIENT_ID"))
.clientSecret(System.getenv("FGA_CLIENT_SECRET"))));
var fgaClient = new OpenFgaClient(config);
var body = new ClientWriteRequest()
.writes(java.util.Arrays.asList(new ClientTupleKey()
.user("user:anne")
.relation("reader")
._object("document:budget")));
var options = new ClientWriteOptions();
var response = fgaClient.write(body, options).get();
}
}# 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.
curl -X POST "$FGA_API_URL/stores/$FGA_STORE_ID/write" \
-H "authorization: Bearer $FGA_BEARER_TOKEN" \
-H "content-type: application/json" \
-d '{
"writes": {
"tuple_keys": [
{
"user": "user:anne",
"relation": "reader",
"object": "document:budget"
}
]
}
}'{}{
"code": "validation_error",
"message": "Generic validation error"
}{
"code": "unauthorized_principal",
"message": "Missing permissions to perform operation"
}{
"code": "unauthorized_principal",
"message": "Missing permissions to perform operation"
}{
"code": "undefined_endpoint",
"message": "Endpoint not enabled"
}{
"code": "10",
"message": "transaction conflict"
}{
"code": "throttled_timeout_error",
"message": "timeout due to throttling on complex request"
}{
"code": "rate_limit_exceeded",
"message": "Rate Limit exceeded"
}{
"code": "internal_error",
"message": "Internal Server Error"
}Authorizations
The access token received from the authorization server in the OAuth 2.0 flow.
Path Parameters
Body
Response
A successful response.
The response is of type object.