Notification Payload — What It Is, JSON Structure and Parsing

Author: IT Sectr Published: 2026-03-20 Reading time: 10 min

Notification Payload is a JSON structure that the server sends via APNS to an iOS device, defining the content of a push notification and the behavior upon receipt. The payload includes mandatory and optional keys that control text, sound, badge, media attachments, and background processing. According to Apple Developer Documentation, 2026, the maximum payload size is 4096 bytes for regular notifications and 5120 bytes for VoIP push, imposing strict limits on the amount of data transferred.

Key Takeaways

  • aps structure — a mandatory dictionary with alert, badge, sound, and content-available keys that defines the visual and audio behavior of the notification.
  • Size limit — maximum payload size is 4096 bytes for APNS and 5120 bytes for VoIP push; anything larger is rejected by Apple’s server.
  • Custom fields — any additional data is passed at the same level as aps and is available in userInfo after notification receipt.
  • Alert localization — title-loc-key, loc-key, and loc-args keys allow displaying localized text without sending different payloads for each language.
  • Request-identifier — a custom identifier in the APNS response for tracking delivery status and callbacks from Apple’s server.

What Is a Notification Payload

Notification Payload is a JSON object that the server sends to APNS (Apple Push Notification Service) for delivery to an iOS device. The payload contains all the data the system needs to display the notification: title, text, sound, badge, and metadata for background processing. The payload structure is strictly regulated by Apple and includes mandatory keys for proper system processing.

The Role of Payload in Push Delivery

When the server sends a push notification through the APNS HTTP/2 API, the request contains authorization headers and a JSON body — the payload. APNS validates the payload: if the JSON is malformed or exceeds the size limit, Apple’s server returns a 400 Bad Request error. After validation, APNS delivers the payload to the device, where iOS parses it and determines how to handle the notification — show a banner, run a background task, or play a sound.

Evolution of the Payload Format

The APNS payload format evolved from a simple text payload in iOS 2 to a multi-component JSON structure in modern versions. iOS 10 introduced support for media attachments via mutable-content, iOS 12 added notification grouping via thread-id, and iOS 15 introduced supports-live-activities for Live Activities. Today, a payload can contain up to 15 different keys depending on the desired notification behavior.

APNS Payload Structure: Mandatory and Optional Keys

The root object of the payload contains an aps dictionary and optional custom fields at the top level. The aps dictionary is the only mandatory element, but inside it various key combinations can appear depending on the notification type: alert, badge, sound, content-available, mutable-content, interruption-level, and others.

aps KeyTypePurpose
alertString or DictionaryNotification text or an object with title, subtitle, body, and localization
badgeNumberNumber on the app icon; 0 removes the badge
soundStringSound file name or default for the system sound
content-availableNumber (1)Background activation flag; 1 = silent push
mutable-contentNumber (1)Flag to activate Service Extension for content modification
categoryStringCategory identifier for buttons and Content Extension
thread-idStringGroup identifier for notification grouping
interruption-levelStringInterruption level: passive, active, time-sensitive, critical
relevance-scoreNumber (0–1)Notification priority for the smart ranking system

The alert Key: String and Dictionary Format

The alert key can be a simple string (which becomes the notification body) or a dictionary with title, subtitle, and body fields. The dictionary format allows setting the title and subtitle separately from the main text. For localized notifications, use title-loc-key, title-loc-args, loc-key, and loc-args keys, which reference the app’s Localizable.strings. This allows sending a payload without language-specific text — the app substitutes the translation.

Interruption Management: interruption-level and relevance-score

Starting with iOS 15, Apple introduced the Focus Mode mechanism, which requires developers to specify the notification interruption level. interruption-level accepts the following values: passive (no sound, no screen wake), active (standard behavior), time-sensitive (breaks through Focus, requires special entitlement), and critical (medical/emergency situations). The relevance-score key (0–1) helps the Focus system rank notifications within a single category.

