Batching Experience Events
By default, the Adobe Experience Platform Edge Network extension sends each Experience Event to the Edge Network in its own request. Event batching combines multiple queued Experience Events into a single Edge Network request, which reduces the number of network calls made by your app and can lower the overall response time when several events are queued. Batching is disabled by default and applies only to the event types you explicitly allow. This tutorial explains how to enable batching, select which events are combined, and verify the result.
Prerequisites
Before continuing with this tutorial, please ensure that the Edge extension version meets the minimum requirements:
- Edge extension version >= 3.1.0 (Android) and >= 5.1.0 (iOS).
How batching works
When batching is enabled, the Edge Network extension combines the Experience Events at the front of its send queue into a single request, up to a configurable maximum. Events are combined only when they are consecutive in the queue, share the same datastream configuration, and have an xdm.eventType that is present on the allowlist. Consent and identity reset requests are never batched.
Batching does not change how you send events with the sendEvent API, and it does not change how responses are handled. Each event in a batch is tracked individually, so response handles, errors, and completion handlers are still delivered per event.
Configure batching
Batching is configured through a single edge.batching object. The same format applies whether you provide the object at runtime using the updateConfiguration API or bundle it with your app.
{
"enabled": true,
"maxBatchSize": 10,
"wildcards": [
{ "xdmEventType": "media.*", "enabled": false }
],
"commerce": [
{ "xdmEventType": "commerce.productListAdds", "enabled": true },
{ "xdmEventType": "commerce.purchases", "enabled": true }
]
}
enabledfalse (the default), each event is sent in its own request.maxBatchSize10, must be a positive value, and is capped at 20.wildcardsxdmEventType is matched as a pattern. See select which events to batch for more information.commerce) is used only for readability; all groups are combined into a single allowlist._metaEach allowlist entry is an object with an xdmEventType value and an enabled flag. An event is batched only when its xdm.eventType matches an entry whose enabled value is true; an event type that is not listed, or is listed only with enabled set to false, is always sent in its own request.
Override the configuration at runtime
Provide the edge.batching object at runtime using the updateConfiguration API. This overrides the current configuration and takes effect immediately:
data-slots=heading, code
data-repeat=2
iOS Swift
let batching: [String: Any] = [
"enabled": true,
"maxBatchSize": 10,
"commerce": [
["xdmEventType": "commerce.purchases", "enabled": true]
]
]
MobileCore.updateConfigurationWith(configDict: ["edge.batching": batching])
Android Kotlin
val batching = mapOf(
"enabled" to true,
"maxBatchSize" to 10,
"commerce" to listOf(
mapOf("xdmEventType" to "commerce.purchases", "enabled" to true)
)
)
MobileCore.updateConfiguration(mapOf("edge.batching" to batching))
A batching configuration provided through the updateConfiguration API takes precedence, as a whole, over the bundled file described below.
Bundle the configuration in your app
If you are not providing the configuration through the updateConfiguration API, you can bundle it with your app. Create a file named ADBMobileEdgeBatchingConfig.json that contains the batching object directly, without the edge.batching key:
{
"_meta": { "schemaVersion": 1 },
"enabled": true,
"maxBatchSize": 10,
"wildcards": [
{ "xdmEventType": "media.*", "enabled": false }
],
"commerce": [
{ "xdmEventType": "commerce.purchases", "enabled": true }
]
}
The Edge Network extension loads this file automatically at startup; no additional code is required. The bundled file is used only when an edge.batching object is not present in your configuration.
Android
Place ADBMobileEdgeBatchingConfig.json in your app module's src/main/assets directory. If the assets directory does not already exist, create it, then perform a clean build so the file is packaged with the app.
iOS
Add ADBMobileEdgeBatchingConfig.json to your Xcode project (for example, by dragging it into the Project navigator). In the file inspector, add it to your app target, and confirm the file appears under the target's Build Phases > Copy Bundle Resources.
Select which events to batch
An event is added to the allowlist by its xdm.eventType. List the exact event types under any extension group, or use the wildcards array to match a family of event types with a single *.
prefix.*prefix., for example commerce.* matches commerce.purchases.*.suffix.suffix, for example *.purchases matches commerce.purchases.*Batch custom event types
Batching matches an event by its xdm.eventType, so any value can be batched — including custom event types your app defines. Add the custom type to the allowlist under any group, or match a family of them with a wildcard such as myapp.*:
{
"enabled": true,
"maxBatchSize": 10,
"myApp": [
{ "xdmEventType": "myapp.levelComplete", "enabled": true },
{ "xdmEventType": "myapp.itemPurchased", "enabled": true }
]
}
Send the event as usual, setting eventType in the XDM data to your custom value:
data-slots=heading, code
data-repeat=2
iOS Swift
// Create Experience event from dictionary
let xdmData: [String: Any] = [
"eventType": "myapp.levelComplete"
]
let experienceEvent = ExperienceEvent(xdm: xdmData)
Edge.sendEvent(experienceEvent: experienceEvent) { (handles: [EdgeEventHandle]) in
// Handle the Edge Network response
}
Android Kotlin
// Create experience event from Map
val xdmData = mutableMapOf<String, Any>()
xdmData["eventType"] = "myapp.levelComplete"
val experienceEvent = ExperienceEvent.Builder()
.setXdmSchema(xdmData)
.build()
Edge.sendEvent(experienceEvent) {
// Handle the Edge Network response
}
Complete configuration reference
The following is a complete edge.batching configuration for the standard Adobe extensions, including the _meta block, an example wildcard, and every event type these extensions send. Use it as a starting point and set enabled to true only for the event types you want to batch. Copy the configuration from the section below into a file named ADBMobileEdgeBatchingConfig.json.
data-slots=heading, code
Complete edge.batching configuration
{
"_meta": {
"schemaVersion": 1,
"description": "Edge event batching configuration. Extension keys map to arrays of event objects. The Edge SDK parses only `xdmEventType` and `enabled`, ORs duplicates across extensions, and builds a flat allowlist of enabled xdm.eventType values. At send time, an outgoing edge/requestContent Experience Event is batched iff its xdm.eventType matches an enabled exact entry or an enabled wildcard. `purpose` and this `_meta` are ignored at parse time.",
"matchKey": "xdm.eventType"
},
"wildcards": [
{
"xdmEventType": "media.*",
"enabled": false,
"purpose": "EXAMPLE wildcard: set enabled to true to batch every media.* event in one line instead of listing them individually. Disabled here — the explicit edgeMedia entries below govern batching."
}
],
"edgeBridge": [
{ "xdmEventType": "analytics.track", "enabled": true, "purpose": "Analytics trackAction/trackState forwarded to Edge via Edge Bridge." }
],
"edgeMedia": [
{ "xdmEventType": "media.sessionStart", "enabled": true, "purpose": "Streaming session begins." },
{ "xdmEventType": "media.sessionComplete", "enabled": true, "purpose": "Media reached its natural end." },
{ "xdmEventType": "media.sessionEnd", "enabled": true, "purpose": "Session terminated without completing." },
{ "xdmEventType": "media.play", "enabled": true, "purpose": "Player entered or resumed active playback." },
{ "xdmEventType": "media.pauseStart", "enabled": true, "purpose": "Player paused or seeking." },
{ "xdmEventType": "media.ping", "enabled": true, "purpose": "Periodic playhead heartbeat — highest-frequency event." },
{ "xdmEventType": "media.error", "enabled": true, "purpose": "Playback error." },
{ "xdmEventType": "media.bufferStart", "enabled": true, "purpose": "Player entered buffering." },
{ "xdmEventType": "media.bitrateChange", "enabled": true, "purpose": "Streaming bitrate changed." },
{ "xdmEventType": "media.adBreakStart", "enabled": true, "purpose": "Ad break (pod) started." },
{ "xdmEventType": "media.adBreakComplete", "enabled": true, "purpose": "Ad break finished." },
{ "xdmEventType": "media.adStart", "enabled": true, "purpose": "Individual ad started." },
{ "xdmEventType": "media.adSkip", "enabled": true, "purpose": "Ad skipped." },
{ "xdmEventType": "media.adComplete", "enabled": true, "purpose": "Ad finished." },
{ "xdmEventType": "media.chapterStart", "enabled": true, "purpose": "Chapter/segment started." },
{ "xdmEventType": "media.chapterSkip", "enabled": true, "purpose": "Chapter skipped." },
{ "xdmEventType": "media.chapterComplete", "enabled": true, "purpose": "Chapter finished." },
{ "xdmEventType": "media.statesUpdate", "enabled": true, "purpose": "Custom player state (for example, fullscreen or mute) start or end." }
],
"messaging": [
{ "xdmEventType": "pushTracking.applicationOpened", "enabled": true, "purpose": "Push notification opened (application launch from push)." },
{ "xdmEventType": "pushTracking.customAction", "enabled": true, "purpose": "Push notification custom-action button interaction." },
{ "xdmEventType": "decisioning.propositionFetch", "enabled": true, "purpose": "Personalization query for in-app, content-card, and code-based propositions." },
{ "xdmEventType": "decisioning.propositionDisplay", "enabled": true, "purpose": "Proposition displayed (impression) tracking." },
{ "xdmEventType": "decisioning.propositionInteract", "enabled": true, "purpose": "Proposition interaction (tap) tracking." },
{ "xdmEventType": "decisioning.propositionDismiss", "enabled": true, "purpose": "Proposition dismissed tracking." },
{ "xdmEventType": "decisioning.propositionTrigger", "enabled": true, "purpose": "Proposition trigger tracking." },
{ "xdmEventType": "decisioning.propositionDisqualify", "enabled": true, "purpose": "Proposition disqualify tracking." },
{ "xdmEventType": "decisioning.propositionSuppressDisplay", "enabled": true, "purpose": "Proposition suppressed-display tracking." },
{ "xdmEventType": "liveActivity.pushToStart", "enabled": true, "purpose": "Live Activity push-to-start token sync." },
{ "xdmEventType": "liveActivity.start", "enabled": true, "purpose": "Live Activity start tracking." },
{ "xdmEventType": "liveActivity.updateToken", "enabled": true, "purpose": "Live Activity per-activity update token sync." }
],
"optimize": [
{ "xdmEventType": "decisioning.propositionFetch", "enabled": true, "purpose": "Personalization decisions query for decision scopes. (Same xdm.eventType is also produced by messaging.)" },
{ "xdmEventType": "decisioning.propositionDisplay", "enabled": true, "purpose": "Proposition display tracking. (Also produced by messaging.)" },
{ "xdmEventType": "decisioning.propositionInteract", "enabled": true, "purpose": "Proposition interaction tracking. (Also produced by messaging.)" }
],
"edgeIdentity": [
{ "xdmEventType": "profile.updateAttributes", "enabled": true, "purpose": "Profile attribute updates (for example, timezone) sent to Edge." }
],
"places": [
{ "xdmEventType": "location.entry", "enabled": true, "purpose": "Geofence POI entry." },
{ "xdmEventType": "location.exit", "enabled": true, "purpose": "Geofence POI exit." }
],
"lifecycle": [
{ "xdmEventType": "application.launch", "enabled": true, "purpose": "App foreground/launch — reaches Edge only via a 'Forward to Edge Network' rule; the forwarded event carries this xdm.eventType." },
{ "xdmEventType": "application.close", "enabled": true, "purpose": "App background/close — reaches Edge only via a 'Forward to Edge Network' rule; the forwarded event carries this xdm.eventType." }
],
"offerDecisioning": [
{ "xdmEventType": "personalization.request", "enabled": true, "purpose": "Offer prefetch personalization query." }
],
"analyticsEdge": [
{ "xdmEventType": "legacy.analytics", "enabled": true, "purpose": "Legacy Analytics track event forwarded to Edge." }
]
}
Send events
Batching is automatically applied to the Experience Events you send with the sendEvent API. When batching is enabled and their event types are on the allowlist, events sent in succession are combined into a single request.
data-slots=heading, code
data-repeat=2
iOS Swift
// Create Experience event from dictionary
let xdmData: [String: Any] = [
"eventType": "commerce.purchases"
]
let experienceEvent = ExperienceEvent(xdm: xdmData)
Edge.sendEvent(experienceEvent: experienceEvent) { (handles: [EdgeEventHandle]) in
// Handle the Edge Network response
}
Android Kotlin
// Create experience event from Map
val xdmData = mutableMapOf<String, Any>()
xdmData["eventType"] = "commerce.purchases"
val experienceEvent = ExperienceEvent.Builder()
.setXdmSchema(xdmData)
.build()
Edge.sendEvent(experienceEvent) {
// Handle the Edge Network response
}
Response outcomes
The response for a batch is applied per event, matching the behavior of events sent individually:
2xx / 207429, recoverable 5xx, or timeoutRetry-After. Events are not lost.400403, 404, 422, and so on)Verify batching
Set the SDK log level to verbose and confirm that events are combined. When a batch is formed, a log entry similar to the following is printed:
Edge/EdgeHitProcessor - Processing batch of 4 ExperienceEvent(s).
You can also use a network debugging tool to confirm that the combined events were delivered in a single request to the Edge Network.