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

# Sidecar

## Overview

The Sidecar SDK is used to interact with the Stigg Sidecar Service, which provides low latency entitlement checks, handles caching, and subscribes to real-time entitlement and usage data updates.

<Note>
  **Node.js applications** do not require the Sidecar. The [Node.js SDK](https://node-sdk-docs.stigg.io/classes/stigg) provides full feature parity with the Sidecar, including low-latency entitlement checks, caching, and real-time updates.
</Note>

<Tip>
  [Learn more](/documentation/high-availability-and-scale/sidecar/overview) about the Stigg Sidecar service.
</Tip>

<Warning>
  Since Sidecar SDK version `3.0.0`, the TLS connection method has been deprecated in favor of non-TLS. Please ensure you are running a compatible Sidecar image version `2.494.0` or later. For backwards compatibility details, check the [changelog](https://docs.stigg.io/api-and-sdks/changelog/backend-graphql/sidecar#3-0-0). TLS self-signed certificates will **expire on January 26, 2026**, so upgrading the Sidecar image is strongly recommended.
</Warning>

## Installing the SDK

The first step is to install the Stigg SDK as a dependency in your application:

<CodeGroup>
  ```shell Python theme={null}
  $ pip install stigg-sidecar-sdk
  ```

  ```shell Ruby theme={null}
  bundle add stigg-sidecar-sdk
  ```

  ```go Go theme={null}
  go get github.com/stiggio/sidecar-sdk-go/v3
  ```

  ```groovy Java (Gradle) theme={null}
  repositories {
    mavenCentral()
  }

  dependencies {
    implementation 'io.stigg:stigg-sidecar-sdk:+'
    implementation "com.google.protobuf:protobuf-java:3.25.0"
    implementation "io.grpc:grpc-protobuf:1.59.0"
    implementation "io.grpc:grpc-stub:1.59.0"
    runtimeOnly "io.grpc:grpc-netty-shaded:1.59.0"

    // API client dependencies
    implementation "io.stigg:stigg-api-client:+"
    implementation "com.apollographql.apollo3:apollo-runtime:3.8.2"
    implementation "com.apollographql.apollo3:apollo-api-jvm:3.8.2"
    implementation "com.apollographql.apollo3:apollo-adapters-jvm:3.8.2"
  }
  ```

  ```xml Java (Maven) theme={null}
  <dependencies>
  	<dependency>
  		<groupId>io.stigg</groupId>
  		<artifactId>stigg-sidecar-sdk</artifactId>
  		<version>LATEST</version>
  	</dependency>
  	<dependency>
  		<groupId>com.google.protobuf</groupId>
  		<artifactId>protobuf-java</artifactId>
  		<version>3.25.0</version>
  	</dependency>
  	<dependency>
  		<groupId>io.grpc</groupId>
  		<artifactId>grpc-protobuf</artifactId>
  		<version>1.59.0</version>
  	</dependency>
  	<dependency>
  		<groupId>io.grpc</groupId>
  		<artifactId>grpc-stub</artifactId>
  		<version>1.59.0</version>
  	</dependency>
  	<dependency>
  		<groupId>io.grpc</groupId>
  		<artifactId>grpc-netty-shaded</artifactId>
  		<version>1.59.0</version>
      <scope>runtime</scope>
  	</dependency>
    
    // API client dependencies
  	<dependency>
  		<groupId>io.stigg</groupId>
  		<artifactId>stigg-api-client</artifactId>
  		<version>LATEST</version>
  	</dependency>
  	<dependency>
  		<groupId>com.apollographql.apollo3</groupId>
  		<artifactId>apollo-runtime</artifactId>
  		<version>3.8.2</version>
  	</dependency>
  	<dependency>
  		<groupId>com.apollographql.apollo3</groupId>
  		<artifactId>apollo-api-jvm</artifactId>
  		<version>3.8.2</version>
  	</dependency>
  	<dependency>
  		<groupId>com.apollographql.apollo3</groupId>
  		<artifactId>apollo-adapters-jvm</artifactId>
  		<version>3.8.2</version>
  	</dependency>
  </dependencies>
  ```
</CodeGroup>

## Retrieving the full access key

In the Stigg app, go to **Integrations > API keys**.

Copy the **Full access key** of the relevant environment.

## Initializing the SDK

### Development

For development, first run the Sidecar service using Docker. Run the service with the latest version in the [ECR Public Gallery](https://gallery.ecr.aws/stigg/sidecar):

```shell theme={null}
docker run -it -p 80:80 \ 
  -e SERVER_API_KEY="<SERVER_API_KEY>" \
  public.ecr.aws/stigg/sidecar:latest
```

Once the Sidecar service is running, you can initialize the SDK with the remote sidecar host and port parameters.

### Production

For production use, its recommended to deploy the Sidecar service using the sidecar pattern, or as a standalone service.

You set the remote sidecar host and port parameters as part of the SDK initialization:

<CodeGroup>
  ```python Python theme={null}
  from stigg_sidecar_sdk import Stigg, ApiConfig

  stigg = Stigg(
    	# To access the API you need to set the server AP key
      ApiConfig(
  	    api_key="<FULL_ACCESS_KEY>",
      ),
      # For production use, set remote sidecar host and port:
      remote_sidecar_host='localhost',
      remote_sidecar_port=80
  )
  ```

  ```ruby Ruby theme={null}
  require("stigg_sidecar_sdk")

  client = Stigg::Sidecar.create_client(Stigg::Sidecar::ApiConfig.new("<SERVER-API-KEY>"),
                                        remote_sidecar_host: "localhost",
                                        remote_sidecar_port: 80)
  ```

  ```go Go theme={null}
  package main 

  import (
  	"fmt"
  	sidecar "github.com/stiggio/sidecar-sdk-go/v2"
    sidecarv1 "github.com/stiggio/sidecar-sdk-go/v2/generated/stigg/sidecar/v1"
  )

  func main() {
  	host := "localhost"
  	port := 80
  	client, err := sidecar.NewSidecarClient(sidecar.ApiClientConfig{ApiKey: "<SERVER-API-KEY>"}, &host, &port)
  	if err != nil {
  		fmt.Printf("Failed to create SidecarClient: %v\n", err)
  		return
  	}
  }
  ```

  ```java Java theme={null}
  import io.stigg.sidecar.sdk.Stigg;
  import io.stigg.sidecar.sdk.StiggConfig;
  import io.stigg.sidecar.proto.v1.*;


  class App {
    public static void main(String[] ...args) {
      var stigg = Stigg.init(
        StiggConfig.builder()
        // To access the API you need to set the server AP key:
        .apiConfig(ApiConfig.newBuilder().setApiKey("<SERVER-API-KEY>").build())
        // For production use, set remote sidecar host and port:
        .remoteSidecarHost("localhost")
        .remoteSidecarPort(80)
        .build()
      );
    }
  }
  ```
</CodeGroup>

<Tip>
  [Learn more](/documentation/high-availability-and-scale/sidecar/overview) about running the Sidecar service as separate container.
</Tip>

### Running in offline or air-gapped environments

In air-gapped or highly secure environments where the Sidecar service cannot be deployed, you can use the Sidecar SDK in offline mode. This mode enables entitlement evaluation entirely in-memory without connecting to a remote or local Sidecar process. Offline mode is ideal for:

* Air-gapped deployments
* Environments with restricted network access
* Testing with predefined entitlement data

<Note>
  The Offline mode is only available in Java SDK (v2.420.0+). This mode supports all entitlement types (boolean, numeric, metered). Usage and event reporting (`reportUsage`, `reportEvents`) are disabled in the offline mode.
</Note>

You can initialize the SDK with `OfflineStiggConfig`, supplying a customer-to-entitlements mapping either programmatically or via a JSON string.

#### Initializing with programmatic config

You can pass static entitlement data directly as Java objects:

```java theme={null}
var stigg = Stigg.init(OfflineStiggConfig.builder()
    .entitlements(OfflineEntitlements.builder()
        .customers(Map.of(
            "customer-demo-01",
            CustomerEntitlements.builder()
                .entitlements(Map.of(
                    "feature-example-1", Entitlement.builder().type(EntitlementType.BOOLEAN).build(),
                    "feature-example-2", Entitlement.builder().type(EntitlementType.NUMERIC).value(50.0).build(),
                    "feature-example-3", Entitlement.builder().type(EntitlementType.METERED).usageLimit(100000.0).isUnlimited(false).build()
                ))
                .build()
        ))
        .build())
    .build());
```

#### Initializing from a JSON string

You can also load entitlements from a JSON string:

```java theme={null}
var stigg = Stigg.init(OfflineStiggConfig.builder()
    .entitlements(OfflineEntitlements.fromJson("{ /* JSON string */ }"))
    .build());
```

Example JSON:

```json theme={null}
{
  "customers": {
    "customer-demo-01": {
      "entitlements": {
        "feature-example-1": { "type": "BOOLEAN" },
        "feature-example-2": { "type": "NUMERIC", "value": 1000, "isUnlimited": false },
        "feature-example-3": { "type": "METERED", "isUnlimited": true }
      }
    }
  }
}
```

#### Checking entitlements

All entitlement methods - `getXEntitlement` and `getEntitlements` - remain the same:

```java theme={null}
var result = stigg.getBooleanEntitlement(GetBooleanEntitlementRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setFeatureId("feature-example-1)
    .build());

if (result.hasAccess()) {
    // Feature is enabled
}
```

## Getting the entitlement of a customer to a specific feature

### Generic entitlement check (recommended)

The `getEntitlement` method provides a unified way to check any type of feature entitlement. It automatically returns the appropriate entitlement type based on the feature configuration.

<CodeGroup>
  ```python Python theme={null}
  from stigg_sidecar_sdk import GetEntitlementRequest

  resp = await stigg.get_entitlement(GetEntitlementRequest(
    customer_id='customer-demo-01',
    feature_id='feature-example-1',
    resource_id='site-01' # Optional, specify the resource ID
  ))

  if resp.has_access:
    # Customer has access to the feature
    pass

  # Check the specific entitlement type
  if resp.boolean:
    print('Boolean feature enabled')
  elif resp.numeric:
    print(f'Numeric value: {resp.numeric.value}')
    print(f'Is unlimited: {resp.numeric.is_unlimited}')
  elif resp.metered:
    print(f'Usage limit: {resp.metered.usage_limit}')
    print(f'Current usage: {resp.metered.current_usage}')
    print(f'Is unlimited: {resp.metered.is_unlimited}')
  ```

  ```ruby Ruby theme={null}
  resp = client.get_entitlement(
        Stigg::Sidecar::V1::GetEntitlementRequest.new(customer_id: "customer-demo-01",
                                                      feature_id: "feature-example-1",
                                                      resource_id: "site-01") # Optional, specify the resource ID
  )

  if resp.has_access
    # Customer has access to the feature
  end

  # Check the specific entitlement type
  if resp.boolean
    puts "Boolean feature enabled"
  elsif resp.numeric
    puts "Numeric value: #{resp.numeric.value}"
  elsif resp.metered
    puts "Usage limit: #{resp.metered.usage_limit}"
    puts "Current usage: #{resp.metered.current_usage}"
    puts "Is unlimited: #{resp.metered.is_unlimited}"
  end
  ```

  ```go Go theme={null}
  req := sidecarv1.GetEntitlementRequest{
    CustomerId: "customer-demo-01",
    FeatureId:  "feature-example-1",
    ResourceId: "site-01", // Optional, specify the resource ID
  }

  resp, err := client.GetEntitlement(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to check entitlement: %v\n", err)
    return
  }

  if resp.HasAccess {
    // Customer has access to the feature
  }

  // Check the specific entitlement type
  if resp.GetBoolean() != nil {
    fmt.Printf("Boolean feature enabled\n")
  } else if resp.GetNumeric() != nil {
    fmt.Printf("Numeric value: %v\n", resp.GetNumeric().Value)
    fmt.Printf("Is unlimited: %v\n", resp.GetNumeric().IsUnlimited)
  } else if resp.GetMetered() != nil {
    fmt.Printf("Usage limit: %v\n", resp.GetMetered().UsageLimit)
    fmt.Printf("Current usage: %v\n", resp.GetMetered().CurrentUsage)
    fmt.Printf("Is unlimited: %v\n", resp.GetMetered().IsUnlimited)
  }
  ```

  ```java Java theme={null}
  var req =
    GetEntitlementRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setFeatureId("feature-example-1")
    .setResourceId("site-01") // Optional, specify the resource ID
    .build();

  var res = stigg.getEntitlement(req);

  if (res.getHasAccess()) {
    // Customer has access to the feature
  }

  switch (res.getEntitlementCase()){
    case NUMERIC:
      System.out.println("Numeric value: " + res.getNumeric().getValue());
      break;
    case METERED:
      System.out.println("Usage limit: " + res.getMetered().getUsageLimit());
      System.out.println("Current usage: " + res.getMetered().getCurrentUsage());
      System.out.println("Is unlimited: " + res.getMetered().getIsUnlimited());
      break;
    case BOOLEAN:
      System.out.println("Boolean feature enabled");
      break;
  }
  ```
</CodeGroup>

<Expandable title="Result - boolean feature">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": true,
      "isFallback": false,
      "accessDeniedReason": null,
      "boolean": {
        "feature": {
          "id": "feature-example-1",
          "featureType": "FEATURE_TYPE_BOOLEAN",
          "units": "",
          "unitsPlural": "",
          "meterType": "METER_TYPE_NONE",
          "isMetered": false
        }
      }
    }
    ```
  </CodeGroup>
</Expandable>

<Expandable title="Result - numeric feature">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": true,
      "isFallback": false,
      "accessDeniedReason": null,
      "numeric": {
        "feature": {
          "id": "feature-example-2",
          "featureType": "FEATURE_TYPE_NUMBER",
          "units": "request",
          "unitsPlural": "requests",
          "meterType": "METER_TYPE_NONE",
          "isMetered": false
        },
        "value": 100,
        "isUnlimited": false
      }
    }
    ```
  </CodeGroup>
</Expandable>

<Expandable title="Result - metered feature">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": true,
      "isFallback": false,
      "accessDeniedReason": null,
      "metered": {
        "feature": {
          "id": "feature-example-3",
          "featureType": "FEATURE_TYPE_NUMBER",
          "units": "request",
          "unitsPlural": "requests",
          "meterType": "METER_TYPE_INCREMENTAL",
          "isMetered": true
        },
        "usageLimit": 1000,
        "isUnlimited": false,
        "currentUsage": 250,
        "resetPeriod": "ENTITLEMENT_RESET_PERIOD_MONTH",
        "usagePeriodAnchor": "2023-07-21T00:00:00Z",
        "usagePeriodStart": "2023-11-21T00:00:00Z",
        "usagePeriodEnd": "2023-12-21T00:00:00Z"
      }
    }
    ```
  </CodeGroup>
</Expandable>

<Note>
  The generic `getEntitlement` method is the recommended approach for checking feature entitlements as it provides a unified interface and automatically handles all entitlement types. For metered features, you can also pass a `requestedUsage` parameter to proactively check if additional usage would be allowed.
</Note>

### Checking the entitlement of a boolean feature

<CodeGroup>
  ```python Python theme={null}
  from stigg_sidecar_sdk import GetBooleanEntitlementRequest

  resp = await stigg.get_boolean_entitlement(GetBooleanEntitlementRequest(
    customer_id='customer-demo-01',
    feature_id='feature-example-7',
    resource_id='site-01' # Optional, specify the resource ID
  ))

  if resp.has_access:   
    pass # Customer has access to the feature
  ```

  ```ruby Ruby theme={null}
  resp = client.get_boolean_entitlement(
        Stigg::Sidecar::V1::GetBooleanEntitlementRequest.new(customer_id: "customer-demo-01",
                                                             feature_id: "feature-example-7",
                                                             resource_id: "site-01") # Optional, specify the resource ID
  )

  if resp.has_access
    # Customer has access to the feature
  end
  ```

  ```go Go theme={null}
  req := sidecarv1.GetBooleanEntitlementRequest{
    CustomerId: "customer-demo-01",
    FeatureId:  "feature-example-7",
  }

  resp, err := client.GetBooleanEntitlement(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to check entitlement: %v\n", err)
    return
  }
  if resp.HasAccess {
    fmt.Printf("Customer has access to feature\n")
  }
  ```

  ```java Java theme={null}
  var req =
    GetBooleanEntitlementRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setFeatureId("feature-example-7")
    .setResourceId("site-01") // Optional, specify the resource ID
    .build();

  var res = stigg.getBooleanEntitlement(req);

  if (res.getHasAccess()) {
    // Customer has access to the feature
  }
  ```
</CodeGroup>

<Expandable title="Result - has access">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": true,
      "isFallback": false,
      "accessDeniedReason": null,
      "entitlement": {
        "feature": {
          "id": "feature-example-8",
          "featureType": "FEATURE_TYPE_BOOLEAN",
          "units": "",
          "unitsPlural": "",
          "meterType": "METER_TYPE_NONE",
          "isMetered": false
        }
      }
    }
    ```
  </CodeGroup>
</Expandable>

<Expandable title="Result - no access">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": true,
      "isFallback": false,
      "accessDeniedReason": null,
      "entitlement": {
        "feature": {
          "id": "feature-example-7",
          "featureType": "FEATURE_TYPE_BOOLEAN",
          "units": "",
          "unitsPlural": "",
          "meterType": "METER_TYPE_NONE",
          "isMetered": false
        }
      }
    }
    ```
  </CodeGroup>
</Expandable>

### Checking the entitlement of a numeric configuration feature

<CodeGroup>
  ```python Python theme={null}
  from stigg_sidecar_sdk import GetNumericEntitlementRequest

  resp = await stigg.get_numeric_entitlement(GetNumericEntitlementRequest(
    customer_id='customer-demo-01',
    feature_id='feature-example-5',
    resource_id='site-01' # Optional, resource ID
  ))

  if resp.has_access:   
    value = resp.entitlement.value # Entitlement value  
  ```

  ```ruby Ruby theme={null}
  resp = client.get_numeric_entitlement(
    Stigg::Sidecar::V1::GetNumericEntitlementRequest.new(
      customer_id: "customer-demo-01",
      feature_id: "feature-example-5",
      resource_id: "site-01") # Optional, specify the resource ID
  )

  if resp.has_access
    p resp.entitlement.value # entitlement value
  end
  ```

  ```go Go theme={null}
  req := sidecarv1.GetNumericEntitlementRequest{
    CustomerId: "customer-demo-01",
    FeatureId:  "feature-example-5",
  }

  resp, err := client.GetNumericEntitlement(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to check entitlement: %v\n", err)
    return
  }
  if resp.HasAccess {
    fmt.Printf("Customer has access to feature, value: %v\n", resp.Entitlement.Value)
  }
  ```

  ```java Java theme={null}
  var req =
    GetNumericEntitlementRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setFeatureId("feature-example-5")
    .setResourceId("site-01") // Optional, specify the resource ID
    .build();

  var res = stigg.getNumericEntitlement(req);

  if (res.getHasAccess()) {
    var value = res.getEntitlement().getValue(); // Entitlement value
  }
  ```
</CodeGroup>

<Expandable title="Result - has access">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": true,
      "isFallback": false,
      "accessDeniedReason": null,
      "entitlement": {
        "feature": {
          "id": "feature-example-5",
          "featureType": "FEATURE_TYPE_NUMBER",
          "units": "MB",
          "unitsPlural": "MB",
          "meterType": "METER_TYPE_NONE",
          "isMetered": false
        },
        "value": 100,
        "isUnlimited": false
      }
    }
    ```
  </CodeGroup>
