Unifies sending messages across push, email, and SMS
This guide will walk you through the process of sending messages using OneSignal's API. It covers all options, nuances, and potential gotchas to ensure clarity and effectiveness. Before proceeding, make sure you have:
- Followed our Channel Setup Guide.
- Started accumulating Subscriptions.
Step 1. Choose a targeting strategy
When sending messages, you need to decide how to target your users. Only one strategy can be used per message.
Aliases, email addresses, or phone numbers
Target specific users or groups of users (up to 2,000 entries). This method is best for Transactional Messages.
Users & subscriptions parameters (click to expand)
include_aliases
include_aliases
Type object
Description
Targets specific users based on their External ID or custom Aliases. You may include up to 2,000 Alias IDs per API call. Use with the target_channel
parameter to control the delivery channel.
Provide one object of aliases to include.
- User's External ID –
external_id
- OneSignal ID –
onesignal_id
- Custom alias name –
some_custom_alias
Example
Send a message to the users identified by the External IDs "user1", "user2", and "user3" using the push channel.
{
"include_aliases": {
"external_id": [
"user1",
"user2",
"user3"
]
},
"target_channel": "push"
}
target_channel
target_channel
Type string
Description
The delivery channel in which to deliver a message. Use in conjunction with include_aliases
. Options:
"push"
– send the message as a push notification"email"
- send the message as an email"sms"
- send the message as a text message
Example
{
"target_channel": "push"
}
include_subscription_ids
include_subscription_ids
Type string[]
Description
Targets users based on their Subscription ID. Include up to 2,000 IDs per API call.
Example
{
"include_subscription_ids": [
"1dd608f2-c6a1-11e3-851d-000c2940e62c"
]
}
include_email_tokens
include_email_tokens
Type string[]
Description
Targets users based on their email address. Can only be used when sending Email. Include up to 2,000 IDs per API call. If the email address does not exist within the OneSignal App, then a new email Subscription will be created.
Example
{
"include_email_tokens": [
"[email protected]"
]
}
include_phone_numbers
include_phone_numbers
Type string[]
Description
Targets users based on their phone number in E.164 format. Can only be used when sending SMS/MMS. Include up to 2,000 IDs per API call. If the phone number does not exist within the OneSignal App, then a new SMS Subscription will be created.
Example
{
"include_phone_numbers": [
"+19999999999"
]
}
Segments
Target groups of users based on predefined Segments.
Segment parameters (click to expand)
included_segments
included_segments
Type string[]
Description
The segments to target. Members of each segment will receive the notification unless they're also members of a segment specified in the optional excluded segments
parameter. Users that are in multiple segments will only be sent the message once.
Example
{
"included_segments": [
"Active Users",
"Inactive Users"
]
}
excluded_segments
excluded_segments
Type string[]
Description
Segments to exclude from targeting. Overrides membership in any segment specified in the included_segments
parameter.
Example
Target a message for customers who've spent $5K or more while excluding those deemed inactive, even if they are included in the segment "CLTV 5K".
{
"included_segments": [
"CLTV 5K"
],
"excluded_segments": [
"Inactive Users"
]
}
Filters
Use filters to target groups of users dynamically without creating predefined Segments.
Filters parameters (click to expand)
filters
filters
Type object[]
Description
Filters allow you to dynamically define your message's audience based on their properties. They can be combined with "AND"
and "OR"
operators for complex targeting logic, enabling precise audience segmentation.
For performance reasons, a maximum of 200 entries can be used. The 200 entries limit includes the "field"
entry and "OR"
entries - each would count towards the 200 limit.
Filter performance:
- Tag filters using an equality (
"="
) or"exists"
operator are very fast. - Negation (eg.
"!="
and"not_exists"
) is inherently more expensive to compute because it depends on how many tags per user are set. Upon request, it is sometimes possible for us to create a specific index on your application to address problems with negation. - Numeric ordering operators like
"<"
or">"
are slow by default. Note that upon special request, we can add a special index for specific tags to improve performance of these operators. - Filters on the
"last_session"
,"session_count"
, and"country"
fields are fast on their own or together, but when used with other filters such as tags can make the segment take longer to compute.
Available filters:
- operator
- tag
- last_session
- first_session
- session_count
- session_time
- language
- app_version
- location
- amount_spent
- bought_sku
Filter property definitions (click to expand)
operator
operator
Description
Allows you to combine or separate properties. Filters combined with an "AND"
have higher priority than "OR"
filters.
"AND"
= the 2+ connected filters must be satisfied for the recipient to be included. Filter entries use this by default and its not required to be included."OR"
= the 2 filters separated by the"OR"
operator are mutually exclusive. The recipients only need to satisfy the conditions on either side of the"OR"
operator.
// Users must satisfy both filters to be included.
// Notice the AND operator is not required
"filters": [
{"field": "tag", "key": "level", "relation": "=", "value": "10"},
{"field": "amount_spent", "relation": ">","value": "0"}
]
// The same example using the AND operator. This is not required.
"filters": [
{"field": "tag", "key": "level", "relation": "=", "value": "10"},
{"operator": "AND"},
{"field": "amount_spent", "relation": ">","value": "0"}
]
// Users can satisfy either filter to be included.
"filters": [
{"field": "tag", "key": "level", "relation": "=", "value": "10"},
{"operator": "OR"},
{"field": "tag", "key": "level", "relation": "=", "value": "20"}
]
// In this example, users must either have:
// The specified session_count AND tag requirement
// Or it will be all records where last_session is satisfied
{
"name": "2 filters or 1",
"filters": [
{"field": "session_count", "relation": ">", "value": "2"},
{"operator": "AND"},
{"field": "tag", "relation": "!=", "key": "tag_key", "value": "1"},
{"operator": "OR"},
{"field": "last_session", "relation": "<", "hours_ago": "30"}
]
}
// Similar to the first example, this shows how to require a specific field
// across other filters
{
"name": "3 filters, 1 required across all",
"filters": [
{"field": "session_count", "relation": ">", "value": "2"},
{"operator": "AND"},
{"field": "tag", "relation": "!=", "key": "tag_key", "value": "1"},
{"operator": "OR"},
{"field": "last_session", "relation": "<", "hours_ago": "30"},
{"operator": "AND"},
{"field": "tag", "relation": "!=", "key": "tag_key", "value": "1"}
]
}
tag
tag
Description
Maps to the Tags set on Users.
Do not use tags for targeting individual users like a "user id". Instead use External ID or custom Aliases and the include_aliases
targeting property.
relation
=">"
,"<"
,"="
,"!="
,"exists"
,"not_exists"
,"time_elapsed_gt"
, (time elapsed greater than) and"time_elapsed_lt"
(time elapsed less than)- The
time_elapsed_gt/lt
fields correspond to Time Operators and require a paid plan.
- The
key
= Tag key to compare.value
= Tag value to compare. Not required for"exists"
or"not_exists"
.
"filters": [
{"field": "tag", "key": "level", "relation": "=", "value": "10"}
]
last_session
last_session
Description
Maps to the last active time the Subscriptions used the app.
relation
=">"
or"<"
hours_ago
= number of hours before or after the user's last session. Example:"1.1"
"filters": [
{"field": "last_session", "relation": ">","hours_ago": "10"}
]
first_session
first_session
Description
Maps to the first date and time the Users were created within OneSignal.
relation
=">"
or"<"
hours_ago
= number of hours before or after the user's first session. Example:"1.1"
"filters": [
{"field": "first_session", "relation": "<","hours_ago": "24"}
]
session_count
session_count
Description
Maps to the amount of sessions for the Subscriptions.
relation
=">"
,"<"
,"="
or"!="
value
= number sessions. Example:"1"
"filters": [
{"field": "session_count", "relation": ">","value": "5"}
]
session_time
session_time
Description
Maps to the usage duration of your Subscriptions which is the total number of seconds they had your app open.
relation
=">"
or"<"
value
= Time in seconds the user has been in your app. Example: 1 day is"86400"
seconds
"filters": [
{"field": "session_time", "relation": ">","value": "86400"}
]
language
language
Description
Maps to the language code of your Users. See Multi-Language Messaging for details and supported language codes.
relation
="="
or"!="
value
= 2 character language code. Example:"en"
.
"filters": [
{"field": "language", "relation": "=","value": "en"},
{"operator": "OR"},
{"field": "language", "relation": "=","value": "es"}
]
app_version
app_version
Description
Maps to your app version set on Subscriptions.
relation
=">"
,"<"
,"="
or"!="
value
= app version. Example:"1.0.0"
"filters": [
{"field": "app_version", "relation": "=","value": "1.0.1"}
]
location
location
Description
Maps to the GPS coordinates of the device. Location tracking must be turned on and accepted by the user. See Location-Triggered Notifications for more details.
radius
= in meterslat
= latitudelong
= longitude
"filters": [
{"field": "location", "radius": "1000","lat": "37.77", "long":"-122.43"}
]
amount_spent
amount_spent
Description
The amount of consumable in-app purchases made while our SDK was active on the mobile app.
If you have subscription-based purchases, a website, email-only, and/or SMS-only users, then you should consider using Tags instead.
relation
=">"
,"<"
, or"="
value
= Amount in USD a user has spent on IAP (In App Purchases). Example:"0.99"
"filters": [
{"field": "amount_spent", "relation": ">","value": "100"}
]
bought_sku
bought_sku
Description
Maps to the purchases property on Users. The stock-keeping unit (SKU) identifier for the purchased item.
relation
=">"
,"<"
or"="
key
= SKU purchased in your app as an IAP (In App Purchases). Example:"com.domain.100coinpack"
value
= value of SKU to compare to. Example:"0.99"
"filters": [
{"field": "bought_sku", "relation": ">","key":"com.domain.100coinpack","value": "10"}
]
Step 2. Craft your message
Each messaging channel has channel-specific parameters that can be included in the request body. For example, push and SMS use contents
and email uses email_body
.
This example shows sending the push or sms with message 'Hello, world' localized for Spanish, French, and Chinese. See Multi-language messaging for more details on localization.
// Message request body
{
"contents": {
"en": "Hello, world",
"es": "Hola Mundo",
"fr": "Bonjour le monde",
"zh-Hans": "你好世界"
}
}
There are also general message parameters that can be used across all channels. For example, if you want to personalize your messages, you can use the template_id
and custom_data
to inject personalization into each message.
Step 3. Schedule delivery
By default, messages will be sent immediately when the request is made. You can schedule them to be sent at a specific time and add per-user optimizations like timezone and last-active.
send_after
send_after
Type string
Description
Schedule notifications for future delivery; defaults to Coordinated Universal Time (UTC). Align delivery with your target users' time zones so they're more likely to see it.
Specify the string according to the ISO 8601 calendar format. A heuristic you can use to determine whether we can parse your date-time string is whether Javascript's Date constructor can parse the string.
Example
All of the following are valid and equivalent formats for the same date and time:
{ "send_after": "Thu Sep 24 2015 14:00:00 GMT-0700 (PDT)" }
{ "send_after": "2015-09-24 14:00:00 GMT-0700" }
{ "send_after": "Sept 24 2015 14:00:00 GMT-0700" }
delayed_option
delayed_option
Type string
Description
Choose how deliveries should be scheduled. Not compatible with Push Throttling, set throttle_rate_per_minute
to 0 to disable throttling if enabled.
timezone
– simultaneous across time zones at the same local time.last-active
– sends based on the user's Last Active Session.
Example
Deliver the notification worldwide at 9 AM in each user's local time zone.
{
"delayed_option": "timezone",
"send_after": "2024-03-05T15:00:00.600Z"
}
delivery_time_of_day
delivery_time_of_day
Type string
Description
Specify when the message should be delivered when the delayed_option
parameter is "timezone". It's ideal for time-sensitive daily digests, reminders, or promotions.
- Format the time as a string, which can be in various formats such as:
- 12-hour e.g., "9:00AM"
- 24-hour e.g., "21:45"
HH:mm:ss
format - e.g., "9:45:30"
Example
Schedule a notification for all users in the morning at 9:00 AM local time.
{
"delayed_option": "timezone",
"delivery_time_of_day": "9:00AM"
}
Step 4. Submit the request
The final cURL request will send the message to All Subscribers in each recipient's local language.
curl -X "POST" "https://api.onesignal.com/notifications" \
-H 'Content-Type: application/json' \
-H 'Authorization: Key YOUR_API_KEY' \
-d $'{
"target_channel": "push",
"included_segments": [
"Subscribed Users"
],
"app_id": "YOUR_APP_ID",
"contents": {
"en": "Hello, world",
"es": "Hola mundo",
"fr": "Bonjour le monde",
"zh-Hans": "你好世界"
}
}'
Next steps
Head to the API doc for the channel you'd like to use.
General message parameters
name
name
Type string
Description
Serves as an internal identifier to help organize your notification campaigns. The name
is not visible to the end-users and is used solely for your purposes within your campaign management workflow.
Assign meaningful names that reflect your notification campaign's purpose, content, or target audience to facilitate easier tracking and management of multiple campaigns.
template_id
template_id
Type string
Description
Identifies a specific template configured within the dashboard or Create template API. See Templates for details.
Leveraging templates facilitates standardized messages across different campaigns, allowing for a cohesive customer experience and analytics to be aggregated at the template-level, which can be viewed on the dashboard.
Provide the UUID of the template, which can be found in the URL when viewing a template on our dashboard.
Example
{
"template_id": "be4a8044-bbd6-11e4-a581-000c2940e62c"
}
custom_data
custom_data
Type object
Description
User-specific or context-specific information for Message Personalization.
- The object must be in JSON format.
- The payload may include up to 2,048 bytes for Push and SMS notifications and up to 10 kilobytes for email messages.
Example
{
"custom_data": {
"cart_items": [
{
"item_name": "sweater",
"img_url": "https://store.onesignal.com/assets/sweater.png"
},
{
"item_name": "socks",
"img_url": "https://store.onesignal.com/assets/socks.png"
}
]
}
}
idempotency_key
idempotency_key
Type string
as UUID
Description
Acts as a correlation identifier and an idempotency key to prevent duplicate message deliveries. Idempotent Notification Requests enable you to send requests while ensuring users receive a message only once. Messages with the same idempotency_key
send only one notification, and subsequent responses return the same result.
- Keys must be unique v3 or v4 UUID format.
- The key remains idempotent for 30 days, meaning any request with the same
idempotency_key
will not result in additional messages sent within this period. After 30 days, a notification with the sameidempotency_key
may be considered a new request and could result in a new message being sent. - This parameter used to be called
external_id
but has been updated toidempotency_key
to reduce confusion with our User alias. Both will work in this case.
Example
{
"idempotency_key": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}