SMS API
Send SMS and MMS messages with RelayAPI — text messages, images, multi-recipient delivery, and media messaging via Twilio.
Quick Reference
| Property | Value |
|---|---|
| Platform key | sms |
| Auth method | Twilio Account SID + Auth Token |
| Text limit | 1,600 characters (auto-segmented) |
| Media per message | 10 (MMS) |
| Image formats | JPEG, PNG, GIF |
| Image max size | 5 MB |
| Video formats | MP4 |
| Video max size | 5 MB |
| Message types | SMS (text), MMS (media) |
| Multi-recipient | Yes (sends individually to each number) |
| Scheduling | Yes |
| Analytics | No (use Twilio dashboard) |
SDK source — TypeScript · Python · Go · Java · REST API
Before You Start
SMS uses Twilio as the delivery provider — you must bring your own Twilio credentials. Each message requires a from number (your Twilio phone number) and at least one recipient phone number in E.164 format (e.g., +15017122661). Messages longer than 160 characters are auto-segmented by Twilio into multiple SMS segments. MMS (media messages) supports up to 10 media URLs per message. Multi-recipient messages are sent individually to each phone number (not as a group).
Quick Start
Send an SMS message:
import Relay from '@relayapi/sdk';
const client = new Relay();
const post = await client.posts.create({
content: 'Hello from RelayAPI!',
targets: ['sms'],
scheduled_at: 'now',
target_options: {
sms: {
from_number: '+15017122661',
phone_numbers: ['+14155238886']
}
}
});
console.log(post.id); // post_abc123from relay import Relay
client = Relay()
post = client.posts.create(
content='Hello from RelayAPI!',
targets=['sms'],
scheduled_at='now',
target_options={
'sms': {
'from_number': '+15017122661',
'phone_numbers': ['+14155238886']
}
}
)
print(post.id) # post_abc123client := relaygo.NewClient()
post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Content: relaygo.F("Hello from RelayAPI!"),
Targets: relaygo.F([]string{"sms"}),
ScheduledAt: relaygo.F("now"),
TargetOptions: relaygo.F(map[string]interface{}{
"sms": map[string]interface{}{
"from_number": "+15017122661",
"phone_numbers": []string{"+14155238886"},
},
}),
})RelayClient client = RelayOkHttpClient.fromEnv();
PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.content("Hello from RelayAPI!")
.addTarget("sms")
.scheduledAt("now")
.targetOptions(PostCreateParams.TargetOptions.builder()
.putAdditionalProperty("sms", JsonValue.from(Map.of(
"from_number", "+15017122661",
"phone_numbers", List.of("+14155238886")
)))
.build())
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Hello from RelayAPI!",
"targets": ["sms"],
"scheduled_at": "now",
"target_options": {
"sms": {
"from_number": "+15017122661",
"phone_numbers": ["+14155238886"]
}
}
}'Content Types
Text Message (SMS)
Plain text message up to 1,600 characters. Messages over 160 characters are auto-segmented.
const post = await client.posts.create({
content: 'Your order #12345 has been shipped! Track it here: https://example.com/track/12345',
targets: ['sms'],
scheduled_at: 'now',
target_options: {
sms: {
from_number: '+15017122661',
phone_numbers: ['+14155238886']
}
}
});post = client.posts.create(
content='Your order #12345 has been shipped! Track it here: https://example.com/track/12345',
targets=['sms'],
scheduled_at='now',
target_options={
'sms': {
'from_number': '+15017122661',
'phone_numbers': ['+14155238886']
}
}
)post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Content: relaygo.F("Your order #12345 has been shipped! Track it here: https://example.com/track/12345"),
Targets: relaygo.F([]string{"sms"}),
ScheduledAt: relaygo.F("now"),
TargetOptions: relaygo.F(map[string]interface{}{
"sms": map[string]interface{}{
"from_number": "+15017122661",
"phone_numbers": []string{"+14155238886"},
},
}),
})PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.content("Your order #12345 has been shipped! Track it here: https://example.com/track/12345")
.addTarget("sms")
.scheduledAt("now")
.targetOptions(PostCreateParams.TargetOptions.builder()
.putAdditionalProperty("sms", JsonValue.from(Map.of(
"from_number", "+15017122661",
"phone_numbers", List.of("+14155238886")
)))
.build())
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Your order #12345 has been shipped! Track it here: https://example.com/track/12345",
"targets": ["sms"],
"scheduled_at": "now",
"target_options": {
"sms": {
"from_number": "+15017122661",
"phone_numbers": ["+14155238886"]
}
}
}'Media Message (MMS)
Send images or videos via MMS. Up to 10 media URLs per message.
const post = await client.posts.create({
content: "Here's your receipt!",
targets: ['sms'],
media: [
{ url: 'https://cdn.example.com/receipt.png', type: 'image' }
],
scheduled_at: 'now',
target_options: {
sms: {
from_number: '+15017122661',
phone_numbers: ['+14155238886']
}
}
});post = client.posts.create(
content="Here's your receipt!",
targets=['sms'],
media=[
{'url': 'https://cdn.example.com/receipt.png', 'type': 'image'}
],
scheduled_at='now',
target_options={
'sms': {
'from_number': '+15017122661',
'phone_numbers': ['+14155238886']
}
}
)post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Content: relaygo.F("Here's your receipt!"),
Targets: relaygo.F([]string{"sms"}),
ScheduledAt: relaygo.F("now"),
Media: relaygo.F([]relaygo.PostNewParamsMedia{
{URL: relaygo.F("https://cdn.example.com/receipt.png"), Type: relaygo.F(relaygo.PostNewParamsMediaTypeImage)},
}),
TargetOptions: relaygo.F(map[string]interface{}{
"sms": map[string]interface{}{
"from_number": "+15017122661",
"phone_numbers": []string{"+14155238886"},
},
}),
})PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.content("Here's your receipt!")
.addTarget("sms")
.scheduledAt("now")
.addMedia(PostCreateParams.Media.builder()
.url("https://cdn.example.com/receipt.png")
.type(PostCreateParams.Media.Type.IMAGE)
.build())
.targetOptions(PostCreateParams.TargetOptions.builder()
.putAdditionalProperty("sms", JsonValue.from(Map.of(
"from_number", "+15017122661",
"phone_numbers", List.of("+14155238886")
)))
.build())
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Here'\''s your receipt!",
"targets": ["sms"],
"media": [
{"url": "https://cdn.example.com/receipt.png", "type": "image"}
],
"scheduled_at": "now",
"target_options": {
"sms": {
"from_number": "+15017122661",
"phone_numbers": ["+14155238886"]
}
}
}'Media-Only Message (MMS)
Send media without any text body.
const post = await client.posts.create({
targets: ['sms'],
media: [
{ url: 'https://cdn.example.com/promo.jpg', type: 'image' }
],
scheduled_at: 'now',
target_options: {
sms: {
from_number: '+15017122661',
phone_numbers: ['+14155238886']
}
}
});post = client.posts.create(
targets=['sms'],
media=[
{'url': 'https://cdn.example.com/promo.jpg', 'type': 'image'}
],
scheduled_at='now',
target_options={
'sms': {
'from_number': '+15017122661',
'phone_numbers': ['+14155238886']
}
}
)post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Targets: relaygo.F([]string{"sms"}),
ScheduledAt: relaygo.F("now"),
Media: relaygo.F([]relaygo.PostNewParamsMedia{
{URL: relaygo.F("https://cdn.example.com/promo.jpg"), Type: relaygo.F(relaygo.PostNewParamsMediaTypeImage)},
}),
TargetOptions: relaygo.F(map[string]interface{}{
"sms": map[string]interface{}{
"from_number": "+15017122661",
"phone_numbers": []string{"+14155238886"},
},
}),
})PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.addTarget("sms")
.scheduledAt("now")
.addMedia(PostCreateParams.Media.builder()
.url("https://cdn.example.com/promo.jpg")
.type(PostCreateParams.Media.Type.IMAGE)
.build())
.targetOptions(PostCreateParams.TargetOptions.builder()
.putAdditionalProperty("sms", JsonValue.from(Map.of(
"from_number", "+15017122661",
"phone_numbers", List.of("+14155238886")
)))
.build())
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"targets": ["sms"],
"media": [
{"url": "https://cdn.example.com/promo.jpg", "type": "image"}
],
"scheduled_at": "now",
"target_options": {
"sms": {
"from_number": "+15017122661",
"phone_numbers": ["+14155238886"]
}
}
}'Multi-Recipient
Send the same message to multiple phone numbers. Each recipient receives an individual message.
const post = await client.posts.create({
content: 'Reminder: Team meeting at 3 PM today.',
targets: ['sms'],
scheduled_at: 'now',
target_options: {
sms: {
from_number: '+15017122661',
phone_numbers: [
'+14155238886',
'+14155238887',
'+14155238888'
]
}
}
});post = client.posts.create(
content='Reminder: Team meeting at 3 PM today.',
targets=['sms'],
scheduled_at='now',
target_options={
'sms': {
'from_number': '+15017122661',
'phone_numbers': [
'+14155238886',
'+14155238887',
'+14155238888'
]
}
}
)post, err := client.Posts.New(context.TODO(), relaygo.PostNewParams{
Content: relaygo.F("Reminder: Team meeting at 3 PM today."),
Targets: relaygo.F([]string{"sms"}),
ScheduledAt: relaygo.F("now"),
TargetOptions: relaygo.F(map[string]interface{}{
"sms": map[string]interface{}{
"from_number": "+15017122661",
"phone_numbers": []string{
"+14155238886",
"+14155238887",
"+14155238888",
},
},
}),
})PostCreateResponse post = client.posts().create(PostCreateParams.builder()
.content("Reminder: Team meeting at 3 PM today.")
.addTarget("sms")
.scheduledAt("now")
.targetOptions(PostCreateParams.TargetOptions.builder()
.putAdditionalProperty("sms", JsonValue.from(Map.of(
"from_number", "+15017122661",
"phone_numbers", List.of("+14155238886", "+14155238887", "+14155238888")
)))
.build())
.build());curl -X POST https://api.relayapi.dev/v1/posts \
-H "Authorization: Bearer $RELAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Reminder: Team meeting at 3 PM today.",
"targets": ["sms"],
"scheduled_at": "now",
"target_options": {
"sms": {
"from_number": "+15017122661",
"phone_numbers": [
"+14155238886",
"+14155238887",
"+14155238888"
]
}
}
}'Media Requirements
Images
| Property | Requirement |
|---|---|
| Max per message | 10 (MMS) |
| Formats | JPEG, PNG, GIF |
| Max file size | 5 MB |
Videos
| Property | Requirement |
|---|---|
| Max per message | 1 |
| Formats | MP4 |
| Max file size | 5 MB |
target_options Fields
All fields go inside target_options.sms on your post request.
| Field | Type | Default | Description |
|---|---|---|---|
content | string | — | Override message content for SMS specifically |
media | object[] | — | Override media for SMS specifically |
from_number | string | — | Required. Your Twilio phone number in E.164 format (e.g., "+15017122661") |
phone_numbers | string[] | — | Required. Recipient phone numbers in E.164 format |
Common Errors
| Error | Cause | Fix |
|---|---|---|
| Missing from_number | No sender phone number provided | Add from_number in target_options.sms with your Twilio phone number. |
| Missing phone_numbers | No recipients provided | Add at least one phone number to phone_numbers array. |
| Empty content | No body text or media provided | Add text content or at least one media URL. |
| Invalid phone number | Phone number is not in E.164 format | Use E.164 format with country code (e.g., +14155238886). |
| Twilio auth error | Invalid Account SID or Auth Token | Verify your Twilio credentials in the connected account settings. |
Known Quirks
- Twilio-powered — you must bring your own Twilio Account SID, Auth Token, and phone number.
- Auto-segmentation — messages over 160 characters are split into multiple segments by Twilio (billed per segment).
- 1,600-character limit — Twilio allows up to 1,600 characters per message (auto-segmented).
- MMS availability — MMS (media messages) is only available in the US and Canada. International messages are SMS-only.
- Multi-recipient sends individually — each phone number receives a separate message (not a group text).
- Media URLs must be publicly accessible — Twilio fetches media from the provided URLs.
- Partial failures are possible — when sending to multiple recipients, some may succeed while others fail.
Automations
SMS automation is provider-abstracted — Twilio and Telnyx both supported. Provider is stored on the account metadata as provider (defaults to twilio).
Triggers
| Type | Fires on |
|---|---|
sms_received | Inbound SMS (any provider) |
Send nodes
| Node | Twilio endpoint | Telnyx endpoint | Required fields |
|---|---|---|---|
sms_send | POST /2010-04-01/Accounts/{sid}/Messages.json | POST /v2/messages | text |
sms_send_mms | same (+ MediaUrl) | same (+ media_urls[]) | media_url, optional text |
Auth is Basic (Twilio: sid:auth_token) or Bearer (Telnyx: API key), chosen from the account's provider field.
Found something wrong? Help us improve this page.