</Expandable>

<Expandable title="Result - no access">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": false,
      "isFallback": false,
      "accessDeniedReason": "ACCESS_DENIED_REASON_NO_FEATURE_ENTITLEMENT_IN_SUBSCRIPTION",
      "entitlement": {
        "feature": null,
        "value": null,
        "isUnlimited": false
      }
    }
    ```
  </CodeGroup>
</Expandable>

### Checking if a customer has access to a metered feature

<CodeGroup>
  ```python Python theme={null}
  from stigg_sidecar_sdk import GetMeteredEntitlementRequest

  resp = await stigg.get_metered_entitlement(GetMeteredEntitlementRequest(
    customer_id='customer-demo-01',
    feature_id='feature-example-4',
    resource_id='site-01', # Optional, resource ID
  ))	

  if resp.has_access:   
    pass # Has access, current usage is under the entitlement limit
  ```

  ```ruby Ruby theme={null}
  resp = client.get_metered_entitlement(
    Stigg::Sidecar::V1::GetMeteredEntitlementRequest.new(
      customer_id: "customer-demo-01",
      feature_id: "feature-example-4",
      resource_id: "site-01" # Optional, specify the resource ID
    )
  )

  if resp.has_access
    p resp.entitlement.current_usage # current usage
  end
  ```

  ```go Go theme={null}
  req := sidecarv1.GetMeteredEntitlementRequest{
    CustomerId: "customer-demo-01",
    FeatureId:  "feature-example-4",
  }

  resp, err := client.GetMeteredEntitlement(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to check entitlement: %v\n", err)
    return
  }
  if resp.HasAccess {
    // Has access, current usage is under the entitlement limit
  }
  ```

  ```java Java theme={null}
  var req =
    GetMeteredEntitlementRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setFeatureId("feature-example-4")
    .setResourceId("site-01") // Optional, specify the resource ID
    .build();

  var resp = stigg.getMeteredEntitlement(req);

  if (resp.getHasAccess()){
    // Has access, current usage is under the entitlement limit
  }
  ```
</CodeGroup>

<Expandable title="Result - has access">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": true,
      "isFallback": false,
      "accessDeniedReason": null,
      "requestedUsage": 0.0,
      "entitlement": {
        "feature": {
          "id": "feature-example-4",
          "featureType": "FEATURE_TYPE_NUMBER",
          "units": "template",
          "unitsPlural": "templates",
          "meterType": "METER_TYPE_FLUCTUATING",
          "isMetered": true
        },
        "usageLimit": 6.0,
        "isUnlimited": false,
        "currentUsage": 3.0,
        "resetPeriod": "ENTITLEMENT_RESET_PERIOD_UNSPECIFIED",
        "usagePeriodAnchor": null,
        "usagePeriodStart": null,
        "usagePeriodEnd": null
      }
    }
    ```
  </CodeGroup>
