Libraries and schemas for interacting with the Akka Platform API. The API has two planes:
- Federation plane — a gRPC API for managing projects, organizations, billing, users, and auth. Served at
api.kalix.io:443. - Control plane — a REST API (OpenAPI) for managing resources within a project (services, routes, secrets, etc.). Served at a region-specific endpoint, e.g.
https://api.gcp-us-east1.akka.io.
All API calls require a short-lived access token obtained by one of the following mechanisms.
Refresh token — a long-lived token exchanged for a short-lived access token via the Auth API. There are two kinds:
- User token — grants access to all projects belonging to your user account:
akka auth token create --description 'My refresh token' - Service token — grants access to a single project; suitable for CI/CD pipelines:
akka service-account token create --description 'My service token'
Store the token value securely. It is shown only once.
OAuth token exchange (RFC 8693) — exchange an existing identity token (e.g. a GitHub Actions OIDC token or a workload identity token) for an Akka access token. No long-lived Akka credential needs to be stored. The audience parameter identifies the configured OpenID Connect identity provider and is required.
The java-client module provides a ready-to-use SDK that handles token acquisition and caching, gRPC channel management, and automatic routing of control plane requests to the correct regional endpoint.
Refresh token — environment variable
Set AKKA_TOKEN before starting your application. Optionally set AKKA_API_HOST to override the federation plane host (defaults to api.kalix.io:443).
export AKKA_TOKEN=<your-refresh-token>AkkaPlatformSdk sdk = AkkaPlatformSdk.create();Refresh token — automatic fallback to the Akka CLI
If AKKA_TOKEN is not set and the akka CLI is installed and logged in, the SDK will run akka config get refresh-token (and akka config get api-server-host for non-default installations) automatically. No code changes are needed.
Refresh token — programmatic
AkkaPlatformSdk sdk = AkkaPlatformSdk.create(
AkkaPlatformSdkConfig.withRefreshToken("your-refresh-token"));To also override the federation plane host:
AkkaPlatformSdk sdk = AkkaPlatformSdk.create(
AkkaPlatformSdkConfig.withRefreshToken("your-refresh-token", "api.kalix.io:443"));OAuth token exchange — environment variables
Set AKKA_OAUTH_TOKEN (or AKKA_OAUTH_TOKEN_FILE to point to a file containing the token) and AKKA_OAUTH_TOKEN_AUDIENCE. When either OAuth variable is present it takes precedence over AKKA_TOKEN. If the audience variable is missing the SDK throws at startup.
export AKKA_OAUTH_TOKEN="$(cat /var/run/secrets/token)" # or a static value
export AKKA_OAUTH_TOKEN_AUDIENCE="regions/gcp-us-east1"
# -- or --
export AKKA_OAUTH_TOKEN_FILE=/var/run/secrets/token
export AKKA_OAUTH_TOKEN_AUDIENCE="regions/gcp-us-east1"AkkaPlatformSdk sdk = AkkaPlatformSdk.create(); // picks up the env vars automaticallyWhen AKKA_OAUTH_TOKEN_FILE is used the file is re-read on every token exchange, so rotating the file contents is sufficient to rotate the credential without restarting the application.
Akka services: if workload identity has been enabled for an Akka service, these environment variables are injected automatically into the service at runtime. Calling
AkkaPlatformSdk.create()with no further configuration is sufficient.
OAuth token exchange — programmatic
// Fixed token value
AkkaPlatformSdk sdk = AkkaPlatformSdk.create(
AkkaPlatformSdkConfig.withOAuthToken(myOidcToken, "regions/gcp-us-east1"));
// File-based (re-read on every exchange)
AkkaPlatformSdk sdk = AkkaPlatformSdk.create(
AkkaPlatformSdkConfig.withOAuthTokenFile("/var/run/secrets/token", "regions/gcp-us-east1"));Custom token fetcher
For authentication mechanisms not covered above, implement both TokenConfig and TokenFetcher on the same class. TokenFetcher.getToken() must return a CompletableFuture<TokenWithExpiry> containing a bearer token and its expiry instant. The SDK caches the token until 60 seconds before expiry and then calls getToken() again.
public class MyTokenConfig implements TokenConfig, TokenFetcher {
@Override
public CompletableFuture<TokenWithExpiry> getToken() {
return fetchMyToken().thenApply(t ->
new TokenWithExpiry(t.value(), t.expiresAt()));
}
}
AkkaPlatformSdk sdk = AkkaPlatformSdk.create(
AkkaPlatformSdkConfig.of(new MyTokenConfig(), "api.kalix.io:443"));Projects are identified by a UUID internally. Use resolveProjectId to translate a friendly name to the UUID required by the control plane API:
sdk.resolveProjectId("my-project")
.thenAccept(projectId -> System.out.println("Project ID: " + projectId));To list all projects directly:
sdk.projects()
.listProjects(ListProjectsRequest.newBuilder().build())
.thenAccept(response ->
response.getProjectsList().forEach(p ->
System.out.println(p.getFriendlyName() + " → " + p.getName())));The control plane API is accessed via sdk.controlPlaneApi(). Each call is automatically routed to the project's primary region — the project UUID is parsed from the request URI, the region list is fetched and cached, and the request is forwarded to the correct regional endpoint.
AkkaControlPlaneApi api = sdk.controlPlaneApi();
sdk.resolveProjectId("my-project").thenCompose(projectId ->
api.getService("my-service", projectId)
).thenAccept(service ->
System.out.println(service.getMetadata().getName()
+ " — " + service.getStatus()));To list all services in a project:
sdk.resolveProjectId("my-project").thenCompose(projectId ->
api.listServices(projectId, null)
).thenAccept(list ->
list.getItems().forEach(s -> System.out.println(s.getMetadata().getName())));import io.akka.platformapi.controlplane.model.*;
Service service = new Service()
.metadata(new ObjectMeta()
.name("my-service"))
.spec(new ServiceSpec()
.containers(List.of(new ServiceSpecContainer()
.name("main")
.image("my-registry.example.com/my-image:1.0.0"))));
sdk.resolveProjectId("my-project").thenCompose(projectId ->
api.createService(projectId, service)
).thenAccept(created ->
System.out.println("Created: " + created.getMetadata().getName()));By default controlPlaneApi() routes to the primary region. To target a specific region (e.g. for multi-region projects):
// Region name format: "<region-id>" e.g. "gcp-us-east1"
AkkaControlPlaneApi regionalApi = sdk.controlPlaneApiForRegion("gcp-us-east1");If the specified region is not associated with the project identified in the request URI, the call will fail asynchronously with IllegalArgumentException.
Region information is cached. After a region configuration change, call sdk.clearRegionCache() to force a fresh lookup on the next request.
Close the SDK when your application shuts down to release the underlying gRPC channels and (if applicable) the managed ActorSystem:
sdk.close();The federation plane protobuf schemas are in schemas/federation-plane/protobuf/ and the control plane OpenAPI schema is at schemas/control-plane/openapi/api.json. The following describes the authentication and region discovery flow for clients that use these schemas without the Java SDK.
All requests require a short-lived access token. Exchange your refresh token for one by calling CreateAccessToken on the Auth service (api.kalix.io:443, TLS):
Service: kalix.api.auth.v1alpha.Auth
Method: CreateAccessToken
Request: CreateAccessTokenRequest {} (empty body)
Header: Authorization: Bearer <refresh-token>
The response AccessToken.token is your access token. Send it as Authorization: Bearer <access-token> on all subsequent requests. Tokens are short-lived — cache them and refresh before expire_time.
Control plane REST requests must be directed to the region-specific endpoint for the project, not to api.kalix.io. Discover the endpoint by calling ListProjects on the Projects service (authenticated with your access token):
Service: kalix.api.projects.v1alpha.Projects
Method: ListProjects
Request: ListProjectsRequest {}
Header: Authorization: Bearer <access-token>
Each Project in the response contains a regions list. Each Region has:
endpoint— the base URL for control plane REST requests, e.g.https://api.gcp-us-east1.akka.ioprimary—truefor the region that should be used by defaultname— the fully-qualified resource name, e.g.projects/<id>/regions/gcp-us-east1
In most cases you want the region where primary == true. Use its endpoint as the base URL for all control plane REST calls for that project:
GET https://api.gcp-us-east1.akka.io/apis/kalix.io/v1alpha1/namespaces/<project-id>/kalixservices
Authorization: Bearer <access-token>
If you already have the project ID, you can also call ListRegions directly with parent = "projects/<project-id>" instead of iterating through ListProjects.
Page through ListProjects using the next_page_token field in the response until it is empty, as projects are returned in pages.