diff --git a/examples/list-decisions/src/main/java/com/getaxonflow/examples/ListDecisions.java b/examples/list-decisions/src/main/java/com/getaxonflow/examples/ListDecisions.java index f9d5c47..247f3f9 100644 --- a/examples/list-decisions/src/main/java/com/getaxonflow/examples/ListDecisions.java +++ b/examples/list-decisions/src/main/java/com/getaxonflow/examples/ListDecisions.java @@ -14,7 +14,9 @@ // mvn -q compile exec:java // // Optional filters via env: -// AXONFLOW_LIST_DECISION allow|deny|require_approval +// AXONFLOW_LIST_DECISION allowed|blocked|redacted|needs_approval|error +// (canonical audit verdicts, platform 9.0.0+; +// pre-9.0.0 allow|deny|require_approval now 400) // AXONFLOW_LIST_POLICY_ID e.g. sys_sqli_stacked_drop // AXONFLOW_LIST_LIMIT integer (server-capped per tier) package com.getaxonflow.examples; diff --git a/src/main/java/com/getaxonflow/sdk/AxonFlow.java b/src/main/java/com/getaxonflow/sdk/AxonFlow.java index 2ef116f..f642fc6 100644 --- a/src/main/java/com/getaxonflow/sdk/AxonFlow.java +++ b/src/main/java/com/getaxonflow/sdk/AxonFlow.java @@ -756,7 +756,7 @@ public CompletableFuture explainDecisionAsync(String decisi *
{@code
    * try {
    *     List decisions = axonflow.listDecisions(
-   *         ListDecisionsOptions.builder().decision("deny").limit(10).build());
+   *         ListDecisionsOptions.builder().decision("blocked").limit(10).build());
    *     for (DecisionSummary d : decisions) {
    *         System.out.println(d.getDecisionId() + " " + d.getDecision());
    *     }
diff --git a/src/main/java/com/getaxonflow/sdk/types/DecisionExplanation.java b/src/main/java/com/getaxonflow/sdk/types/DecisionExplanation.java
index c7a0247..f2a797a 100644
--- a/src/main/java/com/getaxonflow/sdk/types/DecisionExplanation.java
+++ b/src/main/java/com/getaxonflow/sdk/types/DecisionExplanation.java
@@ -88,6 +88,7 @@ public List getMatchedRules() {
     return matchedRules;
   }
 
+  /** Canonical audit verdict: allowed | blocked | redacted | needs_approval | error (9.0.0+). */
   public String getDecision() {
     return decision;
   }
diff --git a/src/main/java/com/getaxonflow/sdk/types/DecisionSummary.java b/src/main/java/com/getaxonflow/sdk/types/DecisionSummary.java
index ab12458..e2edaea 100644
--- a/src/main/java/com/getaxonflow/sdk/types/DecisionSummary.java
+++ b/src/main/java/com/getaxonflow/sdk/types/DecisionSummary.java
@@ -63,7 +63,7 @@ public Instant getTimestamp() {
     return timestamp;
   }
 
-  /** allow | deny | require_approval */
+  /** Canonical audit verdict: allowed | blocked | redacted | needs_approval | error (9.0.0+). */
   public String getDecision() {
     return decision;
   }
diff --git a/src/main/java/com/getaxonflow/sdk/types/ListDecisionsOptions.java b/src/main/java/com/getaxonflow/sdk/types/ListDecisionsOptions.java
index 88c4da5..a06fbb0 100644
--- a/src/main/java/com/getaxonflow/sdk/types/ListDecisionsOptions.java
+++ b/src/main/java/com/getaxonflow/sdk/types/ListDecisionsOptions.java
@@ -11,8 +11,12 @@
  * Optional filters for {@code AxonFlow.listDecisions}.
  *
  * 

Every field is optional; null values are omitted from the URL so the - * platform applies its tier-default page. {@code decision} must be one of - * {@code "allow"}, {@code "deny"}, or {@code "require_approval"} when set. + * platform applies its tier-default page. {@code decision}, when set, must be one + * of the canonical audit verdicts {@code "allowed"}, {@code "blocked"}, + * {@code "redacted"}, {@code "needs_approval"}, or {@code "error"} (platform + * 9.0.0+); the pre-9.0.0 values {@code "allow"} / {@code "deny"} / + * {@code "require_approval"} are rejected with HTTP 400 by 9.0.0 (see + * https://docs.getaxonflow.com/docs/deployment/v8-to-v9-migration/). * {@code limit} is server-capped per tier; over-cap requests yield a 429 * with the V1 upgrade envelope (surfaced as {@link * com.getaxonflow.sdk.exceptions.RateLimitException} carrying upgrade info). @@ -21,7 +25,7 @@ * *

{@code
  * ListDecisionsOptions opts = ListDecisionsOptions.builder()
- *     .decision("deny")
+ *     .decision("blocked")
  *     .limit(10)
  *     .build();
  * List decisions = client.listDecisions(opts);
diff --git a/src/test/java/com/getaxonflow/sdk/DecisionExplainTest.java b/src/test/java/com/getaxonflow/sdk/DecisionExplainTest.java
index 453d553..0647968 100644
--- a/src/test/java/com/getaxonflow/sdk/DecisionExplainTest.java
+++ b/src/test/java/com/getaxonflow/sdk/DecisionExplainTest.java
@@ -26,7 +26,7 @@ class DecisionExplainTest {
       "{"
           + "\"decision_id\": \"dec_wf1_step2\","
           + "\"timestamp\": \"2026-04-17T12:00:00Z\","
-          + "\"decision\": \"deny\","
+          + "\"decision\": \"blocked\","
           + "\"reason\": \"SQL injection detected\","
           + "\"risk_level\": \"high\","
           + "\"policy_matches\": [{"
@@ -87,7 +87,7 @@ void parsesFullPayload() {
     DecisionExplanation exp = axonflow.explainDecision("dec_wf1_step2");
 
     assertThat(exp.getDecisionId()).isEqualTo("dec_wf1_step2");
-    assertThat(exp.getDecision()).isEqualTo("deny");
+    assertThat(exp.getDecision()).isEqualTo("blocked");
     assertThat(exp.getReason()).isEqualTo("SQL injection detected");
     assertThat(exp.getRiskLevel()).isEqualTo("high");
     assertThat(exp.getPolicyMatches()).hasSize(1);
@@ -108,7 +108,7 @@ void surfacesRequestContext() {
         "{"
             + "\"decision_id\": \"dec-ctx\","
             + "\"timestamp\": \"2026-05-30T12:00:00Z\","
-            + "\"decision\": \"deny\","
+            + "\"decision\": \"blocked\","
             + "\"reason\": \"\","
             + "\"policy_matches\": [],"
             + "\"override_available\": false,"
@@ -133,7 +133,7 @@ void surfacesRequestContext() {
   void contextAbsentDefaults() {
     String body =
         "{\"decision_id\":\"dec-1\",\"timestamp\":\"2026-04-17T12:00:00Z\","
-            + "\"decision\":\"allow\",\"reason\":\"\",\"policy_matches\":[],"
+            + "\"decision\":\"allowed\",\"reason\":\"\",\"policy_matches\":[],"
             + "\"override_available\":false,\"historical_hit_count_session\":0}";
     stubFor(
         get(urlEqualTo("/api/v1/decisions/dec-1/explain"))
@@ -240,7 +240,7 @@ void decisionExplanationGetters() {
             java.time.Instant.now(),
             null, // policyMatches null should default to empty
             null,
-            "allow",
+            "allowed",
             "",
             null,
             false,
diff --git a/src/test/java/com/getaxonflow/sdk/ListDecisionsTest.java b/src/test/java/com/getaxonflow/sdk/ListDecisionsTest.java
index f3525b4..639fb01 100644
--- a/src/test/java/com/getaxonflow/sdk/ListDecisionsTest.java
+++ b/src/test/java/com/getaxonflow/sdk/ListDecisionsTest.java
@@ -45,23 +45,23 @@ void happyPath() {
                     .withBody(
                         "{\"decisions\":["
                             + "{\"decision_id\":\"dec-1\",\"timestamp\":\"2026-05-07T12:00:00Z\","
-                            + "\"decision\":\"deny\",\"policy_id\":\"pol-sqli\","
+                            + "\"decision\":\"blocked\",\"policy_id\":\"pol-sqli\","
                             + "\"tool_signature\":\"postgres.query\"},"
                             + "{\"decision_id\":\"dec-2\",\"timestamp\":\"2026-05-07T11:00:00Z\","
-                            + "\"decision\":\"allow\",\"policy_id\":\"pol-default\","
+                            + "\"decision\":\"allowed\",\"policy_id\":\"pol-default\","
                             + "\"tool_signature\":\"github.status\"},"
                             + "{\"decision_id\":\"dec-3\",\"timestamp\":\"2026-05-07T10:00:00Z\","
-                            + "\"decision\":\"require_approval\",\"policy_id\":\"pol-amount\","
+                            + "\"decision\":\"needs_approval\",\"policy_id\":\"pol-amount\","
                             + "\"tool_signature\":\"stripe.charge\"}"
                             + "]}")));
 
     List got = axonflow.listDecisions(null);
     assertThat(got).hasSize(3);
     assertThat(got.get(0).getDecisionId()).isEqualTo("dec-1");
-    assertThat(got.get(0).getDecision()).isEqualTo("deny");
+    assertThat(got.get(0).getDecision()).isEqualTo("blocked");
     assertThat(got.get(0).getPolicyId()).isEqualTo("pol-sqli");
     assertThat(got.get(0).getToolSignature()).isEqualTo("postgres.query");
-    assertThat(got.get(2).getDecision()).isEqualTo("require_approval");
+    assertThat(got.get(2).getDecision()).isEqualTo("needs_approval");
   }
 
   @Test
@@ -76,11 +76,11 @@ void surfacesRequestContext() {
                     .withBody(
                         "{\"decisions\":["
                             + "{\"decision_id\":\"dec-ctx\",\"timestamp\":\"2026-05-30T12:00:00Z\","
-                            + "\"decision\":\"deny\",\"context\":{"
+                            + "\"decision\":\"blocked\",\"context\":{"
                             + "\"x_ai_agent\":\"refund-bot\",\"x_session_id\":\"sess-42\","
                             + "\"x_leader_identity\":\"ops-lead\"}},"
                             + "{\"decision_id\":\"dec-noctx\",\"timestamp\":\"2026-05-30T11:00:00Z\","
-                            + "\"decision\":\"allow\"}"
+                            + "\"decision\":\"allowed\"}"
                             + "]}")));
 
     List got = axonflow.listDecisions(null);
@@ -100,7 +100,7 @@ void filterSerialization() {
     stubFor(
         get(urlPathEqualTo("/api/v1/decisions"))
             .withQueryParam("since", equalTo("2026-05-07T00:00:00Z"))
-            .withQueryParam("decision", equalTo("deny"))
+            .withQueryParam("decision", equalTo("blocked"))
             .withQueryParam("policy_id", equalTo("pol-sqli"))
             .withQueryParam("tool_signature", equalTo("postgres.query"))
             .withQueryParam("limit", equalTo("25"))
@@ -109,7 +109,7 @@ void filterSerialization() {
     ListDecisionsOptions opts =
         ListDecisionsOptions.builder()
             .since(Instant.parse("2026-05-07T00:00:00Z"))
-            .decision("deny")
+            .decision("blocked")
             .policyId("pol-sqli")
             .toolSignature("postgres.query")
             .limit(25)
@@ -123,7 +123,7 @@ void filterSerialization() {
   void omitsUnsetFilters() {
     stubFor(
         get(urlPathEqualTo("/api/v1/decisions"))
-            .withQueryParam("decision", equalTo("deny"))
+            .withQueryParam("decision", equalTo("blocked"))
             // wiremock fails the test if the URL contains any of these:
             .withQueryParam("policy_id", absent())
             .withQueryParam("tool_signature", absent())
@@ -131,7 +131,7 @@ void omitsUnsetFilters() {
             .withQueryParam("since", absent())
             .willReturn(aResponse().withStatus(200).withBody("{\"decisions\":[]}")));
 
-    axonflow.listDecisions(ListDecisionsOptions.builder().decision("deny").build());
+    axonflow.listDecisions(ListDecisionsOptions.builder().decision("blocked").build());
   }
 
   @Test
@@ -219,7 +219,7 @@ void forwardCompat() {
                         "{\"decisions\":[{"
                             + "\"decision_id\":\"dec-fwd\","
                             + "\"timestamp\":\"2026-05-07T12:00:00Z\","
-                            + "\"decision\":\"deny\","
+                            + "\"decision\":\"blocked\","
                             + "\"policy_id\":\"pol-x\","
                             + "\"tool_signature\":\"tool-x\","
                             + "\"policy_version\":7,"
@@ -244,7 +244,7 @@ void summaryMinimalShape() {
                         "{\"decisions\":[{"
                             + "\"decision_id\":\"dec-min\","
                             + "\"timestamp\":\"2026-05-07T12:00:00Z\","
-                            + "\"decision\":\"deny\""
+                            + "\"decision\":\"blocked\""
                             + "}]}")));
 
     List got = axonflow.listDecisions(null);
@@ -263,8 +263,8 @@ void buildQueryEmpty() {
   @DisplayName("buildListDecisionsQuery — partial options omit None fields")
   void buildQueryPartial() {
     ListDecisionsOptions opts =
-        ListDecisionsOptions.builder().decision("deny").limit(7).build();
-    assertThat(AxonFlow.buildListDecisionsQuery(opts)).isEqualTo("?decision=deny&limit=7");
+        ListDecisionsOptions.builder().decision("blocked").limit(7).build();
+    assertThat(AxonFlow.buildListDecisionsQuery(opts)).isEqualTo("?decision=blocked&limit=7");
   }
 
   @Test