</Expandable>

<Expandable title="Result - no access">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": false,
      "isFallback": false,
      "accessDeniedReason": "ACCESS_DENIED_REASON_NO_FEATURE_ENTITLEMENT_IN_SUBSCRIPTION",
      "requestedUsage": 1.0,
      "entitlement": {
        "feature": null,
        "usageLimit": null,
        "isUnlimited": false,
        "currentUsage": 0.0,
        "resetPeriod": "ENTITLEMENT_RESET_PERIOD_UNSPECIFIED",
        "usagePeriodAnchor": null,
        "usagePeriodStart": null,
        "usagePeriodEnd": null
      }
    }
    ```
  </CodeGroup>
</Expandable>

### Proactively checking if a customer will have access to X units of a metered feature

<CodeGroup>
  ```python Python theme={null}
  resp = await stigg.get_metered_entitlement(GetMeteredEntitlementRequest(
    customer_id='customer-demo-01',
    feature_id='feature-example-4',
    resource_id='site-01', # Optional, resource ID
    options=MeteredEntitlementOptions(requested_usage=10)
  ))

  if resp.has_access:   
    pass # Has access, (current + requested usage) is under the entitlement limit
  ```

  ```ruby Ruby theme={null}
  resp = client.get_metered_entitlement(
    Stigg::Sidecar::V1::GetMeteredEntitlementRequest.new(
      customer_id: "customer-demo-01",
      feature_id: "feature-example-4",
      resource_id: "site-01", # Optional, specify the resource ID
      options: Stigg::Sidecar::V1::MeteredEntitlementOptions.new(requested_usage: 10)
    )
  )

  if resp.has_access
    # Has access, (current + requested usage) is under the entitlement limit
  end
  ```

  ```go Go theme={null}
  requestedUsage := 10.0
  req := sidecarv1.GetMeteredEntitlementRequest{
    CustomerId: "customer-demo-01",
    FeatureId:  "feature-example-4",
    Options: &sidecarv1.MeteredEntitlementOptions{
      RequestedUsage: &requestedUsage,
    },
  }

  resp, err := client.GetMeteredEntitlement(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to check entitlement: %v\n", err)
    return
  }
  if resp.HasAccess {
    // Has access, current usage is under the entitlement limit
  }
  ```

  ```java Java theme={null}
  var req =
    GetMeteredEntitlementRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setFeatureId("feature-example-4")
    .setResourceId("site-01") // Optional, specify the resource ID
    .setOptions(MeteredEntitlementOptions.newBuilder()
                .setRequestedUsage(10)
                .build())
    .build();

  var resp = stigg.getMeteredEntitlement(req);

  if (resp.getHasAccess()){
    // Has access, (current + requested usage) is under the entitlement limit
  }
  ```
</CodeGroup>

<Expandable title="Result - has access">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": true,
      "isFallback": false,
      "accessDeniedReason": null,
      "requestedUsage": 1.0,
      "entitlement": {
        "feature": {
          "id": "feature-example-4",
          "featureType": "FEATURE_TYPE_NUMBER",
          "units": "template",
          "unitsPlural": "templates",
          "meterType": "METER_TYPE_FLUCTUATING",
          "isMetered": true
        },
        "usageLimit": 6.0,
        "isUnlimited": false,
        "currentUsage": 3.0,
        "resetPeriod": "ENTITLEMENT_RESET_PERIOD_UNSPECIFIED",
        "usagePeriodAnchor": null,
        "usagePeriodStart": null,
        "usagePeriodEnd": null
      }
    }
    ```
  </CodeGroup>
