Java integration
Send WhatsApp messages with Java
A complete, runnable Java example for sending WhatsApp messages through the SPWP REST API. There is no SDK to install — Java talks to the API over ordinary HTTP, so you can paste this straight into an existing project.
About 5 minutes to your first delivered message
1. Setup
Create an API key in the dashboard under Accounts → your account → API keys, then make it available to your Java code as an environment variable. Never commit the key or ship it in client-side code.
# Java 11+ includes java.net.http.HttpClient — no dependency required.
export SPWP_API_KEY="wak_live_your_key_here"2. Send a text message
One POST with a recipient and a body. The API replies 202 Accepted with a message id in QUEUED state; delivery happens asynchronously and you can track it by id or webhook.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class SpwpClient {
private static final String API_BASE = "https://api.spwp.app/api/v1";
private final HttpClient http = HttpClient.newHttpClient();
public String sendText(String to, String text) throws Exception {
String payload = """
{"to": "%s", "text": "%s"}
""".formatted(to, text);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_BASE + "/public/messages/text"))
.header("X-API-Key", System.getenv("SPWP_API_KEY"))
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(10))
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
HttpResponse<String> response =
http.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new IllegalStateException("SPWP error: " + response.body());
}
return response.body();
}
}3. Send an image, document or other media
Media endpoints take the same shape, with a source that is either a public HTTPS URL we fetch server-side or a base64 data URL you upload inline. Swap the path segment for video, audio, document or location.
public String sendImage(String to, String source, String caption) throws Exception {
String payload = """
{"to": "%s", "source": "%s", "caption": "%s"}
""".formatted(to, source, caption);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_BASE + "/public/messages/image"))
.header("X-API-Key", System.getenv("SPWP_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
return http.send(request, HttpResponse.BodyHandlers.ofString()).body();
}4. Receive replies and delivery receipts
Inbound messages and status changes arrive as signed POSTs to your webhook URL. Recompute the HMAC over the timestamp and the raw request body — re-serialised JSON will not match — and reject anything older than five minutes.
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;
public final class SpwpSignature {
public static boolean isValid(String rawBody, String timestamp,
String signatureHeader, String secret)
throws Exception {
String v1 = signatureHeader.replaceAll(".*v1=([a-f0-9]+).*", "$1");
// Replay protection: five-minute window.
long age = Math.abs(System.currentTimeMillis() - Long.parseLong(timestamp));
if (age > 5 * 60_000) return false;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] digest = mac.doFinal((timestamp + "." + rawBody).getBytes(StandardCharsets.UTF_8));
String expected = HexFormat.of().formatHex(digest);
return MessageDigest.isEqual(
expected.getBytes(StandardCharsets.UTF_8),
v1.getBytes(StandardCharsets.UTF_8));
}
}Error handling
Errors carry a non-2xx status and a stable code string you can branch on: VALIDATION_FAILED, INVALID_API_KEY, ACCOUNT_NOT_CONNECTED, RATE_LIMITED, MEDIA_FETCH_FAILED. Retry on 429 while honouring Retry-After; treat 401 as a configuration problem rather than a transient one.
Next steps
The full reference covers every endpoint, field and error code, plus the webhook payload shapes.