Azure Communication Sms Java
MicrosoftOfficialCreated by Microsoft
- Skill
- Tool integration
- Automation and integrations
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The azure-communication-sms-java skill provides the necessary tools to send SMS messages from Java applications using Azure Communication Services. It supports sending messages to single or multiple recipients, handling delivery reports via Event Grid, and managing asynchronous operations. The SDK includes robust error handling and configuration options for authentication, including support for DefaultAzureCredential, connection strings, and AzureKeyCredential.
What it does
- Sends SMS messages to single or multiple phone numbers.
- Supports asynchronous SMS sending for non-blocking operations.
- Enables delivery reporting through Azure Event Grid integration.
- Provides custom tagging for message tracking and business context.
- Includes comprehensive error handling for request-level and message-level failures.
- Supports multiple authentication methods including Entra ID and connection strings.
When to use it
- When implementing SMS-based notifications or alerts in a Java application.
- When building OTP delivery systems for user authentication.
- When managing bulk SMS messaging campaigns.
- When you need to track message delivery status for critical communications.
Advantages
- Native Java support for Azure Communication Services.
- Flexible authentication options for local development and production environments.
- Built-in support for batch messaging to multiple recipients.
- Detailed response objects for granular error tracking per recipient.
Usage examples
- “How do I send a single SMS using the Azure Communication Services Java SDK?”
- The agent provides a code snippet demonstrating how to initialize the SmsClient and call the send method with a sender number, recipient number, and message body.
- “Show me how to send a bulk SMS message with delivery reports enabled.”
- The agent provides an example using SmsSendOptions to enable delivery reports and a list of recipients to send a message to multiple numbers simultaneously.
- “How can I handle errors when sending SMS messages in Java?”
- The agent explains how to catch HttpResponseException for request failures and how to inspect the SmsSendResult object to identify individual message delivery failures.
- “What are the best practices for using the Azure SMS Java SDK?”
- The agent lists recommendations such as using E.164 phone number formatting, enabling delivery reports for critical messages, and implementing retry logic for rate-limited requests.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/microsoft/azure-communication-sms-java/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip azure-communication-sms-java.zip -d .agents/skills
Documentation by Microsoft
Azure Communication SMS (Java)
Send SMS messages to single or multiple recipients with delivery reporting.
Installation
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-communication-sms</artifactId>
<version>1.2.0</version>
</dependency>Client Creation
import com.azure.communication.sms.SmsClient;
import com.azure.communication.sms.SmsClientBuilder;
import com.azure.core.credential.TokenCredential;
import com.azure.identity.AzureIdentityEnvVars;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.identity.ManagedIdentityCredentialBuilder;
// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
TokenCredential credential = new DefaultAzureCredentialBuilder()
.requireEnvVars(AzureIdentityEnvVars.AZURE_TOKEN_CREDENTIALS)
.build();
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/java/api/overview/azure/identity-readme?view=azure-java-stable#credential-classes
// TokenCredential credential = new ManagedIdentityCredentialBuilder().build();
// With DefaultAzureCredential (recommended)
SmsClient smsClient = new SmsClientBuilder()
.endpoint("https://<resource>.communication.azure.com")
.credential(credential)
.buildClient();
// With connection string
SmsClient smsClient = new SmsClientBuilder()
.connectionString("<connection-string>")
.buildClient();
// With AzureKeyCredential
import com.azure.core.credential.AzureKeyCredential;
SmsClient smsClient = new SmsClientBuilder()
.endpoint("https://<resource>.communication.azure.com")
.credential(new AzureKeyCredential("<access-key>"))
.buildClient();
// Async client
SmsAsyncClient smsAsyncClient = new SmsClientBuilder()
.connectionString("<connection-string>")
.buildAsyncClient();Send SMS to Single Recipient
import com.azure.communication.sms.models.SmsSendResult;
// Simple send
SmsSendResult result = smsClient.send(
"+14255550100", // From (your ACS phone number)
"+14255551234", // To
"Your verification code is 123456");
System.out.println("Message ID: " + result.getMessageId());
System.out.println("To: " + result.getTo());
System.out.println("Success: " + result.isSuccessful());
if (!result.isSuccessful()) {
System.out.println("Error: " + result.getErrorMessage());
System.out.println("Status: " + result.getHttpStatusCode());
}Send SMS to Multiple Recipients
import com.azure.communication.sms.models.SmsSendOptions;
import java.util.Arrays;
import java.util.List;
List<String> recipients = Arrays.asList(
"+14255551111",
"+14255552222",
"+14255553333"
);
// With options
SmsSendOptions options = new SmsSendOptions()
.setDeliveryReportEnabled(true)
.setTag("marketing-campaign-001");
Iterable<SmsSendResult> results = smsClient.sendWithResponse(
"+14255550100", // From
recipients, // To list
"Flash sale! 50% off today only.",
options,
Context.NONE
).getValue();
for (SmsSendResult result : results) {
if (result.isSuccessful()) {
System.out.println("Sent to " + result.getTo() + ": " + result.getMessageId());
} else {
System.out.println("Failed to " + result.getTo() + ": " + result.getErrorMessage());
}
}Send Options
SmsSendOptions options = new SmsSendOptions();
// Enable delivery reports (sent via Event Grid)
options.setDeliveryReportEnabled(true);
// Add custom tag for tracking
options.setTag("order-confirmation-12345");Response Handling
import com.azure.core.http.rest.Response;
Response<Iterable<SmsSendResult>> response = smsClient.sendWithResponse(
"+14255550100",
Arrays.asList("+14255551234"),
"Hello!",
new SmsSendOptions().setDeliveryReportEnabled(true),
Context.NONE
);
// Check HTTP response
System.out.println("Status code: " + response.getStatusCode());
System.out.println("Headers: " + response.getHeaders());
// Process results
for (SmsSendResult result : response.getValue()) {
System.out.println("Message ID: " + result.getMessageId());
System.out.println("Successful: " + result.isSuccessful());
if (!result.isSuccessful()) {
System.out.println("HTTP Status: " + result.getHttpStatusCode());
System.out.println("Error: " + result.getErrorMessage());
}
}Async Operations
import reactor.core.publisher.Mono;
SmsAsyncClient asyncClient = new SmsClientBuilder()
.connectionString("<connection-string>")
.buildAsyncClient();
// Send single message
asyncClient.send("+14255550100", "+14255551234", "Async message!")
.subscribe(
result -> System.out.println("Sent: " + result.getMessageId()),
error -> System.out.println("Error: " + error.getMessage())
);
// Send to multiple with options
SmsSendOptions options = new SmsSendOptions()
.setDeliveryReportEnabled(true);
asyncClient.sendWithResponse(
"+14255550100",
Arrays.asList("+14255551111", "+14255552222"),
"Bulk async message",
options)
.subscribe(response -> {
for (SmsSendResult result : response.getValue()) {
System.out.println("Result: " + result.getTo() + " - " + result.isSuccessful());
}
});Error Handling
import com.azure.core.exception.HttpResponseException;
try {
SmsSendResult result = smsClient.send(
"+14255550100",
"+14255551234",
"Test message"
);
// Individual message errors don't throw exceptions
if (!result.isSuccessful()) {
handleMessageError(result);
}
} catch (HttpResponseException e) {
// Request-level failures (auth, network, etc.)
System.out.println("Request failed: " + e.getMessage());
System.out.println("Status: " + e.getResponse().getStatusCode());
} catch (RuntimeException e) {
System.out.println("Unexpected error: " + e.getMessage());
}
private void handleMessageError(SmsSendResult result) {
int status = result.getHttpStatusCode();
String error = result.getErrorMessage();
if (status == 400) {
System.out.println("Invalid phone number: " + result.getTo());
} else if (status == 429) {
System.out.println("Rate limited - retry later");
} else {
System.out.println("Error " + status + ": " + error);
}
}Delivery Reports
Delivery reports are sent via Azure Event Grid. Configure an Event Grid subscription for your ACS resource.
// Event Grid webhook handler (in your endpoint)
public void handleDeliveryReport(String eventJson) {
// Parse Event Grid event
// Event type: Microsoft.Communication.SMSDeliveryReportReceived
// Event data contains:
// - messageId: correlates to SmsSendResult.getMessageId()
// - from: sender number
// - to: recipient number
// - deliveryStatus: "Delivered", "Failed", etc.
// - deliveryStatusDetails: detailed status
// - receivedTimestamp: when status was received
// - tag: your custom tag from SmsSendOptions
}SmsSendResult Properties
| Property | Type | Description |
|---|---|---|
getMessageId() | String | Unique message identifier |
getTo() | String | Recipient phone number |
isSuccessful() | boolean | Whether send succeeded |
getHttpStatusCode() | int | HTTP status for this recipient |
getErrorMessage() | String | Error details if failed |
getRepeatabilityResult() | RepeatabilityResult | Idempotency result |
Environment Variables
AZURE_COMMUNICATION_ENDPOINT=https://<resource>.communication.azure.com # Required for all auth methods
AZURE_COMMUNICATION_CONNECTION_STRING=endpoint=https://...;accesskey=... # Alternative to Entra ID auth
SMS_FROM_NUMBER=+14255550100 # Required for the sender phone number
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in productionBest Practices
Phone Number Format - Use E.164 format:
+[country code][number]Delivery Reports - Enable for critical messages (OTP, alerts)
Tagging - Use tags to correlate messages with business context
Error Handling - Check
isSuccessful()for each recipient individuallyRate Limiting - Implement retry with backoff for 429 responses
Bulk Sending - Use batch send for multiple recipients (more efficient)
Trigger Phrases
"send SMS Java", "text message Java"
"SMS notification", "OTP SMS", "bulk SMS"
"delivery report SMS", "Azure Communication Services SMS"
Files
- ATTRIBUTION.mdAdded by AutoGineer991 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md8.8 KiB
- references/examples.md14.9 KiB
references/examples.md
Azure Communication SMS Java SDK - Examples
Comprehensive code examples for the Azure Communication Services SMS SDK for Java.
Table of Contents
Maven Dependency
Client Creation
Send Single SMS
Send Bulk SMS
Delivery Reports
Async Operations
Error Handling
Complete Application Example
Maven Dependency
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-communication-sms</artifactId>
<version>1.2.0</version>
</dependency>Client Creation
With DefaultAzureCredential (Recommended)
import com.azure.communication.sms.SmsClient;
import com.azure.communication.sms.SmsClientBuilder;
import com.azure.identity.DefaultAzureCredentialBuilder;
SmsClient smsClient = new SmsClientBuilder()
.endpoint("https://<resource>.communication.azure.com")
.credential(new DefaultAzureCredentialBuilder().build())
.buildClient();With Connection String
SmsClient smsClient = new SmsClientBuilder()
.connectionString(System.getenv("AZURE_COMMUNICATION_CONNECTION_STRING"))
.buildClient();With AzureKeyCredential
import com.azure.core.credential.AzureKeyCredential;
SmsClient smsClient = new SmsClientBuilder()
.endpoint("https://<resource>.communication.azure.com")
.credential(new AzureKeyCredential("<access-key>"))
.buildClient();Async Client
import com.azure.communication.sms.SmsAsyncClient;
SmsAsyncClient asyncClient = new SmsClientBuilder()
.connectionString(connectionString)
.buildAsyncClient();Send Single SMS
Simple Send
import com.azure.communication.sms.models.SmsSendResult;
SmsSendResult result = smsClient.send(
"+14255550100", // From (your ACS phone number)
"+14255551234", // To
"Your verification code is 123456"
);
System.out.println("Message ID: " + result.getMessageId());
System.out.println("Success: " + result.isSuccessful());Send with Options
import com.azure.communication.sms.models.SmsSendOptions;
import com.azure.core.util.Context;
import java.util.Collections;
SmsSendOptions options = new SmsSendOptions()
.setDeliveryReportEnabled(true)
.setTag("verification-code");
SmsSendResult result = smsClient.sendWithResponse(
"+14255550100",
Collections.singletonList("+14255551234"),
"Your verification code is 123456",
options,
Context.NONE
).getValue().iterator().next();Send Bulk SMS
Send to Multiple Recipients
import java.util.Arrays;
import java.util.List;
List<String> recipients = Arrays.asList(
"+14255551111",
"+14255552222",
"+14255553333"
);
SmsSendOptions options = new SmsSendOptions()
.setDeliveryReportEnabled(true)
.setTag("marketing-campaign-001");
Iterable<SmsSendResult> results = smsClient.sendWithResponse(
"+14255550100",
recipients,
"Flash sale! 50% off today only.",
options,
Context.NONE
).getValue();
// Process results for each recipient
for (SmsSendResult result : results) {
if (result.isSuccessful()) {
System.out.printf("✓ Sent to %s: %s%n", result.getTo(), result.getMessageId());
} else {
System.out.printf("✗ Failed to %s: %s (HTTP %d)%n",
result.getTo(),
result.getErrorMessage(),
result.getHttpStatusCode());
}
}Batch SMS with Retry Logic
import java.util.ArrayList;
import java.util.List;
public class SmsBatchSender {
private final SmsClient smsClient;
private final String fromNumber;
public SmsBatchSender(SmsClient smsClient, String fromNumber) {
this.smsClient = smsClient;
this.fromNumber = fromNumber;
}
public List<SmsSendResult> sendBatch(List<String> recipients, String message) {
List<SmsSendResult> allResults = new ArrayList<>();
List<String> failedRecipients = new ArrayList<>();
SmsSendOptions options = new SmsSendOptions()
.setDeliveryReportEnabled(true);
// First attempt
Iterable<SmsSendResult> results = smsClient.sendWithResponse(
fromNumber, recipients, message, options, Context.NONE
).getValue();
for (SmsSendResult result : results) {
allResults.add(result);
if (!result.isSuccessful() && result.getHttpStatusCode() == 429) {
failedRecipients.add(result.getTo());
}
}
// Retry rate-limited messages after delay
if (!failedRecipients.isEmpty()) {
try {
Thread.sleep(2000); // Wait 2 seconds
Iterable<SmsSendResult> retryResults = smsClient.sendWithResponse(
fromNumber, failedRecipients, message, options, Context.NONE
).getValue();
for (SmsSendResult result : retryResults) {
allResults.add(result);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
return allResults;
}
}Delivery Reports
Delivery reports are sent via Azure Event Grid. Configure an Event Grid subscription for your ACS resource.
Event Grid Webhook Handler
import com.azure.messaging.eventgrid.EventGridEvent;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class SmsDeliveryReportHandler {
private final ObjectMapper objectMapper = new ObjectMapper();
public void handleDeliveryReport(String eventJson) throws Exception {
List<EventGridEvent> events = EventGridEvent.fromString(eventJson);
for (EventGridEvent event : events) {
if ("Microsoft.Communication.SMSDeliveryReportReceived".equals(event.getEventType())) {
JsonNode data = objectMapper.readTree(event.getData().toString());
String messageId = data.get("messageId").asText();
String from = data.get("from").asText();
String to = data.get("to").asText();
String deliveryStatus = data.get("deliveryStatus").asText();
String deliveryStatusDetails = data.get("deliveryStatusDetails").asText();
String tag = data.has("tag") ? data.get("tag").asText() : null;
System.out.printf("Delivery Report - MessageId: %s, To: %s, Status: %s%n",
messageId, to, deliveryStatus);
// Update your database or trigger actions based on status
switch (deliveryStatus) {
case "Delivered":
onMessageDelivered(messageId, to, tag);
break;
case "Failed":
onMessageFailed(messageId, to, deliveryStatusDetails, tag);
break;
}
}
}
}
private void onMessageDelivered(String messageId, String to, String tag) {
System.out.printf("Message %s delivered to %s (tag: %s)%n", messageId, to, tag);
}
private void onMessageFailed(String messageId, String to, String reason, String tag) {
System.out.printf("Message %s failed to %s: %s (tag: %s)%n",
messageId, to, reason, tag);
}
}Async Operations
Async Send Single Message
import reactor.core.publisher.Mono;
SmsAsyncClient asyncClient = new SmsClientBuilder()
.connectionString(connectionString)
.buildAsyncClient();
asyncClient.send("+14255550100", "+14255551234", "Async message!")
.subscribe(
result -> {
System.out.println("Sent: " + result.getMessageId());
System.out.println("Success: " + result.isSuccessful());
},
error -> System.err.println("Error: " + error.getMessage())
);Async Bulk Send with Reactive Streams
import reactor.core.publisher.Flux;
SmsSendOptions options = new SmsSendOptions()
.setDeliveryReportEnabled(true);
asyncClient.sendWithResponse(
"+14255550100",
Arrays.asList("+14255551111", "+14255552222"),
"Bulk async message",
options)
.flatMapMany(response -> Flux.fromIterable(response.getValue()))
.subscribe(
result -> {
if (result.isSuccessful()) {
System.out.printf("✓ %s: %s%n", result.getTo(), result.getMessageId());
} else {
System.out.printf("✗ %s: %s%n", result.getTo(), result.getErrorMessage());
}
},
error -> System.err.println("Error: " + error.getMessage()),
() -> System.out.println("All messages processed")
);Error Handling
Comprehensive Error Handling
import com.azure.core.exception.HttpResponseException;
public class SmsService {
private final SmsClient smsClient;
private final String fromNumber;
public SmsService(SmsClient smsClient, String fromNumber) {
this.smsClient = smsClient;
this.fromNumber = fromNumber;
}
public SmsSendResult sendSms(String to, String message) {
try {
SmsSendResult result = smsClient.send(fromNumber, to, message);
if (!result.isSuccessful()) {
handleMessageError(result);
}
return result;
} catch (HttpResponseException e) {
// Request-level failures (auth, network, etc.)
System.err.printf("HTTP Error %d: %s%n",
e.getResponse().getStatusCode(), e.getMessage());
throw new RuntimeException("Failed to send SMS", e);
} catch (RuntimeException e) {
System.err.println("Unexpected error: " + e.getMessage());
throw e;
}
}
private void handleMessageError(SmsSendResult result) {
int status = result.getHttpStatusCode();
String to = result.getTo();
String error = result.getErrorMessage();
switch (status) {
case 400:
System.err.printf("Invalid phone number: %s%n", to);
break;
case 401:
System.err.println("Authentication failed - check credentials");
break;
case 403:
System.err.printf("Not authorized to send to: %s%n", to);
break;
case 429:
System.err.println("Rate limited - implement backoff retry");
break;
default:
System.err.printf("Error %d sending to %s: %s%n", status, to, error);
}
}
}Complete Application Example
OTP SMS Service
import com.azure.communication.sms.SmsClient;
import com.azure.communication.sms.SmsClientBuilder;
import com.azure.communication.sms.models.SmsSendOptions;
import com.azure.communication.sms.models.SmsSendResult;
import com.azure.core.util.Context;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class OtpSmsService {
private final SmsClient smsClient;
private final String fromNumber;
private final Map<String, OtpEntry> otpStore = new ConcurrentHashMap<>();
private final SecureRandom random = new SecureRandom();
public OtpSmsService(String connectionString, String fromNumber) {
this.smsClient = new SmsClientBuilder()
.connectionString(connectionString)
.buildClient();
this.fromNumber = fromNumber;
}
public String sendOtp(String phoneNumber) {
// Generate 6-digit OTP
String otp = String.format("%06d", random.nextInt(1000000));
// Store OTP with expiry
otpStore.put(phoneNumber, new OtpEntry(otp, System.currentTimeMillis() + 300000));
// Send SMS
String message = String.format("Your verification code is %s. Valid for 5 minutes.", otp);
SmsSendOptions options = new SmsSendOptions()
.setDeliveryReportEnabled(true)
.setTag("otp-" + phoneNumber);
SmsSendResult result = smsClient.sendWithResponse(
fromNumber,
Collections.singletonList(phoneNumber),
message,
options,
Context.NONE
).getValue().iterator().next();
if (result.isSuccessful()) {
System.out.printf("OTP sent to %s: %s%n", phoneNumber, result.getMessageId());
return result.getMessageId();
} else {
throw new RuntimeException("Failed to send OTP: " + result.getErrorMessage());
}
}
public boolean verifyOtp(String phoneNumber, String otp) {
OtpEntry entry = otpStore.get(phoneNumber);
if (entry == null) {
return false;
}
if (System.currentTimeMillis() > entry.expiryTime) {
otpStore.remove(phoneNumber);
return false;
}
if (entry.otp.equals(otp)) {
otpStore.remove(phoneNumber);
return true;
}
return false;
}
private static class OtpEntry {
final String otp;
final long expiryTime;
OtpEntry(String otp, long expiryTime) {
this.otp = otp;
this.expiryTime = expiryTime;
}
}
}Usage
public class Main {
public static void main(String[] args) {
String connectionString = System.getenv("AZURE_COMMUNICATION_CONNECTION_STRING");
String fromNumber = System.getenv("SMS_FROM_NUMBER");
OtpSmsService otpService = new OtpSmsService(connectionString, fromNumber);
// Send OTP
String messageId = otpService.sendOtp("+14255551234");
System.out.println("OTP sent with message ID: " + messageId);
// Verify OTP (user enters code)
boolean verified = otpService.verifyOtp("+14255551234", "123456");
System.out.println("OTP verified: " + verified);
}
}Environment Variables
AZURE_COMMUNICATION_ENDPOINT=https://<resource>.communication.azure.com
AZURE_COMMUNICATION_CONNECTION_STRING=endpoint=https://...;accesskey=...
SMS_FROM_NUMBER=+14255550100Best Practices
Phone Number Format - Use E.164 format:
+[country code][number]Delivery Reports - Enable for critical messages (OTP, alerts)
Tagging - Use tags to correlate messages with business context
Error Handling - Check
isSuccessful()for each recipient individuallyRate Limiting - Implement retry with exponential backoff for 429 responses
Bulk Sending - Use batch send for multiple recipients (more efficient)
Connection Reuse - Reuse
SmsClientinstances across requests
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Microsoft
- Publisher
- Microsoft (Official)
- Source
- microsoft/skills · .github/plugins/azure-sdk-java/skills/azure-communication-sms-java · commit 0696600
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Microsoft.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download azure-communication-sms-java.zip