</Expandable>

<Expandable title="Result - no access">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "hasAccess": false,
      "isFallback": false,
      "accessDeniedReason": "ACCESS_DENIED_REASON_REQUESTED_USAGE_EXCEEDING_LIMIT",
      "requestedUsage": 10.0,
      "entitlement": {
        "feature": {
          "id": "feature-example-4",
          "featureType": "FEATURE_TYPE_NUMBER",
          "units": "template",
          "unitsPlural": "templates",
          "meterType": "METER_TYPE_FLUCTUATING",
          "isMetered": true
        },
        "usageLimit": 6.0,
        "isUnlimited": false,
        "currentUsage": 3.0,
        "resetPeriod": "ENTITLEMENT_RESET_PERIOD_UNSPECIFIED",
        "usagePeriodAnchor": null,
        "usagePeriodStart": null,
        "usagePeriodEnd": null
      }
    }
    ```
  </CodeGroup>
</Expandable>

# Getting all of the entitlements for a specific customer

<CodeGroup>
  ```python Python theme={null}
  from stigg_sidecar_sdk import GetEntitlementsRequest

  resp = await stigg.get_entitlements(GetEntitlementsRequest(
    customer_id='customer-demo-01',
    resource_id='site-01' # Optional, resource ID
  ))

  # All the entitlements the customer is entitled to  
  entitlements = resp.entitlements
  ```

  ```ruby Ruby theme={null}
  resp = client.get_entitlements(
    Stigg::Sidecar::V1::GetEntitlementsRequest.new(
      customer_id: "customer-demo-01",
      resource_id: "site-01",
    )
  )

  # List of entitlements
  p resp.entitlements
  ```

  ```go Go theme={null}
  req := sidecarv1.GetEntitlementsRequest{
    CustomerId: "customer-demo-01",
  }

  resp, err := client.GetEntitlements(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to get entitlements: %v\n", err)
  }

  fmt.Printf("Response: %v\n", resp)
  ```

  ```java Java theme={null}
  var req =
    GetEntitlementsRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setResourceId("site-01") // Optional, specify the resource ID
    .build();

  var resp = stigg.getEntitlements(req);
      
  // All the entitlements the customer is entitled to  
  List<Entitlement> entitlements = resp.getEntitlementsList();
  ```
</CodeGroup>

<Expandable title="Result">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "entitlements": [
        {
          "metered": {
            "feature": {
              "id": "feature-example-9",
              "featureType": "FEATURE_TYPE_NUMBER",
              "units": "active user",
              "unitsPlural": "active users",
              "meterType": "METER_TYPE_INCREMENTAL",
              "isMetered": true
            },
            "usageLimit": 25000.0,
            "currentUsage": 1.0,
            "resetPeriod": "ENTITLEMENT_RESET_PERIOD_MONTH",
            "usagePeriodAnchor": "2023-07-21T00:00:00Z",
            "usagePeriodStart": "2023-11-21T00:00:00Z",
            "usagePeriodEnd": "2023-12-21T00:00:00Z",
          }
        },
        {
          "boolean": {
            "feature": {
              "id": "feature-example-8",
              "featureType": "FEATURE_TYPE_BOOLEAN",
              "units": "",
              "unitsPlural": "",
              "meterType": "METER_TYPE_NONE"
            }
          }
        },
        {
          "numeric": {
            "feature": {
              "id": "feature-example-7",
              "featureType": "FEATURE_TYPE_NUMBER",
              "units": "MB",
              "unitsPlural": "MB",
              "meterType": "METER_TYPE_NONE"
            },
            "value": 100
          }
        },
        {
          "metered": {
            "feature": {
              "id": "feature-example-6",
              "featureType": "FEATURE_TYPE_NUMBER",
              "units": "campaign",
              "unitsPlural": "campaigns",
              "meterType": "METER_TYPE_INCREMENTAL",
              "isMetered": true
            },
            "usageLimit": 32.0,
            "resetPeriod": "ENTITLEMENT_RESET_PERIOD_MONTH",
            "usagePeriodAnchor": "2023-02-21T00:00:00Z",
            "usagePeriodStart": "2023-11-21T00:00:00Z",
            "usagePeriodEnd": "2023-12-21T00:00:00Z"
          }
        },
        {
          "metered": {
            "feature": {
              "id": "feature-example-4",
              "featureType": "FEATURE_TYPE_NUMBER",
              "units": "template",
              "unitsPlural": "templates",
              "meterType": "METER_TYPE_FLUCTUATING",
              "isMetered": true
            },
            "usageLimit": 6.0,
            "currentUsage": 3.0,
            "resetPeriod": "ENTITLEMENT_RESET_PERIOD_UNSPECIFIED"
          }
        },
        {
          "boolean": {
            "feature": {
              "id": "feature-example-5",
              "featureType": "FEATURE_TYPE_BOOLEAN",
              "units": "",
              "unitsPlural": "",
              "meterType": "METER_TYPE_NONE"
            }
          }
        }
      ]
    }
    ```
  </CodeGroup>
