GitHub project

Start Live Activities

Updated August 18, 2026

Once you’ve set up Live Activities, there are two ways to start one:

  • From your server (push-to-start): You call the API when you’re ready to start an activity, and iOS creates it on the device. Your app doesn’t need to be open. This is the most common path for activities like delivery tracking and live scores, and requires iOS 17.2.
  • From your app: Start the activity through the module when something happens in the app. This is a common path for activities like a workout progress tracker.

From your server (push-to-start)

Call the start endpoint to start an activity for a profile. Customer.io sends the push-to-start message, and iOS creates the activity with the attributes and content state from your payload. Your payload must include push_payload.alert.title and push_payload.alert.body.

Push-to-start requires iOS 17.2 or later.

From your app

Start the activity through the module—when a customer places an order, for example:

if #available(iOS 16.2, *) {
    // start throws only on a genuine ActivityKit failure (Live Activities
    // disabled, unsupported device). It returns nil—logged, never thrown—if
    // the module isn't initialized or the type wasn't registered.
    guard let activity = try CustomerIO.liveActivities.start(
        DeliveryActivityAttributes(orderNumber: "CIO-1234"),
        contentState: .init(title: "Order received")
    ) else { return }

    // update and end are async.
    await activity.update(.init(title: "Out for delivery"))
    await activity.end(
        .init(title: "Delivered"),
        // Keep the final state on screen briefly before the system removes it.
        dismissalPolicy: .after(.now + 60)
    )
}

start is available on iOS 16.2 and later and returns a CIOLiveActivity handle, or nil if the module isn’t initialized or the type wasn’t registered.

The handle’s update(_:) and end(_:) are async: update changes the activity on the device but isn’t reported to Customer.io, and end(_:dismissalPolicy:) ends it locally, reports the end, and lets you control how long the final state stays on screen.

The handle’s id is the activity’s instance ID—pass it to your backend if you want your server to take over updates. If your app already starts activities with raw ActivityKit calls, pass them to CustomerIO.liveActivities.adopt(_:) so the SDK can manage future updates and the end—but note that adopt doesn’t report that activity’s original start.

Update and end the activity

Once the activity is running, your server can push subsequent updates by calling the update endpoint with the activity’s instance ID. You don’t need to manage push tokens yourself.

When your server ends an activity, iOS dismisses it from the Lock Screen. You can set a dismissal time that keeps the final state visible for a short while before the system removes it. This is useful when you want to make sure someone sees a final score or that their order was delivered.

For the full lifecycle, states, instance IDs, and how deliveries appear in Customer.io, see the API and payload reference.

How the SDK registers push tokens

The SDK registers Live Activity push tokens with Customer.io automatically—you don’t manage tokens yourself. Registration is held until there’s both an identified profile and a registered device token, then flushed automatically. If a live activity doesn’t start, registration is the first thing you should check.

Behind that, ActivityKit issues two kinds of token and the SDK handles each: a push-to-start token (one per activity type, so your server can create an activity without your app open—iOS 17.2 or later) and an instance token minted when an activity starts (so Customer.io can update or end that specific activity). Both can rotate; the SDK re-registers new values as they change so updates flow without interruption.