diff --git a/README.md b/README.md
index f117d7a..5c64b34 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,43 @@
---
+## What Is AXME?
+
+AXME is a coordination infrastructure for durable execution of long-running intents across distributed systems.
+
+It provides a model for executing **intents** — requests that may take minutes, hours, or longer to complete — across services, agents, and human participants.
+
+## AXP — the Intent Protocol
+
+At the core of AXME is **AXP (Intent Protocol)** — an open protocol that defines contracts and lifecycle rules for intent processing.
+
+AXP can be implemented independently.
+The open part of the platform includes:
+
+- the protocol specification and schemas
+- SDKs and CLI for integration
+- conformance tests
+- implementation and integration documentation
+
+## AXME Cloud
+
+**AXME Cloud** is the managed service that runs AXP in production together with **The Registry** (identity and routing).
+
+It removes operational complexity by providing:
+
+- reliable intent delivery and retries
+- lifecycle management for long-running operations
+- handling of timeouts, waits, reminders, and escalation
+- observability of intent status and execution history
+
+State and events can be accessed through:
+
+- API and SDKs
+- event streams and webhooks
+- the cloud console
+
+---
+
## What You Can Do With This SDK
- **Send intents** — create typed, durable actions with delivery guarantees
@@ -19,16 +56,26 @@
## Install
-Add to your `pom.xml`:
+Build and install from source (local Maven repository):
+
+```bash
+git clone https://github.com/AxmeAI/axme-sdk-java.git
+cd axme-sdk-java
+mvn -q -DskipTests install
+```
+
+Then add to your `pom.xml`:
```xml
ai.axme
axme
- 0.1.0
+ 0.1.1
```
+Maven Central publication target: `ai.axme:axme`.
+
---
## Quickstart
@@ -42,9 +89,16 @@ import java.util.Map;
public class Quickstart {
public static void main(String[] args) throws Exception {
AxmeClient client = new AxmeClient(
- new AxmeClientConfig("https://gateway.axme.ai", "YOUR_API_KEY")
+ new AxmeClientConfig(
+ "https://gateway.axme.ai",
+ "YOUR_PLATFORM_API_KEY", // sent as x-api-key
+ "OPTIONAL_USER_OR_SESSION_TOKEN" // sent as Authorization: Bearer
+ )
);
+ // Check connectivity / discover available capabilities
+ System.out.println(client.getCapabilities(RequestOptions.none()));
+
// Send an intent
Map intent = client.createIntent(
Map.of(
@@ -71,9 +125,9 @@ The SDK covers the full public API surface:
---
-## Pagination, Filtering, and Sorting
+## Pagination and Cursor Flows
-List endpoints return paginated results. The SDK handles cursor-based pagination:
+Cursor-based list endpoints are available for inbox change streams:

@@ -155,12 +209,12 @@ Map sa = client.createServiceAccount(
// Issue a key
Map key = client.createServiceAccountKey(
(String) sa.get("id"),
- Map.of("name", "ci-key"),
+ Map.of(),
new RequestOptions(null, null)
);
// List service accounts
-client.listServiceAccounts("org_abc", null, RequestOptions.none());
+client.listServiceAccounts("org_abc", "", RequestOptions.none());
// Revoke a key
client.revokeServiceAccountKey(
diff --git a/pom.xml b/pom.xml
index 622adf5..0e4a6fc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,15 +6,15 @@
ai.axme
axme
- 0.1.0
- Axme Java SDK
+ 0.1.1
+ axme
Official Java SDK for Axme APIs and workflows.
https://github.com/AxmeAI/axme-sdk-java
MIT License
- https://opensource.org/license/mit
+ https://opensource.org/licenses/MIT
repo
@@ -89,7 +89,7 @@
org.apache.maven.plugins
maven-javadoc-plugin
- 3.7.0
+ 3.10.0
11
UTF-8
@@ -108,6 +108,12 @@
org.apache.maven.plugins
maven-gpg-plugin
3.2.7
+
+
+ --pinentry-mode
+ loopback
+
+
sign-artifacts
@@ -128,7 +134,7 @@
org.sonatype.central
central-publishing-maven-plugin
- 0.8.0
+ 0.9.0
true
central
diff --git a/src/main/java/dev/axme/sdk/AxmeClient.java b/src/main/java/dev/axme/sdk/AxmeClient.java
index 6ed306b..a7f1658 100644
--- a/src/main/java/dev/axme/sdk/AxmeClient.java
+++ b/src/main/java/dev/axme/sdk/AxmeClient.java
@@ -15,6 +15,7 @@
public final class AxmeClient {
private final String baseUrl;
private final String apiKey;
+ private final String actorToken;
private final HttpClient httpClient;
private final ObjectMapper objectMapper = new ObjectMapper();
@@ -25,6 +26,7 @@ public AxmeClient(AxmeClientConfig config) {
public AxmeClient(AxmeClientConfig config, HttpClient httpClient) {
this.baseUrl = config.getBaseUrl();
this.apiKey = config.getApiKey();
+ this.actorToken = config.getActorToken();
this.httpClient = httpClient;
}
@@ -648,8 +650,14 @@ private Map requestJson(
.method(method, payload == null ? HttpRequest.BodyPublishers.noBody() : HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(payload)))
.header("Accept", "application/json");
- String resolvedAuthorization = isBlank(options.getAuthorization()) ? "Bearer " + apiKey : options.getAuthorization();
- builder.header("Authorization", resolvedAuthorization);
+ builder.header("x-api-key", apiKey);
+ String resolvedAuthorization = options.getAuthorization();
+ if (isBlank(resolvedAuthorization) && !isBlank(actorToken)) {
+ resolvedAuthorization = "Bearer " + actorToken;
+ }
+ if (!isBlank(resolvedAuthorization)) {
+ builder.header("Authorization", resolvedAuthorization);
+ }
if (payload != null) {
builder.header("Content-Type", "application/json");
diff --git a/src/main/java/dev/axme/sdk/AxmeClientConfig.java b/src/main/java/dev/axme/sdk/AxmeClientConfig.java
index a5521e4..8cc41c6 100644
--- a/src/main/java/dev/axme/sdk/AxmeClientConfig.java
+++ b/src/main/java/dev/axme/sdk/AxmeClientConfig.java
@@ -3,16 +3,33 @@
public final class AxmeClientConfig {
private final String baseUrl;
private final String apiKey;
+ private final String actorToken;
public AxmeClientConfig(String baseUrl, String apiKey) {
+ this(baseUrl, apiKey, null, null);
+ }
+
+ public AxmeClientConfig(String baseUrl, String apiKey, String actorToken) {
+ this(baseUrl, apiKey, actorToken, null);
+ }
+
+ public AxmeClientConfig(String baseUrl, String apiKey, String actorToken, String bearerToken) {
if (baseUrl == null || baseUrl.trim().isEmpty()) {
throw new IllegalArgumentException("baseUrl is required");
}
if (apiKey == null || apiKey.trim().isEmpty()) {
throw new IllegalArgumentException("apiKey is required");
}
+ String normalizedActorToken = actorToken == null ? null : actorToken.trim();
+ String normalizedBearerToken = bearerToken == null ? null : bearerToken.trim();
+ if (isNonBlank(normalizedActorToken)
+ && isNonBlank(normalizedBearerToken)
+ && !normalizedActorToken.equals(normalizedBearerToken)) {
+ throw new IllegalArgumentException("actorToken and bearerToken must match when both are provided");
+ }
this.baseUrl = trimTrailingSlash(baseUrl.trim());
this.apiKey = apiKey.trim();
+ this.actorToken = isNonBlank(normalizedActorToken) ? normalizedActorToken : normalizedBearerToken;
}
public String getBaseUrl() {
@@ -23,6 +40,14 @@ public String getApiKey() {
return apiKey;
}
+ public String getActorToken() {
+ return actorToken;
+ }
+
+ private static boolean isNonBlank(String value) {
+ return value != null && !value.isBlank();
+ }
+
private static String trimTrailingSlash(String value) {
if (value.endsWith("/")) {
return value.substring(0, value.length() - 1);
diff --git a/src/test/java/dev/axme/sdk/AxmeClientTest.java b/src/test/java/dev/axme/sdk/AxmeClientTest.java
index 4a608d9..a1fa333 100644
--- a/src/test/java/dev/axme/sdk/AxmeClientTest.java
+++ b/src/test/java/dev/axme/sdk/AxmeClientTest.java
@@ -1,6 +1,7 @@
package dev.axme.sdk;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.fasterxml.jackson.core.type.TypeReference;
@@ -42,7 +43,7 @@ void registerNickSendsPayloadAndHeaders() throws Exception {
RecordedRequest request = server.takeRequest();
assertEquals("POST", request.getMethod());
assertEquals("/v1/users/register-nick", request.getPath());
- assertEquals("Bearer token", request.getHeader("Authorization"));
+ assertEquals("token", request.getHeader("x-api-key"));
assertEquals("register-1", request.getHeader("Idempotency-Key"));
Map body =
@@ -51,6 +52,31 @@ void registerNickSendsPayloadAndHeaders() throws Exception {
assertTrue((Boolean) response.get("ok"));
}
+ @Test
+ void clientSendsConfiguredActorToken() throws Exception {
+ server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"ok\":true,\"available\":true}"));
+
+ AxmeClient actorClient =
+ new AxmeClient(new AxmeClientConfig(server.url("/").toString(), "platform-token", "actor-token"));
+ actorClient.checkNick("@partner.user", RequestOptions.none());
+
+ RecordedRequest request = server.takeRequest();
+ assertEquals("platform-token", request.getHeader("x-api-key"));
+ assertEquals("Bearer actor-token", request.getHeader("Authorization"));
+ }
+
+ @Test
+ void configRejectsConflictingActorTokenAliases() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ new AxmeClientConfig(
+ "https://api.axme.test",
+ "platform-token",
+ "actor-a",
+ "actor-b"));
+ }
+
@Test
void checkNickSendsQueryParameter() throws Exception {
server.enqueue(
@@ -190,7 +216,13 @@ void intentLifecycleAndControlEndpointsAreReachable() throws Exception {
"it_123",
Map.of("controls_patch", Map.of("timeout_seconds", 120), "expected_policy_generation", 5),
RequestOptions.none());
- assertEquals("/v1/intents/it_123/controls", server.takeRequest().getPath());
+ RecordedRequest controlsRequest = server.takeRequest();
+ assertEquals("/v1/intents/it_123/controls", controlsRequest.getPath());
+ Map controlsBody =
+ objectMapper.readValue(controlsRequest.getBody().readUtf8(), new TypeReference