Kyle Seyler
April 30, 2025

To send an SMS in Java, use a notification API like Courier with a Twilio or Vonage provider configured -- full working code below.
Courier acts as the orchestration layer on top of your SMS provider. You bring your own Twilio account (or Vonage, Plivo, etc.), configure it in Courier, and send through one consistent API. If you ever want to add email, push, or Slack to the same codebase, you add a channel rather than a new integration. Courier's mobile notification channel covers SMS, push, and in-app delivery from the same API.
If you only need SMS and have no plans to add channels or manage preferences, direct Twilio is a reasonable choice and we cover that option later in this guide.
Courier does not provision phone numbers. You connect your Twilio (or other provider) account in the Courier dashboard, and Courier routes your sends through it.
Add the dependency to your project:
Maven:
Copied!
Gradle:
Copied!
Then send your first message:
Copied!
Set COURIER_AUTH_TOKEN in your environment before running. Courier routes the send through your configured Twilio provider and returns a requestId you can use to query delivery status.
The routing.channels field tells Courier which channel type to use. Swap "sms" for "email" or "push" and the rest of your code stays the same. Once a single SMS is a foundational primitive, the next question is usually what comes after, multi-step flows like welcome series, delivery escalations, or renewal reminders. The customer journey orchestration guide covers how those flows are built on top of the same send primitive.
If you want to call Twilio directly without the orchestration layer, the Twilio Java helper library is the straightforward path:
Copied!
Copied!
When direct Twilio is the right call: You need only SMS, you have no plans to add other notification channels, and you want the minimal dependency footprint. Twilio's Java SDK is mature and well-documented. There is no reason to add an orchestration layer if you are not orchestrating.
When Courier makes more sense: You are sending across multiple channels (email + SMS + push), you need per-user notification preferences, you want delivery observability without building it yourself, or you want to swap providers without changing application code.
Courier's preference system lets users opt in or out of notification categories per channel. A user who opts out of "marketing" SMS will not receive those sends even if your code sends them. Preferences are enforced automatically at send time.
Instead of routing to a single channel, set method to RoutingMethod.ALL or define a fallback order:
Copied!
Courier tries the first channel and falls back to the next if the user is not reachable. This is useful for transactional messages where delivery guarantees matter.
Every send returns a requestId. Use the Courier message logs or the dashboard to inspect delivery status, provider responses, and failure reasons. For production workloads, connect Courier to your observability stack via outbound webhooks.
Instead of passing a phone number directly, you can store it against a user profile and send by user ID:
Copied!
Courier looks up the phone number from the profile, which keeps your send logic clean and lets you update contact info in one place.
Related resources:

Build with AI: let your agent handle notifications end to end
Courier's Build with AI toolkit gives coding agents direct access to your notification infrastructure. Four integration points, one goal: let your agent send messages, debug deliveries, manage users, and follow notification best practices without context-switching. Works with Claude Code, Cursor, and any agent that can run shell commands or call MCP tools.
By Kyle Seyler
April 07, 2026

How to Use Claude Code on Mobile to Design, Test, and Ship Multichannel Notifications
A walkthrough of using Claude Code's mobile app with Courier's MCP server and CLI to design, test, and ship a multichannel product announcement from a phone. Covers the full workflow: drafting the content outline, creating notification templates using Courier's elemental format, building channel-specific variants for email, Slack, push, and SMS, customizing brand styling through the API, iterating on design with test sends, getting team approval, and publishing with custom routing rules. The whole thing took a couple hours, mostly cold start and design iterations.
By Kyle Seyler
April 06, 2026

Transactional, Product, and Marketing Notifications: What Are the Differences?
Understanding the difference between transactional, product, and marketing notifications is essential for developers building notification infrastructure. Transactional notifications confirm user actions and require no opt-in. Product notifications drive feature adoption through education. Marketing notifications promote sales and require explicit consent. This guide explains the legal requirements, best practices, and when to use each notification type to build compliant systems users trust.Retry
By Kyle Seyler
October 23, 2025
© 2026 Courier. All rights reserved.
<dependency><groupId>com.trycourier</groupId><artifactId>courier</artifactId><version>4.1.1</version></dependency>
implementation 'com.trycourier:courier:4.1.1'
import com.trycourier.courier.Courier;import com.trycourier.courier.api.send.SendMessageRequest;import com.trycourier.courier.api.send.types.*;import java.util.List;public class SendSms {public static void main(String[] args) {Courier courier = Courier.builder().authorizationToken(System.getenv("COURIER_AUTH_TOKEN")).build();var response = courier.send().message(SendMessageRequest.builder().message(Message.builder().to(UserRecipient.builder().phoneNumber("+15551234567").build()).content(ContentMessage.builder().title("Order update").body("Your order #1234 has shipped.").build()).routing(Routing.builder().method(RoutingMethod.SINGLE).channels(List.of("sms")).build()).build()).build());System.out.println("Request ID: " + response.getRequestId());}}
<dependency><groupId>com.twilio.sdk</groupId><artifactId>twilio</artifactId><version>10.1.0</version></dependency>
import com.twilio.Twilio;import com.twilio.rest.api.v2010.account.Message;import com.twilio.type.PhoneNumber;public class SendSmsDirect {public static void main(String[] args) {Twilio.init(System.getenv("TWILIO_ACCOUNT_SID"),System.getenv("TWILIO_AUTH_TOKEN"));Message message = Message.creator(new PhoneNumber("+15551234567"),new PhoneNumber(System.getenv("TWILIO_PHONE_NUMBER")),"Your order #1234 has shipped.").create();System.out.println("SID: " + message.getSid());}}
.routing(Routing.builder().method(RoutingMethod.ALL).channels(List.of("sms", "email")).build())
.to(UserRecipient.builder().userId("user-123").build())