</Expandable>

# Reload entitlements for a specific customer

Invalidate the cache and reload entitlements of a customer or customer resource. Note: this method skips the Edge API, so use it only when you need "read-after-write" consistency after entitlements presumably have changed.

<CodeGroup>
  ```python Python theme={null}
  from stigg_sidecar_sdk import ReloadEntitlementsRequest

  await stigg.reload_entitlements(ReloadEntitlementsRequest(
    customer_id='customer-demo-01',
    resource_id='site-01' # Optional, resource ID
  ))
  ```

  ```ruby Ruby theme={null}
  resp = client.reload_entitlements(
    Stigg::Sidecar::V1::ReloadEntitlementsRequest.new(
      customer_id: "customer-demo-01",
      resource_id: "site-01",
    )
  )
  ```

  ```go Go theme={null}
  req := sidecarv1.ReloadEntitlementsRequest{
    CustomerId: "customer-demo-01",
  }

  resp, err := client.ReloadEntitlements(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to reload entitlements: %v\n", err)
  }
  ```

  ```java Java theme={null}
  var req =
    ReloadEntitlementsRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setResourceId("site-01") // Optional, specify the resource ID
    .build();

  resp = stigg.reloadEntitlements(req);
  ```
</CodeGroup>

## Reporting usage measurements to Stigg

