OpenWA Java SDK
Official Java client for the OpenWA WhatsApp API Gateway.
Hand-written against the exact API surface (paths, DTOs, response shapes) and unit-tested with a mock HTTP transport that asserts on the precise request URL, method, and body — so contract drift is caught at test time. Synchronous, Java 17+, one runtime dependency (Gson).
Install
Maven
<dependency>
<groupId>com.rmyndharis</groupId>
<artifactId>openwa</artifactId>
<version>0.5.0</version>
</dependency>
Gradle
implementation 'com.rmyndharis:openwa:0.5.0'
Quickstart
import com.rmyndharis.openwa.OpenWAClient;
import com.rmyndharis.openwa.model.MessageResponse;
import com.rmyndharis.openwa.model.SendTextRequest;
OpenWAClient client = new OpenWAClient("http://localhost:2785", "owa_k1_…");
client.sessions.start("my-session");
MessageResponse result = client.messages.sendText("my-session",
SendTextRequest.builder()
.chatId("628123456789@c.us")
.text("Hello from the OpenWA Java SDK!")
.build());
System.out.println(result.messageId());
For full control over configuration (timeout, default headers, a custom
transport), build a ClientConfig:
import com.rmyndharis.openwa.ClientConfig;
import java.time.Duration;
OpenWAClient client = new OpenWAClient(ClientConfig.builder()
.baseUrl("https://wa.example.com")
.apiKey("owa_k1_…")
.timeout(Duration.ofSeconds(15))
.build());
Resources
The client exposes the same fluent resource surface as the JavaScript, Python, and PHP SDKs:
sessions · messages · contacts · groups · webhooks · chats ·
labels · channels · catalog · status · templates · health · search ·
profile · calls · media,
plus client.auth().
Operator-only modules (docker, metrics, infra, plugins, mcp) are
intentionally not exposed; all user-facing resources are.
Error handling
Errors are a typed, unchecked hierarchy — branch with instanceof or on
.status():
import com.rmyndharis.openwa.errors.OpenWAConflictError;
import com.rmyndharis.openwa.errors.OpenWANotFoundError;
try {
client.messages.sendText("my-session", body);
} catch (OpenWAConflictError e) {
// 409 — engine not ready
} catch (OpenWANotFoundError e) {
// 404 — session or chat not found
}
| Class | HTTP | Meaning |
|---|---|---|
OpenWAAuthError |
401 | Missing or invalid API key |
OpenWAForbiddenError |
403 | API key role insufficient |
OpenWANotFoundError |
404 | Resource not found |
OpenWAConflictError |
409 | Engine not ready |
OpenWARateLimitError |
429 | Rate limited |
OpenWANotImplementedError |
501 | Active engine does not support the call |
OpenWAServiceUnavailableError |
503 | Engine did not confirm in time — the only retryable one |
OpenWAApiError |
— | Any other non-2xx (carries .status()) |
OpenWATimeoutError |
— | Request exceeded the configured timeout |
All extend OpenWAError (a RuntimeException).
Reliability & security
- Use HTTPS in production. The API key is sent as
X-API-Keyon every request and is bearer-equivalent — never send it over plaintexthttp://outside local development. - No automatic retries. A failed request throws immediately; wrap calls in
your own backoff if you need retries (especially for
429). Inject a customHttpTransportfor retry or observability middleware. - Redirects are never followed. A
3xxsurfaces as anOpenWAApiErrorrather than being followed, so the API key is never re-sent to a redirect target. - Default per-request timeout is 30 s (configurable). Path segments (chat /
message ids) are percent-encoded; a base-URL path prefix (e.g. behind a proxy
at
/v1) is preserved.
Development
cd sdk/java
mvn -B verify # compile + run the full test suite
Tests inject a recording HttpTransport and assert on the exact path — so the
regression that would ship a broken messages/text path (the real path is
messages/send-text) can never recur silently.
Releasing
Publishing to Maven Central is done by the
java-sdk-release.yml workflow,
which deploys with mvn -B -Prelease deploy. The release profile attaches the
sources/javadoc jars, GPG-signs every artifact, and auto-publishes via the
Sonatype Central Publishing plugin — a plain mvn verify never runs any of it.
One-time setup (repository secrets):
MAVEN_CENTRAL_USERNAME/MAVEN_CENTRAL_PASSWORD— the two halves of a Sonatype Central Portal user token for the verifiedcom.rmyndharisnamespace.GPG_PRIVATE_KEY— ASCII-armored signing key.GPG_PASSPHRASE— passphrase for that key.
All four secrets are checked before anything is built, and a missing one fails the run. That is deliberate: skipping the deploy and reporting green is indistinguishable from a real release in the run list, so configure the secrets before tagging rather than tagging to see what happens.
Cutting a release:
- Bump
<version>inpom.xmland land it onmain. - Tag that commit
java-sdk-v<version>(e.g.java-sdk-v0.5.0) and push the tag. The SDK has its own version line — the monorepo'sv*tags are the app version and never trigger an SDK publish. - The workflow builds, signs, and publishes; Central syncs within a few hours.