Tag Operations & Device Types
|
This page is part of Protocol & internals. Partner integrators should follow Managed BLE Operations instead. |
The SDK is built around a simple idea: an electronic baggage tag (EBT) offers a set of high-level operations — write a ticket to the display, blank the display, read the battery, read the firmware. Those operations are described in vendor-neutral, transport-agnostic terms. How a tag is reached (Bluetooth Low Energy today; potentially NFC, or others in the future) and which vendor built it are details the SDK layers on top — they do not change the core operations.
For partner apps the practical model is: you hold a type of device, and the SDK offers you the
set of operations that the device supports. Everything you call goes through the single
BagIdSdk façade; the layering below explains what that façade exposes and
why, not extra types you have to wire up yourself.
The layered model
- Core tag operations (vendor-neutral)
-
The base capability contract for any electronic baggage tag, independent of vendor or transport: write a ticket to the display, blank the display, read the battery level, read the firmware version. This is the layer that makes the SDK vendor-neutral — the same operations are meaningful whether a tag is reached over BLE, NFC, or any future transport. In the codebase this contract is the
IEbtTaginterface. - BLE specialization
-
Tags reached over Bluetooth Low Energy add a device lifecycle on top of the core operations: scanning for nearby tags, connecting to one, and disconnecting. Transport that is not BLE would expose the core operations without this lifecycle layer. In the codebase this is the
IEbtBleTagspecialization ofIEbtTag. - BagID over BLE (the default implementation)
-
The concrete, shipping implementation is BagID electronic baggage tags over BLE — currently the only implementation of the BLE specialization. On top of scan/connect/disconnect and the core operations, it adds BagID-specific extras: writing a personal nametag image, toggling the display layer (hide / restore), and a connection/error state stream. It also handles BagID GATT generation differences (v2.0 vs. v2.1, including the v2.1 security handshake) internally. In the codebase this is the
IBagIdBleTaginterface and its implementation.
Choosing a device type
A partner app does not pick a transport or a GATT stack in code. Instead, it works with the device identity the SDK reports from BLE discovery, and the SDK routes to the right internals:
-
EbtTagIdentity— the vendor and GATT generation the SDK inferred for a discovered tag (BagIdfor GATT v2.0,BagIdBleV21for GATT v2.1,OtherVendorfor non-BagID advertisements). Use it to label a device in your scan list; the SDK uses it to route internally. -
EbtType— selects vendor-specific BLE extras that sit outside the core operations (e.g.EbtType.BagIdfor nametag). This is how you reach the BagID-implementation layer deliberately when you need an extra it provides.
Today every supported device is a BagID tag over BLE, so in practice the device type is fixed
and all supported BagID hardware lines (BAGID_GO, BAGID_2FM, BAGID_2ST) use the same
operations — generation differences are internal. The layering exists so additional vendors or
transports can be added later without changing the operations your app already calls.
Where each operation lives on the façade
All of these are methods on BagIdSdk. They are grouped here by the layer
they belong to, so the model above maps directly to the API you call.
| Layer | Façade methods | Notes |
|---|---|---|
Core tag operations (vendor-neutral) |
|
The transport-agnostic operations every EBT exposes. Require an active connection. |
BLE specialization (device lifecycle) |
|
Discovery and connection management for tags reached over BLE. |
BagID implementation (vendor extras) |
|
BagID-specific features beyond the core operations. |
Orchestrated flows — full-screen managed |
Android |
Same JSON payload and result contract; SDK owns scan, custody UI, connect, and backend steps. See Managed BLE Operations. |
Orchestrated flows — headless |
|
Same operation strings and JSON contract as full-screen managed, but no SDK UI — your app supplies |
Orchestrated flows — programmatic |
|
Typed |
Headless managed operations (runBleOperation)
Headless mode runs the same managed operation pipeline as the full-screen flow
(ManagedBleFlowRunner) but without ManagedBleFlowActivity or BagIdSDKUI. Use it when:
-
Your app already has BLE UX (or a device registry) and knows which tag to target.
-
You want the managed JSON in/out contract without handing the screen to the SDK.
-
You integrate from a non-coroutine host (e.g. React Native via
BagIdRnFacade.runBleOperationBlocking).
| Surface | Who picks the tag | Entry point |
|---|---|---|
Full-screen managed |
SDK scan list (or auto-select when |
|
Headless ( |
Host app — |
|
Programmatic |
Host app — connect first, then call typed APIs |
|
The SDK scans BLE, connects to peripherals whose GUID matches uniqueDeviceId, runs the operation
(lock lookup, custody when required, authorize, BLE I/O, backend attach/unlock as applicable), returns
result JSON, and disconnects. Operation strings, payload envelope, and result shapes match
Managed BLE Operations.
Steps
-
Call
BagIdSdk.configure(…)andBagIdSdk.initialize()first. -
Build payload JSON with
data.uniqueDeviceIdset to the tag’s backend GUID (stable device identity — not the BLEdeviceId/ iOS peripheral UUID). -
Call
BagIdSdk.runBleOperation(operation, payloadJson). -
Parse the returned JSON string (same
status/code/messagecontract as the full-screen flow).
suspend fun BagIdSdk.runBleOperation(
operation: String,
payloadJson: String,
custodyProofCollector: CustodyProofCollector? = null,
): String
// After configure + initialize:
val resultJson = BagIdSdk.runBleOperation(
operation = "READ_BATTERY",
payloadJson = """
{
"schemaVersion": 1,
"data": { "uniqueDeviceId": "$tagGuid" }
}
""".trimIndent(),
)
On Android, BagIdRnFacade.runBleOperationBlocking wraps the same API for JVM hosts that cannot use
coroutines (blocks the calling thread — invoke from a background thread).
|
Headless mode does not show custody UI. For |
Custody helpers for headless flows
Wire these when your app renders custody proof UI while runBleOperation is suspended:
fun BagIdSdk.managedBleCustodyProofCollector(): CustodyProofCollector
val BagIdSdk.managedBleCustodyPrompt: StateFlow<ManagedBleCustodyHost.Prompt?>
fun BagIdSdk.submitManagedBleCustodyProof(recordLocator: String, surname: String)
fun BagIdSdk.cancelManagedBleCustodyProof()
fun BagIdSdk.resetManagedBleCustodyHost()
Headless-specific result codes
These codes from the managed result JSON contract apply most often to headless entry:
| Code | Typical cause |
|---|---|
|
|
|
Targeted scan timed out or no nearby tag matched the GUID. |
See Managed BLE Operations — error codes for the full list.
|
Partner apps should use the full-screen managed path. Reach for headless or programmatic APIs only when you own the surrounding BLE UX or need RN/JVM blocking entry points. |
What’s next
-
Managed BLE Operations — full-screen integration contract (payload/result JSON)
-
Transfer & Clear operation details — programmatic transfer/clear sequences
-
API Reference — categorized façade method map
-
Models — EbtTagIdentity — device identity and routing
-
FAQ — GATT v2.0 vs. v2.1 — generation differences