The Stigg SDK allows you to report usage measurements for [metered features](/documentation/modeling-your-pricing-in-stigg/features#overview). The reported usage will be used to track, limit and bill the customer's usage of metered features.

Stigg supports metering of usage from the following data sources:

1. [Calculated usage](#calculated-usage) - usage that has been aggregated and calculated by **your application**. This type is useful for features, such as: seats.
2. [Raw events](#raw-events) - raw events from your application, which **Stigg** filters and aggregates aggregate to calculate customer usage. This type is useful for features, such as: monthly active users (MAUs).

More details about Stigg's metering and aggregation capabilities can be found here:

<Card title="Stigg's metering and aggregation capabilities" icon="gauge" horizontal href="/documentation/getting-usage-data-into-stigg/overview" />

<Warning>
  1. Reporting of measurements to Stigg should be done only after the relevant resources have been provisioned in your application.
  2. To ensure that the provisioned resources are aligned with the measurements that are reported to Stigg, ensure that customers are not allowed to allocate new resources until an acknowledgement about the processed measurement is received from the Stigg backend.
</Warning>

<Note>
  Validating that a measurement was successfully reported to Stigg is also possible in the [customer details section](/documentation/managing-customers-and-subscriptions/customers#viewing-customers-entitlement-usage) of the relevant customer in the Stigg app.
</Note>

### Calculated usage

<CodeGroup>
  ```python Python theme={null}
  from stigg_sidecar_sdk import ReportUsageRequest

  # Example: incrementing usage of a metered feature by 2

  await stigg.report_usage(ReportUsageRequest(
    customer_id='customer-demo-01',
    feature_id='feature-example-4',
    value=2,  # Usage amount
    resource_id='site-01' # Optional, resource ID
  ))
  ```

  ```ruby Ruby theme={null}
  # Example: incrementing usage of a metered feature by 2

  resp = client.report_usage(Stigg::Sidecar::V1::ReportUsageRequest.new(
    customer_id: 'customer-demo-01',
    feature_id: 'feature-example-4',
    value: 2, # Usage amount
    resource_id: 'site-01' # Optional, resource ID
  ))
  ```

  ```go Go theme={null}
  req := sidecarv1.ReportUsageRequest{
    CustomerId: "customer-demo-01",
    FeatureId:  "feature-example-4",
    Value:      3,
  }

  resp, err := client.ReportUsage(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to report usage: %v\n", err)
    return
  }

  fmt.Printf("Usage reported: %v\n", resp.MeasurementId)
  ```

  ```java Java theme={null}
  // Example: incrementing usage of a metered feature by 2

  var req =
    ReportUsageRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setFeatureId("feature-example-4")
    .setResourceId("site-01") // Optional, specify the resource ID
    .setValue(2)
    .build();

  stigg.reportUsage(req);
  ```
</CodeGroup>

By default, the value reported should represent the **change** in usage of the feature, for example user added 2 more seats then the report usage call should have the value of 2.

In some cases, it's more useful to set the usage to some value instead of reporting the change value, it's possible by passing the `updateBehavior` parameter:

<CodeGroup>
  ```python Python theme={null}
  # Example: set usage of a metered feature to 3

  resp = await stigg.report_usage(ReportUsageRequest(
    customer_id='customer-demo-01',
    feature_id='feature-example-4',
    value=3,  # Usage amount
    update_behavior=UsageUpdateBehavior.USAGE_UPDATE_BEHAVIOR_SET,
    resource_id='site-01' # optional, resource ID
  ))
  ```

  ```ruby Ruby theme={null}
  # Example: set usage of a metered feature to 3

  resp = client.report_usage(
    Stigg::Sidecar::V1::ReportUsageRequest.new(
      customer_id: 'customer-demo-01',
      feature_id: 'feature-example-4',
      value: 3, # Usage amount
      resource_id: 'site-01', # Optional, resource ID
      update_behavior: 'USAGE_UPDATE_BEHAVIOR_SET'
    )
  )
  ```

  ```go Go theme={null}
  updateBehavior := sidecarv1.UsageUpdateBehavior_USAGE_UPDATE_BEHAVIOR_SET
  req := sidecarv1.ReportUsageRequest{
    CustomerId:     "customer-demo-01",
    FeatureId:      "feature-example-4",
    Value:          3,
    UpdateBehavior: &updateBehavior,
  }

  resp, err := client.ReportUsage(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to report usage: %v\n", err)
    return
  }

  fmt.Printf("Usage reported: %v\n", resp.MeasurementId)
  ```

  ```java Java theme={null}
  // Example: set usage of a metered feature to 3

  var req =
    ReportUsageRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setFeatureId("feature-example-4")
    .setResourceId("site-01") // Optional, specify the resource ID
    .setValue(3) // Usage amount
    .setUpdateBehavior(UsageUpdateBehavior.USAGE_UPDATE_BEHAVIOR_SET)
    .build();

  stigg.reportUsage(req);
  ```
</CodeGroup>

<Expandable title="Result">
  <CodeGroup>
    ```json JSON theme={null}
    {
      "measurementId": "2bbd8473-ab28-4a2e-93ec-25d2489cf2b4"
    }
    ```
  </CodeGroup>
</Expandable>

### Report in bulks

Calculated usage measurements can also be reported in bulks:

<CodeGroup>
  ```python Python theme={null}
  from stigg_sidecar_sdk import ReportUsageRequest

  resp = await stigg.report_usage_bulk(ReportUsageBulkRequest(
    usages=[
      ReportUsageBulkRequest(
        customer_id='customer-demo-01',
        feature_id='feature-example-4',
        value=2
      )
    ]
  ))
  ```

  ```ruby Ruby theme={null}
  resp = client.report_usage_bulk(Stigg::Sidecar::V1::ReportUsageBulkRequest.new(
      usages: [
        Stigg::Sidecar::V1::ReportUsageBulkRequest.new(
          customer_id: 'customer-demo-01',
          feature_id: 'feature-example-4',
          value: 2
        )
      ]
    ))
  ```

  ```go Go theme={null}
  req := sidecarv1.ReportUsageBulkRequest{
    Usages: []*sidecarv1.ReportUsageRequest{
      {
        CustomerId: "customer-demo-01",
        FeatureId:  "feature-example-4",
        Value:      3,
      },
    },
  }

  resp, err := client.ReportUsageBulk(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to report usages: %v\n", err)
    return
  }

  fmt.Printf("Usages reported: %v\n", resp.Usages)
  ```

  ```java Java theme={null}
  // Example: incrementing usage of a metered feature by 2

  var usage =
    ReportUsageRequest.newBuilder()
    .setCustomerId("customer-demo-01")
    .setFeatureId("feature-example-4")
    .setResourceId("site-01") // Optional, specify the resource ID
    .setValue(2)
    .build();

  var req = 
    ReportUsageBulkRequest.newBuilder()
    .setUsages(Arrays.asList(usages))
    .build();

  stigg.reportUsageBulk(req);
  ```
</CodeGroup>

### Raw events

<CodeGroup>
  ```python Python theme={null}
  from stigg_sidecar_sdk import ReportEventsRequest, EventDimensionValue

  # Example: report user_login events

  resp = await stigg.report_events(ReportEventsRequest(
    events=[Event(
      customer_id="customer-demo-01",
      resource_id='site-01', # Optional, resource ID
      event_name="user_login",
      idempotency_key="227c1b73-883a-457b-b715-6ba5a2c69ce4",
      dimensions={
        'user_id': EventDimensionValue(string_value='user-01'),
        'user_email': EventDimensionValue(string_value='john@example.com'),
      },
      timestamp=datetime.datetime.now(),  # Optional, pass it to report event with specific timestamp
    )]
  ))
  ```

  ```ruby Ruby theme={null}
  # Example: report user_login events

  resp = client.report_events(Stigg::Sidecar::V1::ReportEventsRequest.new(
    events: [Stigg::Sidecar::V1::Event.new(
      customer_id: "customer-demo-01",
      resource_id: "site-01", # Optional, resource ID
      event_name: "user_login",
      idempotency_key: "227c1b73-883a-457b-b715-6ba5a2c69ce4",
      dimensions: {
        'user_id': Stigg::Sidecar::V1::EventDimensionValue.new(string_value: "user-01"),
        'user_email': Stigg::Sidecar::V1::EventDimensionValue.new(string_value: "john@example.com")
      },
      # Optional, pass it to report event with specific timestamp
      timestamp: Google::Protobuf::Timestamp.new({ seconds: Time.now.to_i })
    )]
  ))
  ```

  ```go Go theme={null}
  events := []*sidecarv1.Event{{
    EventName:      "user_login",
    CustomerId:     "customer-demo-01",
    IdempotencyKey: "227c1b73-883a-457b-b715-6ba5a2c69ce4",
    Dimensions: map[string]*sidecarv1.EventDimensionValue{
      "user_id":    {Value: &sidecarv1.EventDimensionValue_StringValue{StringValue: "user-01"}},
      "user_email": {Value: &sidecarv1.EventDimensionValue_StringValue{StringValue: "john@example.com"}},
    },
    Timestamp: &timestamppb.Timestamp{
      Seconds: time.Now().Unix(),
    },
  }}
  req := sidecarv1.ReportEventsRequest{Events: events}

  _, err := client.ReportEvents(context.Background(), &req)
  if err != nil {
    fmt.Printf("Failed to report events: %v\n", err)
    return
  }

  fmt.Printf("Events reported\n")
  ```

  ```java Java theme={null}
  // Example: report user_login events

  var req =
    ReportEventsRequest.newBuilder()
    .addEvents(
    Event.newBuilder()
    .setCustomerId("customer-demo-01")
    .setResourceId("site-01") // Optional, resource ID
    .setEventName("user_login")
    .setIdempotencyKey("227c1b73-883a-457b-b715-6ba5a2c69ce4")
    .putDimensions("user_id", EventDimensionValue.newBuilder().setStringValue("user-01").build())
    .putDimensions("user_email", EventDimensionValue.newBuilder().setStringValue("john@example.com").build())
    .setTimestamp(Timestamp.newBuilder().setSeconds(System.currentTimeMillis() / 1000).build()) // Optional, pass it to report event with specific timestamp
    .build()
  ).build();

  stigg.reportEvents(req);
  ```
</CodeGroup>

It's also possible to send a batch of events in one invocation.

<Warning>
  The `dimensions` field support key value pairs, while keys are strictly of type `string` values can be `string | number | boolean`
</Warning>

## Access to additional Stigg APIs

The Sidecar SDK exposes a type-safe generated API client that can be used for send requests to the Stigg API:

<CodeGroup>
  ```python Python theme={null}
  from stigg.generated import ProvisionCustomerInput, ProvisionSubscriptionInput, BillingPeriod

  # Example: access the client through the `.api` field

  cust_resp = await stigg.api.provision_customer(ProvisionCustomerInput(
    customer_id='customer-demo-01',
    email='john@example.com'
  ))

  sub_resp = await stigg.api.provision_subscription(ProvisionSubscriptionInput(
    customer_id='customer-demo-01',
    plan_id='plan-revvenu-essentials',
    billing_period=BillingPeriod.MONTHLY,
    # Optional, entitlements for custom plans (feature + credit)
    entitlements=[
      {"feature": {"feature_id": "feature-seats", "usage_limit": 50}},
      {"credit": {"custom_currency_id": "currency-api-credits", "amount": 50000, "cadence": "MONTH"}},
    ]
  ))

  print(sub_resp.provision_subscription.status)  # SUCCESS
  ```

  ```ruby Ruby theme={null}
  # Example: access the client through the `.api` field

  client.api.request(Stigg::Mutation::ProvisionCustomer, {
    "input": {
      "customerId": "customer-demo-01",
      "email": "john@example.com"
    }
  })

  sub_resp = client.api.request(Stigg::Mutation::ProvisionSubscription, {
    "input": {
      "customerId": "customer-demo-01",
      "planId": "plan-revvenu-essentials",
      "billingPeriod": "MONTHLY",
      # Optional, entitlements for custom plans (feature + credit)
      "entitlements": [
        {"feature": {"featureId": "feature-seats", "usageLimit": 50}},
        {"credit": {"customCurrencyId": "currency-api-credits", "amount": 50000, "cadence": "MONTH"}}
      ]
    }
  })

  p sub_resp.provision_subscription.status
  ```

  ```go Go theme={null}
  customerId := "customer-demo-01"
  email := "john@example.com"
  customerResult, err := client.Api.ProvisionCustomer(context.Background(), stigg.ProvisionCustomerInput{
    CustomerID: &customerId,
    Email:      &email,
  })
  if err != nil {
    t.Fatalf("Failed to provision customer: %v\n", err)
  }
  fmt.Printf("Customer provisioned: %v\n", customerResult)

  billingPeriod := stigg.BillingPeriodMonthly
  unitQuantity := 2.
  subscriptionResult, err := client.Api.ProvisionSubscription(context.Background(), stigg.ProvisionSubscriptionInput{
    CustomerID:    customerId,
    PlanID:        "plan-revvenu-essentials",
    BillingPeriod: &billingPeriod,
    UnitQuantity:  &unitQuantity,
    // Optional, entitlements for custom plans (feature + credit)
    Entitlements: []*stigg.SubscriptionEntitlementInputV2{
      {Feature: &stigg.SubscriptionFeatureEntitlementInput{FeatureID: "feature-seats", UsageLimit: Ptr(float64(50))}},
      {Credit: &stigg.SubscriptionCreditEntitlementInput{CustomCurrencyID: "currency-api-credits", Amount: 50000, Cadence: stigg.CreditCadenceMonth}},
    },
  })
  if err != nil {
    t.Fatalf("Failed to provision subscription: %v\n", err)
  }

  fmt.Printf("Subscription provisioned: %v\n", subscriptionResult)
  ```

  ```java Java theme={null}
  // Example: access the client through the `.api` field

  var customerRes = stigg.api().mutation(ProvisionCustomerMutation.builder()
                                         .input(ProvisionCustomerInput.builder()
                                                .customerId("customer-demo-01")
                                                .email("john@example.com")
                                                .build())
                                         .build());

  var customerId = customerRes.provisionCustomer.customer.slimCustomerFragment.customerId;

  var subscriptionRes = stigg.api().mutation(ProvisionSubscriptionMutation.builder()
                                             .input(ProvisionSubscriptionInput.builder()
                                                    .customerId(customerId)
                                                    .planId("plan-revvenu-essentials")
                                                    .billingPeriod(BillingPeriod.MONTHLY)
                                                    .unitQuantity(2.)
                                                    // Optional, entitlements for custom plans (feature + credit)
                                                    .entitlements(List.of(
                                                      SubscriptionEntitlementInputV2.builder()
                                                        .feature(SubscriptionFeatureEntitlementInput.builder().featureId("feature-seats").usageLimit(50.0).build()).build(),
                                                      SubscriptionEntitlementInputV2.builder()
                                                        .credit(SubscriptionCreditEntitlementInput.builder().customCurrencyId("currency-api-credits").amount(50000.0).cadence(CreditCadence.MONTH).build()).build()
                                                    ))
                                                    .build())
                                             .build());

  if (subscriptionRes.provisionSubscription.status == ProvisionSubscriptionStatus.SUCCESS) {
    // subscription provisioned
  }
  ```
</CodeGroup>

You can find all the available API client functions under each SDK language:

<CardGroup cols={2}>
  <Card title horizontal href="./python">
    <div style={{ display:"flex",alignItems:"center",gap:"12px" }}>
      <img src="https://mintcdn.com/stigg/FZ_ywutvYHnQbKpn/images/python.svg?fit=max&auto=format&n=FZ_ywutvYHnQbKpn&q=85&s=8aa4c263d3355c39616dac4786167a63" alt="Python" style={{ width:"32px" }} width="64" height="64" data-path="images/python.svg" />

      <span>Python</span>
    </div>
  </Card>

  <Card title horizontal href="./ruby">
    <div style={{ display:"flex",alignItems:"center",gap:"12px" }}>
      <img src="https://mintcdn.com/stigg/FZ_ywutvYHnQbKpn/images/ruby.svg?fit=max&auto=format&n=FZ_ywutvYHnQbKpn&q=85&s=bfbfc0efe42725d5dcce5c007af63b6f" alt="Ruby" style={{ width:"32px" }} width="64" height="64" data-path="images/ruby.svg" />

      <span>Ruby</span>
    </div>
  </Card>

  <Card title horizontal href="./go">
    <div style={{ display:"flex",alignItems:"center",gap:"12px" }}>
      <img src="https://mintcdn.com/stigg/aJeGPh9dwMpK_Kcr/images/go.svg?fit=max&auto=format&n=aJeGPh9dwMpK_Kcr&q=85&s=14fba808f81d5b37031e986828224216" alt="go" style={{ width:"32px" }} width="256" height="96" data-path="images/go.svg" />

      <span>Go</span>
    </div>
  </Card>

  <Card title horizontal href="./java">
    <div style={{ display:"flex",alignItems:"center",gap:"12px" }}>
      <img src="https://mintcdn.com/stigg/aJeGPh9dwMpK_Kcr/images/java.svg?fit=max&auto=format&n=aJeGPh9dwMpK_Kcr&q=85&s=2b995d4253d62bc092ad570c6aea8650" alt="java" style={{ width:"32px" }} width="64" height="64" data-path="images/java.svg" />

      <span>Java</span>
    </div>
  </Card>
</CardGroup>

## Persistent caching

In order for the cache to survive between restarts or to be shared across multiple instances of the Sidecar service, you can configure the Sidecar to use Redis as a cache provider.

### Prerequisites

Run a [Persistent Cache Service](/documentation/high-availability-and-scale/persistent-caching) in a separate process.

### SDK configuration

<CodeGroup>
  ```python Python theme={null}

  from stigg_sidecar_sdk import Stigg, ApiConfig, LocalSidecarConfig, RedisOptions

  stigg = Stigg(
    ApiConfig(
      api_key="<FULL_ACCESS_KEY>",
    ),
    # For development purposes, configure local sidecar to use redis: 
    local_sidecar_config=LocalSidecarConfig(
      redis=RedisOptions(
        environment_prefix="development",
        host="localhost",
        port=6379,
        db=0
      )
    ),
    # For production use, provide remote sidecar with the REDIS_* env vars:
    remote_sidecar_host='localhost',
    remote_sidecar_port=80
  )
  ```

  ```java Java theme={null}
  import io.stigg.sidecar.sdk.Stigg;
  import io.stigg.sidecar.sdk.StiggConfig;
  import io.stigg.sidecar.proto.v1.*;

  class App {
    public static void main(String[] ...args) {
      var stigg = Stigg.init(
        StiggConfig.builder()
        // To access the API you need to set the server AP key:
        .apiConfig(ApiConfig.newBuilder().setApiKey("<SERVER-API-KEY>").build())
        
        // For production use, provide remote sidecar with the REDIS_* env vars:
        .remoteSidecarHost("localhost")
        .remoteSidecarPort(80)
        .build()
      );
    }
  }
  ```
</CodeGroup>

## SDK changelog

<Card title="Sidecar SDK changelog" icon="clock-rotate-left" horizontal href="/api-and-sdks/changelog/backend-graphql/sidecar" />
