Key Takeaways
- Java receiving Android SMS gateway API webhooks means a Spring (or servlet) endpoint that verifies X-SmsGateway-Signature on the raw body, then ACKs 200 fast.
- There is no official Maven “Complete Java SDK.” Use HttpClient / servlet APIs. Confirm headers in Developer Center.
- message.delivered is DLR. message.received is two-way inbound. Different parsers.
- Deduplicate on X-SmsGateway-Event-Id. Retries reuse it.
- Do not process OTP guess logic inline on the webhook thread.
Java: Receive Android SMS Gateway API Webhooks is a Spring/servlet pattern: raw body, HMAC, fast 200. Hub: Android SMS gateway API. Signing notes live with the OpenAPI webhooks key in the Developer Center. Two-way product: two-way SMS.
You bring the Android and airtime. Devices and send volume.
v1=HMAC-SHA256(ts.body)
Pretty-printed JSON is a signature mismatch waiting to happen.
A servlet is not an SMSC
Your controller records what already happened on the SIM. It does not send. Delivery reports.
If you HMAC a Map you just rebuilt, you will “fix” production by turning verification off. Read the bytes.
How to receive webhooks in Java
- Register the HTTPS URL (OpenAPI webhooks).
- Read raw bytes. Timestamp header + body.
- HMAC-SHA256, constant-time compare to
v1=hex. - Reject skew > ~5 minutes. Dedupe Event-Id.
- 200, then a queue for CRM / OTP state.
Servlet/filter background: Spring Web MVC.
HMAC on the raw body
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 = "v1=" + HexFormat.of().formatHex(digest);
// MessageDigest.isEqual(expected.getBytes(UTF_8), headerCandidate.getBytes(UTF_8));Conceptual. Header names and encoding must match Developer Center — this is not a packaged SDK.
DLR vs inbound
| type | Means | Your job |
|---|---|---|
| message.delivered | Operator DLR (when present) | Mark OTP/order sent |
| message.failed | Send path failed | Alert; do not infinite-retry OTP |
| message.received | Inbound on the SIM | Thread / STOP / agent |
Inbound payload shape
{
"id": "evt_01K2F8QW3N4RXB7M",
"type": "message.received",
"createdAt": "2026-08-12T14:04:09Z",
"apiVersion": "2026-08-12",
"data": {
"message": {
"id": 41822,
"number": "+14155552671",
"text": "Yes, please reschedule to Thursday.",
"status": "Received",
"deviceId": 3,
"receivedAt": "2026-08-12T14:04:09Z"
}
}
}PHP sibling for the same headers: webhook in-depth.
HttpClient, not a Java SDK product
Sending still uses POST /messages with Bearer — same as every other language sample. Do not NuGet/Maven-brand this as a complete SDK. C# HTTPS samples · PHP HTTPS samples.
Next steps
Point a staging URL, send a staff inbound, watch Event-Id land once. Install the app. Canaries use 300 lifetime SMS — not unmetered radio.
Related product pages
Jump to the live product docs for this topic—not another long-form article.
- SMS webhook integrationInbound and status events
- SMS API documentationLive endpoint reference
- device and SMS volume pricingPlans and allowances
- Android SMS gateway product guideDefinition, product, and how to buy