Notification Grouping via thread-id

The thread-id key groups notifications in the Notification Center. All notifications with the same thread-id are displayed as a single group that the user can expand. This is especially useful for messengers where messages from one contact are grouped together, or for apps that send many notifications of the same type.

Custom Fields and Data Transfer

Custom fields are any keys outside the aps dictionary that the developer adds to pass additional data to the device. The server includes them in the root JSON object of the payload, and the app retrieves them via userInfo in UNNotificationContent. Custom fields must not duplicate key names from aps to avoid parsing conflicts.

Limitations on Custom Data

The main limitation is that the total payload size must not exceed 4096 bytes. Custom fields compete for this limit with the mandatory aps keys, so it is important to minimize the size of transmitted data. Use short key names (e.g., “uid” instead of “user-id”), avoid large JSON structures, and pass only identifiers rather than full data objects.

Security and Validation of Custom Fields

Custom fields come from the server and should not be trusted without verification. Always validate the types and values of custom fields when parsing: check for key existence via optional binding, cast to the expected type using as? String/Int/Dictionary, and handle the nil case. Never use force unwrap (!) for data from the payload — the server may send invalid data, causing the app to crash.

json
{
    "aps": {
        "alert": {
            "title": "New message",
            "body": "Hello! How are you?"
        },
        "badge": 5,
        "sound": "default",
        "category": "message",
        "thread-id": "chat_4521",
        "mutable-content": 1
    },
    "sender-id": "user_789",
    "chat-id": "chat_4521",
    "message-type": "text",
    "image-url": "https://cdn.example.com/img.jpg"
}

Custom Field Naming Recommendations

Use a consistent naming style for custom fields across all payloads in the project. kebab-case (message-type) or camelCase (messageType) — both approaches are acceptable, but consistency within the project is important. Avoid long names: “uid” instead of “user-identifier”, “img” instead of “profile-image-url”. Every character in a key name consumes one byte of the 4096 limit.

Payload Examples for Different Notification Types

Different scenarios of push notifications require different key combinations in the payload. Let’s look at several typical examples: a simple text notification, a localized notification, a Silent Push, and a Rich Notification with a media attachment.

Simple Text Notification

A basic payload with text and sound — the minimum configuration to display a notification to the user. Alert as a string provides a short message, sound default plays the standard system sound. Badge is optional and sets the counter on the app icon. category and thread-id are added for grouping and interactivity.

json
{
    "aps": {
        "alert": "Reminder: meeting in 15 minutes",
        "badge": 3,
        "sound": "default"
    }
}

Localized Notification

To send notifications to devices with different languages, use localization keys instead of hardcoded text. title-loc-key references a key in the app’s Localizable.strings, and title-loc-args supplies arguments. This allows sending a single payload to all devices, while the app displays the text in the appropriate language.

json
{
    "aps": {
        "alert": {
            "title-loc-key": "NEW_MESSAGE_TITLE",
            "title-loc-args": ["Anna"],
            "loc-key": "NEW_MESSAGE_BODY",
            "loc-args": ["Hello!"]
        },
        "sound": "message.caf"
    }
}

Silent Push with Background Sync

For background synchronization without showing a notification, use content-available: 1 with no alert. Custom fields specify the operation type and data for processing. The system activates the app in the background, calls didReceiveRemoteNotification with fetchCompletionHandler, and the app performs synchronization.

json
{
    "aps": {
        "content-available": 1
    },
    "sync-type": "invalidate-cache",
    "timestamp": "2026-07-03T12:00:00Z"
}

Rich Notification with Image

To display a media attachment, mutable-content: 1 is needed to activate the Service Extension, along with an image URL in a custom field. mutable-content: 1 signals the system to launch UNNotificationServiceExtension, which downloads the image from the URL and adds it as a UNNotificationAttachment. The category key specifies a registered category for displaying action buttons.

