Send push, email, and SMS notifications for social actions including likes, follows, direct messages, and competitive gaming events. Uses custom_data for personalized, backend-triggered alerts.
Use this file to discover all available pages before exploring further.
Notify users when something happens that involves them: a like, a reply, a follow, an incoming message, or a competitive event in a game. These notifications drive re-engagement even when users aren’t currently active in your app.
OneSignal is not designed for real-time communication. Push notifications are best used as a fallback when users are not actively in the app. For real-time in-app messaging, use your app’s existing messaging layer and trigger OneSignal notifications only when the recipient is offline or inactive.
An external_id set for every user so you can target them by your own identifier. See Users & Aliases.
A backend that can detect social actions and call the OneSignal API. See REST API overview.
Templates created in the dashboard if you plan to use custom_data for personalization. A template_id is required to use custom_data.
Keep custom_data payloads under 2KB. The custom_data field has a hard size limit. Sending large payloads — full conversation lists, leaderboard arrays, base64 images, or HTML — risks truncation or rejection. For rich content, pass an identifier (e.g., digest_id or summary_url) and have the recipient’s device fetch the full payload from your backend on tap. See Personalize messages with API custom_data for the size limit details and array-iteration patterns.
Send a push notification when a user is involved in a social action. Use custom_data to inject the sender’s name, avatar, and relevant context into the message at send time. No data is stored in OneSignal.
OneSignal renders the template at send time using the custom_data values. The sender’s name and avatar appear in the notification without being stored in OneSignal.
4
Optional: add email and SMS fallbacks
To reach users who have push disabled or whose notification went undelivered, see Email and SMS fallbacks below.
Use | default: filters in every Liquid placeholder so the message still reads naturally if a field is missing. For example: {{ message.custom_data.sender_name | default: "Someone" }}. See Using Liquid syntax for more filters.
Avatar and image URL requirements. The sender_avatar URL (and any other notification image) must be:
HTTPS — iOS rejects HTTP URLs.
Publicly accessible — APNs and FCM cannot send authenticated requests.
Under ~1 MB — iOS limit is 10 MB but practical delivery windows favor smaller assets.
Served with the correct Content-Type header — image/jpeg, image/png, etc.
Hosting from a CDN with cached headers is the safest setup.
A viral post can generate thousands of like events per second. Don’t send a push for each one — that floods the recipient and gets your app muted or uninstalled. The pattern:
Accumulate counts on your backend (e.g., a Redis counter keyed by recipient + post).
After a quiet window (10 minutes is a reasonable default), send a single digest push: “12 people liked your post.”
If more likes arrive after the digest, start a fresh window — don’t immediately push again.
The same logic applies to comments, follows, and reactions. See Throttling for OneSignal-side rate limits if your backend can’t debounce.
Notify a user when they receive a new direct message, and deep link them directly into the conversation.
Only send a push when the recipient is not actively in the chat. Notifying someone who is already reading the conversation creates a poor experience. Use your app’s own logic to check whether the recipient is currently active before triggering a notification. OneSignal does not track whether a user is currently using your app.
When User A sends a message to User B, check whether User B is currently active in that conversation. If User B is offline or not in the conversation, proceed to send a push.
2
Avoid sending one push per message
If User A sends several messages in a row, wait a short period after the last message before triggering a notification. Here is how to do this in your backend:
When the first message arrives, start a timer (for example, 60 seconds).
If another message arrives before the timer runs out, reset it.
When the timer runs out with no new messages, send a single push summarizing the unread count.
OneSignal does not consolidate multiple API calls automatically, so if you call the API five times, five notifications are sent.
3
Send the push notification
Send a push to User B with a deep link to the conversation:
Your app reads data.conversation_id on notification open and navigates to the correct screen. See Deep linking for platform-specific setup.
4
Optional: add email and SMS fallbacks
To reach users who have push disabled or whose notification went undelivered, see Email and SMS fallbacks below.
Group notifications by conversation natively. Backend debouncing reduces how many notifications fire, but iOS and Android can also visually collapse multiple notifications into a single thread. Set a thread or collapse identifier (e.g., the conversation_id) so the OS groups messages from the same chat. See Notification grouping.
Update the badge count on each new message. Most chat apps want the iOS/Android badge to reflect total unread messages across all conversations. Pass the unread count via the API on each push so the badge stays accurate even when a user clears one notification but has others outstanding. See Badges.
Lock Screen privacy. iOS shows notification content on the Lock Screen by default — including the message preview (“Anna: ‘Hey, you around?’”). For messaging apps with sensitive content (health, finance, dating, professional), consider sending a generic preview (“New message from Anna”) and let users opt into full previews via your in-app settings.
Competitive games benefit from time-sensitive alerts that create urgency. Use custom_data to make these notifications feel specific and personal. A notification that names the attacker or shows exact resource counts is far more compelling than a generic alert.
The url deep links the player directly to the defend screen. The data object passes context to your app’s notification handler so it can load the correct battle state.
4
Optional: add email and SMS fallbacks
To reach players who have push disabled or whose notification went undelivered, see Email and SMS fallbacks below.
Respect quiet hours for non-urgent alerts. Base-attack pushes at 3 AM local time are a known opt-out driver. Split your gaming alerts into two tiers:
Time-critical (guild war starting in 30 minutes, base under attack right now) — send immediately regardless of local time.
Most gaming-alert opt-outs come from the second category sending at the wrong time, not from the first category being too frequent.
Consider Live Activities for in-progress events. For ongoing matches, raids, or live events on iOS 16.1+, a Live Activity on the Lock Screen and Dynamic Island is often a better experience than repeated push notifications updating the same context. Use Live Activities for the live state (“23 minutes remaining, you’re #4”) and reserve push for milestone or completion moments.
{{ message.custom_data.overtaker_name | default: "Another player" }} just passed you. You dropped from #{{ message.custom_data.previous_rank }} to #{{ message.custom_data.current_rank }} on the leaderboard.
{{ message.custom_data.challenger_name | default: "A player" }} challenged you to a {{ message.custom_data.game_mode | default: "duel" }}. You have {{ message.custom_data.hours_to_respond | default: "24" }} hours to accept.
Add an email or SMS fallback to any notification type to reach users who have push disabled or whose notification was not delivered. Use the View Message API to check for a confirmed receipt or click. If none is recorded within your delay window, send a follow-up using the same custom_data approach with an Email or SMS template.
Email
SMS
Social activityBest for high-value actions like mentions and direct replies.
JSON
{ "app_id": "YOUR_APP_ID", "template_id": "YOUR_EMAIL_TEMPLATE_ID", "include_aliases": { "external_id": ["user_a"] }, "custom_data": { "sender_name": "Anna", "sender_avatar": "https://cdn.yourapp.com/avatars/anna.jpg", "action": "liked your post", "post_title": "My Hawaii trip", "post_url": "https://yourapp.com/posts/xyz789" }}
Email template example (subject):
Liquid
{{ message.custom_data.sender_name | default: "Someone" }} {{ message.custom_data.action | default: "interacted with your post" }}
Direct messagesBest as a daily digest of unread conversations rather than per-message alerts.
You have {{ message.custom_data.unread_count | default: "new" }} unread message{% if message.custom_data.unread_count != "1" %}s{% endif %}
Email template example (body, iterating over the conversations array):
Liquid
<h2>You have {{ message.custom_data.unread_count }} unread messages</h2><ul> {% for conversation in message.custom_data.conversations %} <li> <strong>{{ conversation.sender_name }}:</strong> "{{ conversation.preview }}" <a href="{{ conversation.url }}">Open</a> </li> {% endfor %}</ul>
See Personalize messages with API custom_data for the full array-iteration reference, including nested objects and conditional rendering.GamingBest for non-urgent recaps like weekly leaderboard summaries, guild war results, or milestone unlocks.
{{ message.custom_data.sender_name | default: "Someone" }} mentioned you in "{{ message.custom_data.post_title | default: "a post" }}". Tap to reply: {{ message.custom_data.post_url }}
Direct messagesBest for individual missed messages rather than bulk message sequences.
New message from {{ message.custom_data.sender_name | default: "a user" }}: "{{ message.custom_data.message_preview }}". Reply in the app: {{ message.custom_data.conversation_url }}
GamingBest for time-critical events like base attacks where the player needs to act immediately.
⚔️ ATTACK! {{ message.custom_data.attacker_name | default: "An enemy" }} is raiding your base. {{ message.custom_data.gold | default: "0" }} gold at risk. Defend now: {{ message.custom_data.game_url }}
Give users control over their fallback preferences. An opt-in like “Notify me by SMS if I miss a message” helps prevent unwanted messages for users who intentionally have push disabled.
Can OneSignal send notifications in real time, like a chat app?
No. Push notifications are delivered through Apple (APNs) and Google (FCM) infrastructure, which introduces variable delivery times and no delivery guarantees. Use your app’s existing messaging layer for real-time in-app communication and use OneSignal as a fallback when the recipient is not actively in the app.
How do I avoid notifying a user who is already in the app?
OneSignal does not track whether a user is currently active in your app. Your own backend logic must determine whether to trigger the notification. Only call the OneSignal API when you’ve confirmed the recipient is offline or not in the relevant screen.
How do I prevent multiple notifications from rapid message sequences?
Add a short delay in your backend before sending a notification. When the first message arrives, start a timer. If another message comes in before it runs out, reset it. When the timer runs out, send a single push with the unread count. OneSignal does not consolidate multiple API calls automatically, so if you call the API five times, five notifications are sent.
Is custom_data saved to the user’s profile after the message sends?
No. custom_data is ephemeral and exists only during the API request, used to render the template at send time. It is not stored in OneSignal and cannot be reused in future messages or Journeys. For persistent user data, use Tags.
Yes. Pass multiple external_id values in the include_aliases array. If each recipient needs different personalized content (for example, different attacker names), use the bulk personalization pattern in custom_data. See Personalize messages with API custom_data for the full approach. The exact per-call recipient cap and rate limits are documented on the Create Message API reference — for very large audiences, segment-based targeting is more efficient than passing thousands of external_id values per call.
Do I need to localize messages for international users?
Yes for any audience that spans languages. The headings and contents fields accept multiple language codes (e.g., { "en": "...", "es": "...", "fr": "..." }) and OneSignal selects the right variant based on each subscription’s language. The same pattern applies to template fields. See Multi-language messaging for the full reference, including fallback language behavior.