diff --git a/docs/develop/java/error-handling.mdx b/docs/develop/java/error-handling.mdx
index 2ecdae44..889ccae7 100644
--- a/docs/develop/java/error-handling.mdx
+++ b/docs/develop/java/error-handling.mdx
@@ -35,4 +35,33 @@ You can catch terminal errors, and build your control flow around it.
When throwing a terminal error, undo earlier handler actions to keep the system consistent. See our [sagas guide](/guides/sagas) for details.
-
\ No newline at end of file
+
+
+## Terminal error metadata
+
+You can attach a metadata map to a `TerminalException` to propagate structured context to callers.
+This requires Restate Server >= 1.6.
+
+
+```java Java {"CODE_LOAD::java/src/main/java/develop/ErrorHandling.java#metadata"}
+throw new TerminalException(
+ "Something went wrong", Map.of("correlationId", correlationId));
+```
+```kotlin Kotlin {"CODE_LOAD::kotlin/src/main/kotlin/develop/ErrorHandling.kt#metadata"}
+throw TerminalException(
+ "Something went wrong", mapOf("correlationId" to correlationId))
+```
+
+
+The metadata is propagated to callers and accessible via `getMetadata()` / `.metadata`:
+
+
+```java Java {"CODE_LOAD::java/src/main/java/develop/ErrorHandling.java#catch_metadata"}
+Map metadata = e.getMetadata();
+String correlationId = metadata.get("correlationId");
+```
+```kotlin Kotlin {"CODE_LOAD::kotlin/src/main/kotlin/develop/ErrorHandling.kt#catch_metadata"}
+val metadata = e.metadata
+val correlationId = metadata["correlationId"]
+```
+
\ No newline at end of file
diff --git a/docs/services/configuration.mdx b/docs/services/configuration.mdx
index e4edc8b3..8dedc9c6 100644
--- a/docs/services/configuration.mdx
+++ b/docs/services/configuration.mdx
@@ -403,6 +403,55 @@ if err := server.NewRestate().
```
+### Spring Boot configuration (Java/Kotlin)
+
+If you use the Spring Boot integration, you can configure services via `application.properties` or `application.yml` without modifying code.
+
+**Per-service configuration:**
+
+```properties application.properties
+# Configuration for a service named "MyService"
+restate.components.MyService.inactivity-timeout=10m
+restate.components.MyService.abort-timeout=1m
+restate.components.MyService.idempotency-retention=7d
+restate.components.MyService.journal-retention=1d
+restate.components.MyService.ingress-private=false
+restate.components.MyService.enable-lazy-state=true
+restate.components.MyService.documentation=My service description
+restate.components.MyService.metadata.version=1.0
+restate.components.MyService.retry-policy.initial-interval=100ms
+restate.components.MyService.retry-policy.exponentiation-factor=2.0
+restate.components.MyService.retry-policy.max-interval=10s
+restate.components.MyService.retry-policy.max-attempts=10
+restate.components.MyService.retry-policy.on-max-attempts=PAUSE
+
+# Per-handler configuration
+restate.components.MyService.handlers.myHandler.inactivity-timeout=5m
+restate.components.MyService.handlers.myHandler.ingress-private=true
+```
+
+**Global defaults (since SDK v2.7.0):**
+
+Top-level `restate.*` properties act as defaults for all registered services.
+Per-service `restate.components..*` properties override them on a per-service basis:
+
+```properties application.properties
+# Applied to all services by default
+restate.journal-retention=PT48H
+restate.inactivity-timeout=PT10M
+restate.retry-policy.max-attempts=5
+
+# Overrides just for MyService
+restate.components.MyService.journal-retention=PT72H
+restate.components.MyService.ingress-private=true
+```
+
+Supported global defaults: `documentation`, `metadata`, `inactivity-timeout`, `abort-timeout`, `idempotency-retention`, `workflow-retention`, `journal-retention`, `ingress-private`, `enable-lazy-state`, `retry-policy`.
+
+
+ The `executor` property is global-only and configured via `restate.executor` (Java only).
+
+
### Override configuration
You can override the service options configured in your code via the UI or CLI.
diff --git a/restate-plugin/skills/building-restate-services/references/java/api-and-pitfalls.md b/restate-plugin/skills/building-restate-services/references/java/api-and-pitfalls.md
index 740b63dd..1e647dbe 100644
--- a/restate-plugin/skills/building-restate-services/references/java/api-and-pitfalls.md
+++ b/restate-plugin/skills/building-restate-services/references/java/api-and-pitfalls.md
@@ -426,6 +426,22 @@ Note: the Java SDK uses `TerminalException`, NOT `TerminalError` (which is used
Any other exception type causes automatic retries with exponential backoff. For retry policy configuration, refer to the retry guide.
+### TerminalException metadata
+
+You can attach a string metadata map to a `TerminalException`. The metadata is propagated to callers and accessible via `getMetadata()`. Requires Restate Server >= 1.6.
+
+```java
+throw new TerminalException(
+ "Something went wrong", Map.of("correlationId", correlationId));
+```
+
+Callers can read the metadata:
+
+```java
+Map metadata = e.getMetadata();
+String correlationId = metadata.get("correlationId");
+```
+
---
## SDK Clients (External Invocations)
diff --git a/restate-plugin/src/references/java/api-and-pitfalls.md b/restate-plugin/src/references/java/api-and-pitfalls.md
index b0378537..1cedc900 100644
--- a/restate-plugin/src/references/java/api-and-pitfalls.md
+++ b/restate-plugin/src/references/java/api-and-pitfalls.md
@@ -288,6 +288,18 @@ Note: the Java SDK uses `TerminalException`, NOT `TerminalError` (which is used
Any other exception type causes automatic retries with exponential backoff. For retry policy configuration, refer to the retry guide.
+### TerminalException metadata
+
+You can attach a string metadata map to a `TerminalException`. The metadata is propagated to callers and accessible via `getMetadata()`. Requires Restate Server >= 1.6.
+
+```java {"CODE_LOAD::java/src/main/java/develop/ErrorHandling.java#metadata"}
+```
+
+Callers can read the metadata:
+
+```java {"CODE_LOAD::java/src/main/java/develop/ErrorHandling.java#catch_metadata"}
+```
+
---
## SDK Clients (External Invocations)
diff --git a/snippets/java/src/main/java/develop/ErrorHandling.java b/snippets/java/src/main/java/develop/ErrorHandling.java
index 5a21e8cf..97ec9dc8 100644
--- a/snippets/java/src/main/java/develop/ErrorHandling.java
+++ b/snippets/java/src/main/java/develop/ErrorHandling.java
@@ -2,6 +2,7 @@
import dev.restate.sdk.Context;
import dev.restate.sdk.common.TerminalException;
+import java.util.Map;
public class ErrorHandling {
@@ -12,4 +13,24 @@ public void errorHandling(Context ctx) {
//
}
+
+ public void errorHandlingWithMetadata(Context ctx, String correlationId) {
+
+ //
+ throw new TerminalException(
+ "Something went wrong", Map.of("correlationId", correlationId));
+ //
+
+ }
+
+ public void catchMetadata(Context ctx) {
+ try {
+ // ... some operation
+ } catch (TerminalException e) {
+ //
+ Map metadata = e.getMetadata();
+ String correlationId = metadata.get("correlationId");
+ //
+ }
+ }
}
diff --git a/snippets/kotlin/src/main/kotlin/develop/ErrorHandling.kt b/snippets/kotlin/src/main/kotlin/develop/ErrorHandling.kt
index 9e0d0e09..737194ca 100644
--- a/snippets/kotlin/src/main/kotlin/develop/ErrorHandling.kt
+++ b/snippets/kotlin/src/main/kotlin/develop/ErrorHandling.kt
@@ -9,4 +9,22 @@ class ErrorHandling {
throw TerminalException(500, "Something went wrong")
//
}
+
+ fun errorHandlingWithMetadata(ctx: Context, correlationId: String) {
+ //
+ throw TerminalException(
+ "Something went wrong", mapOf("correlationId" to correlationId))
+ //
+ }
+
+ fun catchMetadata(ctx: Context) {
+ try {
+ // ... some operation
+ } catch (e: TerminalException) {
+ //
+ val metadata = e.metadata
+ val correlationId = metadata["correlationId"]
+ //
+ }
+ }
}