json
{
    "aps": {
        "alert": {
            "title": "New product",
            "body": "Check out the new collection"
        },
        "category": "product",
        "mutable-content": 1
    },
    "media-url": "https://cdn.example.com/product.jpg"
}

Processing and Parsing the Payload in the App

UNNotificationContent.userInfo contains the full dictionary of the received payload after system processing. The app accesses the payload in the UNUserNotificationCenter delegate when receiving a notification (in the foreground), when tapping on a notification, as well as in Service Extension and Content Extension. Correct parsing is essential for extracting custom data and determining the next actions.

Parsing in AppDelegate When Tapping a Notification

When the user taps a notification, the system calls the didReceive response method in UNUserNotificationCenterDelegate. response.notification.request.content.userInfo contains the full payload. The developer extracts custom fields, determines the action type (e.g., open a chat, navigate to a product), and triggers the appropriate navigation in the app.

swift
func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
) {
    let userInfo = response.notification
        .request.content.userInfo

    guard let chatId = userInfo["chat-id"] as? String
    else {
        completionHandler()
        return
    }

    let messageType = userInfo["message-type"]
        as? String ?? "text"

    NavigationRouter.shared.navigate(
        to: .chat(chatId: chatId,
                  messageType: messageType))
    completionHandler()
}

Payload Validation in Service Extension

The Service Extension receives the payload before the notification is displayed and can modify it. Payload validation is the first step in didReceive: check for mandatory custom fields, validate the attachment URL, and verify data types. If the payload is invalid, call the completion handler with the original content immediately to avoid wasting time on unnecessary processing.

Payload Logging and Monitoring

For debugging push notifications in production, use structured payload logging. OSLog allows logging the payload with the “notifications” category at the debug level. On the server side, monitor APNS responses: a successful response contains apns-id for matching with the sent payload, while a 400 error indicates malformed JSON or size exceedance.

Frequently Asked Questions

What is the maximum APNS payload size?

The maximum payload size is 4096 bytes for regular push notifications and 5120 bytes for VoIP push (PushKit). Exceeding this limit causes APNS to return a 400 Bad Request error. Size is counted in bytes, not characters — account for UTF-8 encoding.

How to send a localized notification in multiple languages?

Use the loc-key, title-loc-key, loc-args, and title-loc-args keys inside alert. The app substitutes the translation from its Localizable.strings based on the device language. This allows sending a single payload to all devices regardless of their language.

What is the difference between content-available and mutable-content?

content-available activates the app in the background for data processing (silent push) without showing a notification. mutable-content activates the Service Extension to modify content before display. Both keys can be used together for background processing and subsequent notification modification.

How to verify that the server sent a correct payload?

Use APNS Sandbox for testing and check Apple’s server HTTP response: 200 OK means successful delivery. For structure validation, use JSON schemas in your CI/CD pipeline. In Xcode, send test notifications via the simulator using xcrun simctl push.

What is apns-id in Apple’s server response?

apns-id is a unique push notification identifier in the APNS system, returned in the response upon successful delivery. It is used for tracking delivery via the Logs API and for debugging. The server should store apns-id for each sent notification.

Summary

  • Notification Payload — a JSON structure for push notifications with a mandatory aps dictionary that defines text, sound, badge, and background processing.
  • Size limit — 4096 bytes for APNS, 5120 bytes for VoIP; exceedance returns a 400 Bad Request error from Apple’s server.
  • aps dictionary includes alert, badge, sound, content-available, mutable-content, category, thread-id, interruption-level, and relevance-score keys.
  • Custom fields are passed outside aps and extracted via userInfo; always validate types and values when parsing.
  • Localization is implemented via loc-key and title-loc-key, referencing the app’s Localizable.strings for translation substitution.
  • interruption-level manages notification behavior in Focus mode: passive, active, time-sensitive, or critical.
  • Notification Payload is the foundation of the entire push notification system; the correctness of delivery, display, and processing of each notification on the device depends on it.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also