Managed BLE Operations
This page is the integration contract for partner apps that use the SDK’s managed BLE path. The same operation strings, payload JSON envelope, and result JSON apply on Android and iOS.
Use this path when you want the SDK to own BLE permission prompts, discovery UI, custody proof UI, connect/reconnect, and backend orchestration.
|
Discovery UI is how the passenger finds and selects their BagID tag. The phone scans for nearby
tags over Bluetooth and shows a list so the user can tap the correct device (or the SDK connects
automatically when you already know the tag’s On the full-screen managed path, the SDK provides this UI—you do not need to call
|
|
Custody proof UI is a short identity check before writing baggage to a tag. The BagID backend may require proof that the passenger owns the tag (typically booking reference / PNR and surname) so baggage data is not sent to the wrong device. On the full-screen managed path, the SDK shows this form, submits proof to the API, and handles
retries—you do not need |
For headless orchestration without SDK UI (runBleOperation), see
Tag Operations — headless mode.
Platform entry points
| Platform | Entry |
|---|---|
Android |
|
iOS |
|
Operations
All operation names are uppercase strings. The managed flow normalizes case internally.
| Operation | Purpose | Payload data required? |
|---|---|---|
|
Write a baggage tag to the EBT and attach the device on the backend (lock lookup, custody when required, authorize, BLE write, attachment). |
Yes — |
|
Clear the tag display and remove custody lock on the backend. |
Optional — |
|
Read battery level (0–100) over BLE. |
Optional — |
|
Read firmware version string over BLE. |
Optional — |
|
Write a personal image (nametag) to the tag display. |
Yes — |
|
Hide the bag-tag layer so the personal image is visible on the tag. |
Optional — |
|
Restore the bag-tag layer after a prior hide. |
Optional — |
|
|
Launch parameters (all platforms)
Every full-screen launch supplies the same logical inputs:
| Parameter | Type | Description |
|---|---|---|
API base URL |
|
BagID API endpoint for this environment (e.g. |
Source app key |
|
|
Federated token |
|
Federated JWT from your auth layer. Passed on launch so the managed surface can authenticate if
|
Operation |
|
One of the operations in the table above. |
Payload JSON |
|
Envelope described in Payload envelope. Use |
Output: a single JSON string. Parse status (SUCCESS, FAILURE, or CANCELLED). See Result JSON.
Payload envelope
The payload is a JSON object with three top-level keys:
{
"schemaVersion": 1,
"selection": {
"deviceId": "ble-peripheral-id",
"autoSelectTimeoutMs": 12000
},
"data": {
"uniqueDeviceId": "tag-guid-from-your-device-table",
"baggageId": 12345,
"journeyId": 67890
}
}
| Field | Type | Description |
|---|---|---|
|
|
Currently |
|
object, optional |
BLE peripheral hint for faster connect when the user already picked a row from a recent scan. |
|
object |
Operation-specific fields. |
Unknown keys in the envelope are ignored.
TRANSFER data fields
Required:
-
baggageId(int) — from create baggage (Step 4) or your PSS. -
journeyId(int) — fromloadJourney. -
Either
journey(full journey object) ordisplayTicket(EBT ticket payload).
Optional:
-
recordLocator(string) — pre-fills custody proof. -
custodyProofSurname(string) — pre-fills custody proof. -
uniqueDeviceId(string) — auto-select registered tag by GUID.
journey vs displayTicket — choosing the right field
| Field | When to use | Shape |
|---|---|---|
|
You have passenger/flight data from DCS or your own PSS — typically obtained via |
Full |
|
You already hold a pre-built |
|
In almost all partner integrations the journey path is the right choice. Supply displayTicket only when BagID explicitly advises it during onboarding.
The journey object mirrors Journey JSON: journeyId, optional recordLocator, passengers[], flights[], baggage[], optional baggageSlots[].
{
"schemaVersion": 1,
"data": {
"baggageId": 1001,
"journeyId": 42,
"recordLocator": "ABC123",
"journey": {
"journeyId": 42,
"recordLocator": "ABC123",
"passengers": [
{ "passengerId": 1, "surname": "DOE", "givenName": "JOHN", "title": "MR" }
],
"flights": [
{
"flightNumber": "WF123",
"departureAirport": "BGO",
"destinationAirport": "OSL",
"departureAt": "2026-06-11T10:00:00Z"
}
],
"baggage": [
{
"baggageId": 1001,
"baggageTagNumber": "0701913714",
"airline": "WF",
"destinationAirport": "OSL"
}
]
}
}
}
SEND_NAMETAG data fields
Required:
-
imageBase64— Base64-encoded image bytes. After decode, the image must be exactly 48000 bytes (NAMETAG_IMAGE_BYTES).
Optional:
-
nametagHeader— PGM header string. Default:"P5 240 400 16 "(note the trailing space; the firmware checks for it). -
uniqueDeviceId— auto-select by GUID.
{
"schemaVersion": 1,
"data": {
"uniqueDeviceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"imageBase64": "<48000-byte-image-as-base64>"
}
}
CLEAR, READ_*, HIDE_TICKET, RESTORE_TICKET data
For manual device pick inside the SDK UI, use an empty data object or omit fields:
{ "schemaVersion": 1, "data": {} }
To skip the list and target a registered tag:
{
"schemaVersion": 1,
"data": {
"uniqueDeviceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"autoSelectTimeoutMs": 15000
}
}
autoSelectTimeoutMs in data applies when selection is omitted (same default and minimum as selection.autoSelectTimeoutMs).
Auto-select by GUID
When data.uniqueDeviceId is set:
-
The managed flow scans for nearby BagID tags (up to the timeout).
-
For each candidate, it connects briefly and compares the tag’s BLE-reported UUID to your GUID.
-
On match, it keeps the connection and runs the operation (no redundant reconnect).
Use this for registered devices in your app’s device table. Store the GUID from the first successful transfer or from your backend device registry.
selection.deviceId is optional acceleration when you already know the BLE peripheral id from a recent scan.
Result JSON
Every managed flow finishes with one JSON string.
SUCCESS
Top-level fields: status = "SUCCESS", operation, data.
| Operation | data shape |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
pendingRetry on transfer means the BLE write succeeded but HTTP attachment did not—your app should offer retry or backend reconciliation (see FAQ).
Example transfer success:
{
"status": "SUCCESS",
"operation": "TRANSFER",
"data": {
"bleWriteSuccess": true,
"attachmentSuccess": true,
"pendingRetry": false,
"device": {
"deviceId": "AA:BB:CC:DD:EE:FF",
"batteryLevel": 87,
"firmwareVersion": "2.1.0"
}
}
}
Error codes
| Code | Typical cause |
|---|---|
|
Blank operation string. |
|
Headless |
|
Auto-select scan timed out or GUID did not match any nearby tag. |
|
BLE connect failed or connected state not ready. |
|
Missing or malformed |
|
|
|
|
|
BLE read failed. |
|
Nametag write or backend authorize failed. |
|
Display-layer toggle failed over BLE. |
|
Unsupported operation string. |
Android: launch full-screen flow
The SDK merges com.bagid.sdk.ui.ManagedBleFlowActivity into your app manifest (see Installation).
Kotlin (recommended)
import android.app.Activity
import com.bagid.sdk.host.BagIdAndroidHost
fun launchTransfer(activity: Activity, federatedToken: String, payloadJson: String) {
BagIdAndroidHost.startManagedBleFlowForResult(
activity = activity,
apiBaseUrl = "https://api.bagid.com",
sourceAppKey = "your-issued-client-key",
federatedToken = federatedToken,
operation = "TRANSFER",
payloadJson = payloadJson,
)
}
// In onActivityResult or Activity Result API callback:
val resultJson = BagIdAndroidHost.managedFlowResultJson(data)
BagIdRnFacade exposes the same methods for JVM hosts that prefer that entry name.
Intent extras (advanced)
| Extra constant | Value |
|---|---|
|
API base URL |
|
Source app key |
|
Federated token |
|
Operation string |
|
Payload envelope JSON |
|
Result JSON on the returned |
iOS: launch full-screen flow
Link both SPM products from the published package:
-
BagIdSDK— KMP core (BagIdSdk, models, BLE façade) -
BagIdSDKUI— SwiftUI managed flow (BagIdIosHost)
import BagIdSDK
import BagIdSDKUI
// From a UIViewController (e.g. when user taps "Transfer to tag"):
BagIdIosHost.presentManagedBleFlow(
from: self,
apiBaseUrl: "https://api.bagid.com",
sourceAppKey: "your-issued-client-key",
federatedToken: federatedToken,
operation: "TRANSFER",
payloadJson: transferPayloadJson
) { resultJson in
// Called on the main thread — parse JSON and update your UI
print(resultJson)
}
The completion handler receives the same result contract as Android’s EXTRA_RESULT_JSON.
End-to-end flows for partner apps
Transfer workflow
-
Configure once at app startup with your issued
sourceAppKeyand atokenProviderthat can return a federated token from your IdP when the SDK asks (silently, without user interaction). -
Initialize before journey calls. The SDK reuses stored access and refresh tokens while they are still valid; it calls
tokenProvideronly when refresh is missing or has expired. -
Load journey and create baggage via
BagIdSdk(or supply your own journey data in the TRANSFER payload). -
Build payload JSON with
baggageId,journeyId, and journey/ticket data; adduniqueDeviceIdif the tag is already registered. -
Launch the managed flow with the launch parameters in Launch parameters (all platforms). The SDK runs
initialize()inside the surface and reuses the session when possible. -
Parse result JSON and drive your UI; handle
pendingRetryon transfer.
Other operations
CLEAR, READ_BATTERY, READ_FIRMWARE, SEND_NAMETAG, HIDE_TICKET, and RESTORE_TICKET do not
require journey load or baggage creation.
-
Configure and initialize as for transfer (session reuse and on-demand
tokenProviderapply the same way). -
Build payload JSON with the fields required for the operation (see the operations table above). Include
uniqueDeviceIdwhen the tag is already registered. -
Launch the managed flow with the launch parameters in Launch parameters (all platforms).
-
Parse result JSON and drive your UI.
Related documentation
-
Partner Integration — onboarding, credentials, device identity
-
Quickstart — configure through first transfer
-
Models and State —
Journey,TransferTagRequest, device identity -
Transfer & Clear operation details — backend/BLE sequence (support reference)
-
Tag Operations — headless mode —
runBleOperationwithout SDK UI