Skip to main content

Documentation Index

Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This guide walks you through sending messages with the OneSignal API — from choosing a target audience to composing content, scheduling delivery, and validating responses. Each section covers channel-specific options and performance guidance.

Prerequisites

  1. Complete Channel Setup for the channels you plan to use: push, email, SMS, Live Activities.
  2. Start accumulating Subscriptions for your users.
  3. Have your REST API Key and App ID ready. See Keys and IDs.

Choose your target audience

You can target users with one of the following methods per request:
  • Aliases: Specific users via unique IDs, emails, or phone numbers.
  • Segments: Groups based on predefined behaviors or attributes.
  • Filters: Custom rules using tags, location, activity, and more.
Only one targeting method is allowed per message. For example, you cannot mix aliases and filters.
You can also target specific platforms (for example, isAndroid, isIos, isAnyWeb) when sending push notifications. By default, all push platforms are enabled.

Aliases, emails, phone numbers

Target specific users or lists of users (up to 20,000 entries per request). This method is best for Transactional Messages.
include_aliases
object
Target up to 20,000 users by their external_id, onesignal_id, or a custom alias. Combine with target_channel to select the delivery channel.
{
  "include_aliases": {
    "external_id": [
      "user1",
      "user2",
      "user3"
    ]
  },
  "target_channel": "push"
}
target_channel
string
The targeted delivery channel. Required when using include_aliases or included_segments and sending SMS/RCS. Accepts "push", "email", or "sms". The "sms" value covers both SMS and RCS — RCS messages are delivered through the SMS channel.
{
  "target_channel": "push"
}
include_subscription_ids
array
Target users’ specific Subscriptions by ID. Max 20,000 subscription_id per request.
{
  "include_subscription_ids": [
    "1dd608f2-c6a1-11e3-851d-000c2940e62c"
  ]
}
email_to
array
Send to specific email addresses (max 20,000 per request). Can only be used when sending email. If the email address does not exist within the OneSignal App, a new email subscription is created.
{
  "email_to": [
    "user1@example.com",
    "user2@example.com"
  ]
}
include_phone_numbers
array
Send SMS/MMS/RCS to phone numbers in E.164 format (max 20,000 per request). Can only be used when sending SMS/MMS/RCS. If the phone number does not exist within the OneSignal App, a new SMS subscription is created.
{
  "include_phone_numbers": [
    "+19999999999"
  ]
}

Segments

Target users in existing segments. Users in multiple segments only receive the message once.
included_segments
array
Target predefined segments. Users who are in multiple segments only receive the message once. Combine with excluded_segments to exclude users from specific segments. When sending SMS/RCS, set target_channel to "sms" (or use the legacy isSms=true field).
{
  "included_segments": [
    "Active Users",
    "Inactive Users"
  ]
}
excluded_segments
array
Exclude users in these segments even if they’re in included_segments.
{
  "included_segments": [
    "Subscribed Users"
  ],
  "excluded_segments": [
    "Inactive Users"
  ]
}

Filters

Build real-time audiences with AND/OR logic without creating segments first. You can include up to 200 total entries per request. This includes filter rows (for example, each field) and logical operators like {"operator": "OR"}.
Performance guidance:
  • Fast: Tag filters using "=" or "exists", and filters on last_session, session_count, or country.
  • Slower: Negation filters ("!=", "not_exists") — especially with users who have many tags. Contact support to request indexing optimizations.
  • Slow by default: Numeric comparisons (">", "<") on tags or custom properties. Indexing may be available on request.
  • Mixed performance: Combining tag filters with other fields may increase computation time.
operator
string
  • Implicit AND logic between filters. Use "operator": "OR" to start a new branch.
  • OR filters are mutually exclusive. Recipients only need to satisfy one condition.
  • Allowed values: "AND", "OR".
// Users must satisfy both filters to be included.
// Notice the AND operator is not required

"filters": [
{"field": "tag", "key": "level", "relation": "=", "value": "10"},
{"field": "session_count", "relation": ">", "value": "1"}
]

// The same example using the AND operator. This is not required.
"filters": [
{"field": "tag", "key": "level", "relation": "=", "value": "10"},
{"operator": "AND"},
{"field": "session_count", "relation": ">", "value": "1"}
]

field
object

tag

Target based on custom user Tags.
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)
    • time_elapsed_gt/lt fields correspond to Time Operators and require a paid plan.
  • 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"}
]

country

Country code of your Users. Uses ISO 3166-1 Alpha-2 format (two-letter country code).
  • relation = "=", "!=", "in_array", or "not_in_array"
  • value = Two-letter country code in uppercase. Example: "US", "GB", "CA". For "in_array" and "not_in_array", the value should be a comma-separated list of values.
    "filters": [
      {"field": "country", "relation": "=", "value": "US"},
      {"operator": "OR"},
      {"field": "country", "relation": "in_array", "value": "MA,GB,LK"}
    ]
    

last_session

Time since the Subscription last used the app (in hours_ago).
  • 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

Time since the User first used the app or was created within OneSignal (in hours_ago).
  • 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

