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

# SDK Specific Guidance

> Use Oso SDKs with language-specific features and idioms.

This section contains SDK-specific guidance beyond the shared API reference. Find details on async APIs, executors, and other language-specific behaviors for Java, Kotlin, and more.

## Java

Java SDK provides additional features to control how requests are executed. This guide covers how to set request timeouts, configure retries for failed requests, and make asynchronous calls.

### Setting Request Timeouts for Oso Cloud

You may configure your client to abort requests to Oso Cloud that exceed given timeouts.
When a timeout is exceeded, the SDK will throw a `com.osohq.oso_cloud.OsoTimeoutException`.

#### Setting a Default Timeout for All Requests

Use `withDefaultTimeoutMilliseconds` to set a timeout that applies to all requests.

```java theme={null}
import com.osohq.oso_cloud.Oso;
import com.osohq.oso_cloud.OsoClientOptions;

OsoClientOptions options = new OsoClientOptions.Builder()
    .withDefaultTimeoutMilliseconds(2000)
    .build();
Oso oso = new Oso(YOUR_API_KEY, null, options);
```

#### Setting Separate Timeouts for Read and Write Requests

* Use `withReadTimeoutMilliseconds` to set a timeout that applies only to requests that
  read data from Oso Cloud; such as `authorize` and `get`.
* Use `withWriteTimeoutMilliseconds` to set a timeout that applies only to requests that
  write data to Oso Cloud; such as `insert` and `delete`.

If you use these together with `withDefaultTimeoutMilliseconds`, that default timeout will
*NOT* be applied to requests for which a more specific timeout was set.

```java theme={null}
import com.osohq.oso_cloud.Oso;
import com.osohq.oso_cloud.OsoClientOptions;

OsoClientOptions options = new OsoClientOptions.Builder()
    .withReadTimeoutMilliseconds(500)
    .withWriteTimeoutMilliseconds(2000)
    .build();
Oso oso = new Oso(YOUR_API_KEY, null, options);
```

### Retrying Failed Requests

The Oso Cloud client automatically retries requests that failed for any of the following reasons:

* HTTP 429 (Too Many Requests)
* HTTP 5xx (Server Errors)
* Connection errors
* [Timeouts](#setting-request-timeouts-for-oso-cloud)

The client will make a total of up to 3 attempts, with jittered delays of 0-20ms for the first retry and 0-40ms for the second retry.

### Retrieving Request IDs for Debugging

The Oso Cloud client exposes the `X-Request-ID` header from API responses via the `getRequestId()` method. The `X-Request-ID` is a UUIDv4 generated per request. This is useful for debugging, error reporting, and distributed tracing.

#### Synchronous Requests

For synchronous API calls, `getRequestId()` returns the `X-Request-ID` from the last request made on the current thread:

```java theme={null}
Oso oso = new Oso("your-api-key");
oso.authorize(actor, action, resource);

String requestId = oso.getRequestId();
```

#### Asynchronous Requests

For asynchronous API calls, `getRequestId()` on the `Oso.Async` instance returns the `X-Request-ID` from the last request made through that instance:

```java theme={null}
Oso.Async async = oso.withAsyncExecutor(executor);
async.authorize(actor, action, resource).get();

String requestId = async.getRequestId();
```

The method returns `null` if no API calls have been made yet.

### Making Non-Blocking `CompletableFuture` Requests With `Oso.Async`

By default, the Oso Cloud client is *blocking*: calling a function will block the execution of the current thread until the function returns.
In versions 1.4 and above, you can use the `Oso.Async` API to make non-blocking requests that run on an
[`Executor`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/Executor.html).

`Oso.Async` supports all the same functions as `Oso`, but each function returns a
[`CompletableFuture`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/CompletableFuture.html),
which you can `get()` or `cancel()` as necessary.

Cancelling the futures returned by these functions will abort any in-progress HTTP requests.

You can obtain an `Oso.Async` instance by calling `withAsyncExecutor(Executor)` on an `Oso` instance:

```java theme={null}
import com.osohq.oso_cloud.Oso;
import com.osohq.oso_cloud.Oso.Async;
import com.osohq.oso_cloud.Value;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

Executor executor = Executors.newSingleThreadExecutor(); // or any other Executor
Oso.Async osoAsync = (new Oso(YOUR_API_KEY)).withAsyncExecutor(executor);

// Then:
Value user = new Value("User", "alice");
Value anvilsRepository = new Value("Repository", "anvils");
String action = "read";

CompletableFuture<Boolean> allowedFuture = osoAsync.authorize(user, action, anvilsRepository);
boolean allowed = allowedFuture.get();
```

Or in Kotlin:

```kotlin theme={null}
import com.osohq.oso_cloud.Oso
import com.osohq.oso_cloud.Value
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.future.await

// Run Oso requests in the thread pool dedicated to blocking I/O
val osoAsync = Oso(YOUR_API_KEY).withAsyncExecutor(Dispatchers.IO.asExecutor())

// Then:
val allowed = osoAsync.authorize(Value("User", "alice"), "read", Value("Repository", "anvils")).await()
```

In Kotlin, [`await`ing](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.future/await.html) a `CompletableFuture` returned by `Oso.Async` will cooperatively propagate cancellations.
When a parent coroutine is cancelled, any in-progress HTTP requests in the `await`ed future will be aborted.

Note that while calls to `Oso.Async` will not block the *current* thread, the underlying `Runnable` HTTP requests submitted to the `Executor` are fundamentally blocking and can block threads on your `Executor` when they run.
You may wish to use an `Executor` with its own thread pool for `Oso.Async`, to avoid thread starvation in other parts of your application.
