custom_data field in the Create Message API to send dynamic data from your backend and render it inside templates using Liquid syntax.
custom_data:
- Is message-specific
- Is not saved to user profiles and can’t be reused in later messages
- Must be used with a
template_id - Has a size limit set by OneSignal: 2KB for push and SMS, 10KB for email
When to use custom_data
Use custom_data when you need:
- Data changes per message (order totals, cart items, balances)
- You need arrays (product lists, line items, recommendations)
- Data should not persist (one-time codes, temporary URLs)
- You send backend-triggered messages
- You want bulk personalization in one API request
custom_data is not the right fit when you’d send the same large dataset on every request (for example, a full product catalog that Liquid picks from per recipient). Use Dynamic Content for shared content, or Data Feeds to fetch data from your API at send time (email in Journeys only).
custom_data size limits
OneSignal sets the maximum size of custom_data. This limit is not set by Apple (APNs) or Google (FCM).
custom_data object larger than the limit for the channel are rejected by the API.
How this relates to APNs and FCM payload limits
custom_data is not sent to devices. OneSignal uses it on its servers to render your template, then sends only the rendered message to APNs or FCM. APNs and FCM each limit the delivered push payload to 4,096 bytes. That limit applies to the final notification (title, message, Additional Data, and other fields), not to custom_data.
custom_data is different from the data field. The data field (Additional Data) is delivered to the device and has its own 2,048-byte limit. See Push overview.If you’re hitting the limit
- Send only the fields your template uses, not the full object from your backend (for example, 3 fields from an order instead of the whole order).
- Shorten keys and limit arrays to the items you display.
- Move static content, like HTML, into the template.
- For data shared across many sends, use Dynamic Content instead. For email sent through Journeys, Data Feeds can fetch data from your API at send time.
How custom_data personalization works
Adding custom_data to messages requires a few steps:
Create a template
Add Liquid placeholders
Send custom_data in your API request
template_id- The ID of the templatecustom_data- The data object- Audience targeting (
include_player_ids,include_aliases, or segments)
Data patterns
Common examples of data patterns you can use withcustom_data.
Flat JSON example
Use simple key-value pairs for basic personalization like names, IDs, URLs, or any single-value data. Use case: Transactional messages (invoices, receipts, confirmations) where each field contains a single value. Template:Array data example
Pass arrays of objects to work with multiple items like cart products, order line items, or recommendations. Arrays enable both direct access (indexing) and iteration (loops). Use case: Displaying product lists, leaderboards, order summaries, or any multi-item data. Indexing template (accessing first item):{{message.custom_data.cart_items.size}}— Number of items in array (returns2in this example){{message.custom_data.cart_items.first.item_name}}— First item’s name (equivalent to[0]){{message.custom_data.cart_items.last.item_name}}— Last item’s name
Bulk personalization example
Send a single API request to multiple users where each recipient sees personalized content based on theirexternal_id.
How it works:
- Structure
custom_dataas an object where keys are external_ids and values are user-specific data - In the template, use
subscription.external_idto look up the current recipient’s data - OneSignal renders the template once per recipient with their specific data
subscription.external_idcontains the current recipient’s external_id (e.g., “user123”)message.custom_data.users[subscription.external_id]looks up that user’s data from the custom_data objectuserbecomes a shorthand variable for that user’s data- Each recipient only sees their own personalized content
- John (user123): “Hi John, you have 150 points. Your level is Gold.”
- Sarah (user456): “Hi Sarah, you have 200 points. Your level is Platinum.”
- All recipients must have an
external_idset in OneSignal - Each
external_idininclude_aliasesmust have a matching key incustom_data.users - If a recipient’s external_id is missing from
custom_data, their message will have empty fields
Example: Abandoned cart with custom_data
How to build abandoned cart messages for both email and push usingcustom_data.
When to use this approach:
- Your server detects cart abandonment (e.g., 1 hour after last activity)
- Real-time cart data is in your database
- You want to display multiple products with images, names, prices
- Each user may have different items and quantities
- You want to orchestrate the message from your backend.
Example custom_data payload
This is the Create Message API request for this example.
Email template
This example shows how to build an email template that displays:- The cart item count
- Each product with image, name, quantity, and price using a for-loop
- A button that links to the customer’s unique cart URL

Example Abandoned Cart Email Template
Create the email template
Add the layout structure
- Rows 1, 2, and 4: one column with a Paragraph block
- Row 3: four columns with HTML | Paragraph | Paragraph | Paragraph
- Row 5: one column with a Button block

Starter email template for abandoned cart
Display the item count
Start the for-loop
- Begins a loop that iterates over each object in the
cartarray - Creates a temporary variable called
productthat represents the current item - Everything between
{% for %}and{% endfor %}repeats once per cart item - You can name
productanything (e.g.,item,cartItem)—just stay consistent
Display product details
- Column 2:
{{product.product_name}} - Column 3:
{{product.product_quantity}} - Column 4:
{{product.product_price}}
- On the first iteration,
product= first object in the cart array {{product.product_image}}gets the first item’s image URL- On the second iteration,
product= second object - Row repeats automatically for all cart items
End the for-loop
{% for %} must have a matching {% endfor %}. Missing this will break email rendering.Add a cart link button

Finished base email template for abandoned cart without styling
Test the template
- Setup your API request using the Example
custom_datapayload - Send yourself the email.
- Verify the data displays correctly.
Push template
Push notifications have character limits and operating system restrictions, so instead of showing all items, display the first product and indicate the total count with proper grammar. Here is an example push notification we will build:
Abandoned cart push template example
template_id in your Create message API request with the custom_data property to test.Troubleshooting & best practices
- Keep it simple: Only include data you’ll actually use in the template
- Stay within the size limit: 2KB for push and SMS, 10KB for email. Monitor payload size, especially with arrays
- Use consistent naming: Stick to
snake_caseorcamelCasethroughout - Validate before sending: Check for null values, empty arrays, and required fields
- Always use default filters for optional fields:
Liquid
- Check array size before looping:
Liquid
- Test with edge cases: empty arrays, missing fields, maximum item counts
- Log API responses server-side to catch validation errors
- Monitor message delivery rates—sudden drops may indicate Liquid errors
- Keep fallback templates ready for critical transactional messages
- Pre-format complex data in your backend rather than using complex Liquid logic
- Cache templates and reuse them across many API calls
- Consider separating high-volume transactional messages from marketing campaigns
Message sends but content is blank
Message sends but content is blank
- Verify field names match exactly between
custom_dataand template (case-sensitive) - Check for typos:
{{message.custom_data.name}}not{{message.custm_data.name}} - Use default filters to catch missing fields
- Test templates with the actual
custom_datastructure before production
API rejects the request because custom_data is too large
API rejects the request because custom_data is too large
custom_data exceeds the limit for the channel: 2KB for push and SMS, 10KB for email. This is a OneSignal limit, not an APNs or FCM limit. See custom_data size limits.Solutions:- Remove unnecessary fields from your payload
- Shorten field names and values where possible
- Limit arrays to first 3-5 items
- Move large static content (like full HTML) to your template instead
Related pages
Message personalization
Create Message API
Using Liquid syntax
Templates
support@onesignal.comPlease include:- Details of the issue you’re experiencing and steps to reproduce if available
- Your OneSignal App ID
- The External ID or Subscription ID if applicable
- The URL to the message you tested in the OneSignal Dashboard if applicable
- Any relevant logs or error messages