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

# Explaining SDK Requests

> Add a signed per-request Explain control, preserve the native response and retrieve its report.

Use a per-request control when the request your application already constructs is the case you need to understand. The control must be added before SigV4 signing and must remain present on every retry of that attempt.

This guide uses AWS SDK for Java 2.x and S3 `PutObject`. The application keeps its S3 model and configured Cloud Adapter endpoint. The example selects `ExecuteAndExplain`, so it performs the upload.

## Decide whether the request may execute

| Control                                                 | Service action | SDK response behavior                                              | Appropriate use                                                      |
| ------------------------------------------------------- | -------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------- |
| `{"version":1,"mode":"Explain","fmt":"Json"}`           | Not performed  | Returns an explanation document, not a normal `PutObjectResponse`  | A preview handled by an explanation-aware client                     |
| `{"version":1,"mode":"ExecuteAndExplain","fmt":"Json"}` | Performed      | Returns the ordinary service response and report receipt           | An authorized test or real operation whose effects are intended      |
| `true`                                                  | Performed      | Same native-response behavior as `ExecuteAndExplain`, Human report | A concise opt-in when the executing semantics are already understood |

An ordinary generated AWS SDK client expects an origin service response. It can consume `ExecuteAndExplain` because the adapter preserves that native response. Explicit `Explain` instead returns a diagnostic document and needs explanation-aware handling; do not cast that document to a successful created-resource response or allow the SDK to retry it as a transient parsing failure.

<Warning>
  The `true` shorthand performs the request. It is not equivalent to an explicit version-1 control whose mode is `Explain`.
</Warning>

## Configure the S3 client

Set the endpoint either process-wide or on this client, as described in the [AWS Java SDK guide](/cloud-adapter/guides/aws-java-sdk). Keep origin credentials separate from the target-cloud identity held by the adapter.

For one client:

```java theme={null}
import java.net.URI;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

S3Client s3 = S3Client.builder()
    .endpointOverride(URI.create(System.getenv("T9_CLOUD_ADAPTER_ENDPOINT")))
    .region(Region.US_EAST_1)
    .credentialsProvider(credentials)
    .forcePathStyle(true)
    .build();
```

The caller supplies its normal configured `AwsCredentialsProvider`; do not place an access key in source code or diagnostic output.

## Execute one upload and request a report

`AwsRequestOverrideConfiguration` adds the header before the SDK signs this request:

```java theme={null}
import java.net.URI;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;

public final class ExplainUploadExample {
  private ExplainUploadExample() {}

  public static PutObjectResponse upload(
      URI endpoint,
      AwsCredentialsProvider credentials,
      String bucket,
      String key) {
    try (S3Client client = S3Client.builder()
        .endpointOverride(endpoint)
        .region(Region.US_EAST_1)
        .credentialsProvider(credentials)
        .forcePathStyle(true)
        .build()) {
      PutObjectRequest request = PutObjectRequest.builder()
          .bucket(bucket)
          .key(key)
          .overrideConfiguration(c -> c.putHeader(
              "x-t9-explain",
              "{\"version\":1,\"mode\":\"ExecuteAndExplain\",\"fmt\":\"Json\"}"))
          .build();

      PutObjectResponse response = client.putObject(
          request,
          RequestBody.fromString("hello"));

      System.out.println("HTTP " + response.sdkHttpResponse().statusCode());
      System.out.println("Report " + response.sdkHttpResponse()
          .firstMatchingHeader("x-t9-explain-id")
          .orElse("not supplied"));
      System.out.println("Mode " + response.sdkHttpResponse()
          .firstMatchingHeader("x-t9-explain-mode")
          .orElse("not supplied"));
      return response;
    }
  }
}
```

This is an executing example. Use a unique test key, verify the object in the target service, and remove it only after you have preserved the report and verified what was created. A `PutObjectResponse` or ETag establishes neither report completeness nor support for multipart or resumable upload.

## Capture receipts on success and service errors

Read receipt headers from the SDK's HTTP response before converting the result into application-specific logging. The same helper works for a response carried by an AWS service exception. This complete class is compiled against the same AWS SDK major version used by the application example:

```java theme={null}
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.http.SdkHttpResponse;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
import software.amazon.awssdk.services.s3.model.S3Exception;

public final class ExplainReceiptCaptureExample {
  private ExplainReceiptCaptureExample() {}

  public record ExplainReceipt(
      String reportId,
      String mode,
      String unexplained) {
    static ExplainReceipt from(SdkHttpResponse response) {
      return new ExplainReceipt(
          response.firstMatchingHeader("x-t9-explain-id").orElse(null),
          response.firstMatchingHeader("x-t9-explain-mode").orElse(null),
          response.firstMatchingHeader("x-t9-explain-unexplained").orElse(null));
    }
  }

  public static PutObjectResponse upload(
      S3Client client,
      PutObjectRequest request,
      RequestBody body) {
    try {
      PutObjectResponse response = client.putObject(request, body);
      ExplainReceipt receipt = ExplainReceipt.from(response.sdkHttpResponse());
      // Store the receipt with your request record, without credentials or payload data.
      System.out.printf("report=%s mode=%s%n", receipt.reportId(), receipt.mode());
      return response;
    } catch (S3Exception error) {
      ExplainReceipt receipt = ExplainReceipt.from(
          error.awsErrorDetails().sdkHttpResponse());
      System.err.printf("status=%d requestId=%s report=%s mode=%s%n",
          error.statusCode(), error.requestId(), receipt.reportId(), receipt.mode());
      throw error;
    } catch (SdkClientException error) {
      // DNS, TLS, connection, signing setup, or SDK decoding can fail before any HTTP response.
      System.err.println("No usable service response: " + error.getMessage());
      throw error;
    }
  }
}
```

A service error can still carry a report receipt. A transport error might have no HTTP response at all. Neither case is permission to replay a write. First inspect target state and the operation's idempotency.

## Handle preview mode deliberately

To preview the same request, change only the control value:

```java theme={null}
.overrideConfiguration(c -> c.putHeader(
    "x-t9-explain",
    "{\"version\":1,\"mode\":\"Explain\",\"fmt\":\"Json\"}"))
```

The adapter does not perform `PutObject`. Its response is an explanation document rather than an S3 `PutObjectResponse`. Use an explanation-aware integration that:

1. marks the explained response as non-retryable;
2. preserves the report ID and document;
3. does not treat the absence of an S3 ETag as a transient provider error;
4. reads the report through the authorized report route;
5. has the report owner acknowledge the sealed revision only after exporting the evidence others need.

Use only a published explanation-aware integration available in your environment. If none is available, do not send `Explain` through a response-unaware generated SDK or invent a response wrapper. Use [`tensor9 explain`](/cloud-adapter/debugging/using-explain) to inspect the directed profile while you coordinate an approved request-level integration.

## Keep the control signed and singular

Request override configuration is applied before SigV4 signing. Middleware that mutates the final HTTP request afterward can invalidate the signature or leave the Explain control unsigned.

Inspect the final request construction if the request is refused:

* add exactly one `x-t9-explain` field;
* do not combine a default interceptor and a request override that append two fields;
* use the exact case-sensitive JSON enum values;
* keep the header ASCII and under 4096 bytes;
* ensure the signing identity covers `x-t9-explain`.

If the request is retried by the SDK, every execution attempt can have its own report. Preserve each report ID rather than overwriting the first with the last. An explanation-aware client must also prevent explicit `Explain` documents from entering a normal service retry loop.

## Retrieve without replaying

Once you have `x-t9-explain-id`, read that report. Do not repeat `PutObject` to “get the explanation again.” A signed report GET returns the current revision in Human, Agent or JSON format. For an asynchronous origin operation with a declared status read, an explanation-aware client can poll the original status operation with:

```text theme={null}
x-t9-explain-id: report-123
Accept: application/vnd.tensor9.explain+json
x-t9-explain-fmt: Json
```

That status poll retains the origin operation's native result inside the explanation envelope. Select by the exact resource and operation identity; do not guess from a reused display name.

See [Diagnostic Response Headers](/cloud-adapter/debugging/response-headers) for report GET, poll and acknowledgment contracts. See [Following Asynchronous Explanations](/cloud-adapter/debugging/async-explanations) for partial revisions and lost final responses.

## Assign acknowledgment ownership

The authenticated principal recorded as the report owner is the principal that can read and acknowledge it through the HTTP report routes. Acknowledging its sealed revision consumes that report. Decide responsibility before the request:

* the application can export a sanitized bundle and leave acknowledgment to the component acting as the report owner;
* an automated test that owns the report can acknowledge after its assertions and evidence export;
* people or systems that do not hold the owner identity receive only the sanitized evidence shared through an approved channel, not direct report-route access.

Never acknowledge merely because the native operation succeeded. First record the final report revision, declared limitations and evidence needed by other people. If the owner loses the acknowledgment response, repeating the same sealed revision is idempotent and returns `Consumed`.