Total number of sessions by the Subscription.
  • relation = ">", "<", "=" or "!="
  • value = number of sessions. Example: "1"
    "filters": [
      {"field": "session_count", "relation": ">","value": "5"}
    ]
    

session_time

Total time the Subscription has spent in the app, in seconds.
  • 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

User’s language code (e.g., “en”). See Multi-Language Messaging for details and supported language codes.
  • relation = "=", "!=", "in_array", or "not_in_array"
  • value = 2 character language code. Example: "en". For "in_array" and "not_in_array", the value should be a comma-separated list of values.
    "filters": [
      {"field": "language", "relation": "=","value": "en"},
      {"operator": "OR"},
      {"field": "language", "relation": "in_array","value": "es,fr,de"}
    ]
    

app_version

App version set on the Subscription.
  • relation = ">", "<", "=" or "!="
  • value = app version. Example: "1.0.0"
    "filters": [
      {"field": "app_version", "relation": "=","value": "1.0.1"}
    ]
    

location

Target by GPS coordinates and radius. Location tracking must be turned on and accepted by the user. See Location-Triggered Notifications for details.
  • radius = in meters
  • lat = latitude
  • long = longitude
    "filters": [
      {"field": "location", "radius": "1000","lat": "37.77", "long":"-122.43"}
    ]
    

Craft your message

Each channel has its own set of fields. At a minimum, you need the following to send a displayable message:
  • Push and SMS use contents
  • Email uses email_subject and email_body
  • Or reuse template_id if you created templates.
// Message request body
{
  "contents": {
    "en": "Hello, world",
    "es": "Hola Mundo",
    "fr": "Bonjour le monde",
    "zh-Hans": "你好世界"
  }
}
For advanced customization—like adding images, buttons, sounds, or tracking—see the channel-specific options below.

Push notification options

Below are the most common parameters for push notifications. For the full list of options, see the Push notification reference.

Content and text

Appearance

Delivery and priority

Data and extras

  • data – Custom key-value pairs sent to your app.
  • url – Opens when notification is tapped.

Email options

Content

Appearance

Sender info

SMS/MMS options

Content

Media (MMS only)

Sender info


Schedule and per-user delivery

By default, messages are sent immediately. You can schedule delivery in advance and optimize timing per user based on their local timezone or recent activity.
send_after
string
Schedule delivery for a future date/time (in UTC). The format must be valid per the ISO 8601 standard and compatible with JavaScript's Date() parser.
{
  "send_after": "2025-09-24T14:00:00-07:00"
}
delayed_option
string
Controls how push and email messages are delivered on a per-user basis. Not available for SMS.
  • "timezone": Sends at the same local time across time zones.
  • "last-active": Delivers based on each user’s most recent session.
Not compatible with push throttling. When delayed_option is set, you must also set throttle_rate_per_minute to 0.
{
  "delayed_option": "last-active",
  "throttle_rate_per_minute": 0
}
delivery_time_of_day
string
Use with delayed_option: "timezone" to set a consistent daily delivery time. Accepted formats:
  • "9:00AM" (12-hour)
  • "21:45" (24-hour)
  • "09:45:30" (HH:mm:ss)
Example — Send every day at 9 AM in each user’s local time:
{
  "delayed_option": "timezone",
  "delivery_time_of_day": "9:00AM",
  "throttle_rate_per_minute": 0
}

Submit the request

This final example sends a localized push notification to all subscribed users:
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": "你好世界"
      }
    }'
Once the request is sent, OneSignal forwards it to the appropriate downstream provider, which then delivers it to the recipient:
  • Push: FCM, APNs, or HMS
  • Email: your email service provider
  • SMS: Twilio

Handle the response

You receive a 200 response code if the request is valid and accepted.
  • If an id is returned, the message was created successfully. Save this id for future tracking and reference of message stats via the View message API.
  • If no id is returned, the message was not created, likely because there are no valid subscriptions in the target audience.
Response details by channel:
See our REST API Overview page for details on retries and rate limits.

FAQ

Can I combine include_aliases, included_segments, and filters in one request?

No. Each request must use exactly one targeting method. Mixing them returns a validation error. Choose aliases for transactional sends to specific users, segments for predefined audiences, or filters for ad-hoc audiences built with AND/OR logic.

Does target_channel: "sms" cover RCS?

Yes. The target_channel enum accepts "push", "email", and "sms". RCS messages are delivered through the SMS channel — there is no separate "rcs" value. OneSignal decides whether to send via RCS or SMS based on the recipient’s device and your sender configuration.

My request returned 200 but no id. What happened?

The request was valid but no message was created, typically because the target audience contains no valid subscriptions for the selected channel. Common causes include a segment that resolves to zero users, all targeted aliases being unsubscribed, or phone numbers that don’t match an existing SMS subscription.

Can I use // comments in the JSON request body?

No. The // comments in the examples on this page are illustrative only. Strict JSON does not support comments — strip them before sending to the API. The dashboard Sent metric is a composite. To derive it from the View message API, sum successful + failed + errored. See the Metrics glossary for the full mapping between dashboard, API, CSV, and Event Streams names.

Next steps

Refer to the channel-specific APIs to customize delivery further: