Skip to main content
The Android SDK has many high-level and low-level methods to interact with Plaud devices. We recommend using the high-level methods outlined on this page to handle:
  1. Device Connection: Connecting to & disconnecting from Plaud devices via mobile app
  2. File Management: Syncing files from Plaud device to mobile app
  3. Firmware Updates: Updating Plaud device firmware
These methods should cover majority of Plaud Embedded use cases. For advanced usage, see the advanced Android SDK usage.

Prerequisites

The SDK ships native libraries for arm64-v8a / armeabi-v7a. For testing, you must use a physical device, not an emulator.
Try the Plaud Embedded Skill to have your coding agent help you with your Android implementation.
Visit our GitHub repo for more details on the skill.

Installation

1

Clone the Plaud SDK repo

2

Copy and import the Plaud Android SDK as a dependency

The Android SDK ships as a pre-built .aar. Copy sdk/android/plaud-sdk.aar into your app module’s libs/ directory and add it as a dependency:
build.gradle

Getting Started

Import PlaudDeviceAgent from sdk. PlaudDeviceAgent is a singleton object, so call it directly — there is no instance to construct:
Kotlin
Use these methods and callbacks to drive device interactions between your mobile app and your users’ Plaud devices.

Methods

Plaud Device SDK Initialization

The SDK is initialized with your app Context, a User Token, and your regional domain.
If you haven’t onboarded to the Plaud Developer Platform, see our quickstart onboarding steps.If you’d like more details on how to retrieve your User Token and your regional domain, see the Authentication API reference.
Kotlin
Context
required
Your application Context (e.g. applicationContext).
String
required
User Access Token (JWT), used for device authentication.
String
required
Your regional Plaud server domain without https:// prefix. All SDK network requests use this domain.For more information on how to find your regional server domain, see the Authentication API docs.

Permission Manager

BLE scanning requires runtime permissions on Android. The SDK’s sdk.permission.PermissionManager requests the full set it needs and reports the result.
Kotlin
android.app.Activity
required
Android activity
(Boolean) -> Unit
required
Receives true only when every requested permission was granted.
PermissionManager requests BLUETOOTH_SCAN, BLUETOOTH_CONNECT, ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION on API 31+, and BLUETOOTH, BLUETOOTH_ADMIN, ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION on API ≤ 30. Note that it asks for location on Android 12+ as well.

Connecting (binding) to a Plaud Device

Binding a Plaud device generates a key-pair using the user token and creates an ownership lock on your users’ Plaud device. This makes sure their files on device is always encrypted and can only be decrypted with a valid user token by your application. Binding is a two step process:
  1. Local bind over BLE, where the Embedded SDK runs the encrypted handshake and generates the key-pair on the device
  2. Cloud bind that records the device/owner association so you can track device status remotely and via API.
1

Scan and connect over BLE

.startScan() delivers results on the bleScanResult callback defined in the PlaudDeviceAgentListener.
Two things must be in place before connectBleDevice(...), or the secure handshake fails:
  1. The partner RSA key pair must have arrived. initSDK(...) fetches it over HTTP.
  2. The device serial number must be signed and stored, with NiceBuildSdk.signAndStoreDeviceSn(deviceType, sn). The BLE layer reads that signature during the pre-handshake.
Kotlin
BleDevice
required
A scanned device, as delivered by bleScanResult.
String
Optional second argument on the connectBleDevice(bleDevice, deviceToken) overload — a unique identifier for that device. Omit it to connect with an empty token, which is what most integrations do.The serial number is read from the BleDevice — you do not pass it here.
2

Cloud bind to sync binding states

bleBind(status = 0) means the handshake completed and the key-pair exists on the device. Call the sdk/bind API after a successful call.
Re-binding a device to the same owner is idempotent server-side, so the repeat calls have no side effects. See Binding a Plaud Device to a User for the full endpoint reference.
Kotlin
String
required
Device type string derived from the SN prefix (881 → notepro, 882 → notepins).
String
required
Device serial number.

PlaudDeviceAgentListener Callbacks

The PlaudDeviceAgentListener is the single global listener for PlaudDeviceAgent. See the Interfaces and Callbacks section for the full list.
Plaud devices can only be bound to one mobile application. If a user is uninstalling your mobile app, make sure to unbind your Plaud device.

Depair (unbind) a device

Standard unbind

Plaud devices can only be bound to one application at a time. This is done to properly secure and encrypt files stored on a Plaud device. When a user wants to unbind a Plaud device (whether to use with another Plaud Embedded App or the core Plaud app), unbind over both cloud and BLE. Unbinding via the cloud allows you to track Connected Device statuses remotely and via API.
1

Unbind from the cloud

Removes the device/owner association in the Plaud registry (visible on the Plaud developer portal). This does not require a BLE connection. Unbinding an already-unbound device is an idempotent no-op, so this is safe to call best-effort. See Unbinding a Plaud Device to a User for the full endpoint reference.
Kotlin
String
required
Device type string derived from the SN prefix (881 → notepro, 882 → notepins).
String
required
Device serial number.
2

Depair the device over BLE

Clears the ownership lock on the device itself using the .depair(clear) method. The device confirms the outcome on the bleDepair(status: Int) callback, which releases the BLE link with .disconnect(). Because an out-of-range or unresponsive device never answers, pair the callback with a short timeout that disconnects anyway, so the link is always released.
Kotlin
Boolean
default:"false"
Also clears the SDK’s stored connection/bond state for the device. The Android template app passes false on the standard unbind path — the depair itself is what releases the device. Omit the argument to call the no-arg depair() overload, which is equivalent.Keep false during Device Recovery — the recovery handshake has already force-cleared the device’s key material, and clearing again there fights the flow that is about to rebind the device.

Device Recovery

In certain situations, the cloud and local bind state can go out-of-sync, causing the device to lock. The firmware still holds a previous user’s client ID, so the current user’s handshake is rejected — on Android this usually surfaces as a bleConnectState(2) connect failure after a ~10s timeout. Device recovery re-handshakes with each previously bound client ID using the GET /sdk/binding endpoint. On a match, the stale ownership lock is wiped and the device can be bound to the current user. No reflashing tool is needed.
Recovery only applies when the cloud reports the device as unbound (is_bind is false or null). If is_bind is true, the device is genuinely owned by another account and that owner must unbind it first. The SDK cannot override an active binding.
1

Get all previous clients

Use the sdk/binding API to retrieve all previously bound clients, including bindings in other apps.
Kotlin
String
required
Device type string derived from the SN prefix (881 → notepro, 882 → notepins).
String
required
Device serial number.
Response:
Boolean | null
true = bound to another account (stop — recovery is not possible), false = unbound, null = signed but never bound. Use Device Recovery only when this is not true.
String[]
Previously bound client IDs, newest first.
bind_history records one entry per bind event, so the same client ID repeats for every past connect — 50+ duplicates of a single ID is normal. Dedupe before you bound the list, or all your attempts will retry the same key.
2

Run device recovery on each client

For each previous client ID, recoveryConnectBleDevice uses that ID as the handshake token and connects with force-clear, so the firmware wipes its stale key material. The current user’s SN signature and RSA material are reused — nothing has to be signed for the historical user — and the SDK restores the current user’s handshake identity when the attempt ends.
Kotlin
BleDevice
required
The scanned device, from bleScanResult.
String
required
One bind_history entry to try as the handshake token. Accepts either the raw client_user_id or its 32-hex form.

Offer recovery from both entry points

A locked device is hit on two different paths, and only one of them has a sheet on screen to show the failure:
  • User-initiated connect — the connect sheet is open, so present the recovery prompt from its failure state.
  • Background auto-reconnect (e.g. at launch) — nothing is watching. Emit the offer on a separate flow (the template uses a recoveryOffers SharedFlow) and let your host screen present it.
Stop the reconnect loop before prompting, or the retry job will fight the recovery flow.

File Synchronization

The exportAudio method exports audio files from the Plaud device to your users’ phone, reporting progress through the AudioExporter.ExportCallback.
Plaud devices will record up to 5 hours. Recordings longer should be broken up.
The .getFileList accesses files on your users’ Plaud device.
Kotlin
Long
required
Session ID
File
required
Output directory
AudioExportFormat
required
PCM | WAV | OPUS | MP3. We recommend MP3 — it plays everywhere and is accepted directly by the transcription upload API.
Int
Number of audio channels (1 = mono). Optional — an overload without this argument exists.
AudioExporter.ExportCallback
required
fun onProgress(progress: Int, message: String)fun onComplete(outputFile: File)
For large audio files, we recommend UX considerations:
  • Progress indicators and setting expectations for long transfers (i.e. “This file is large. May take ~X minutes”)
  • Supporting background/resumable transfer so closing the app doesn’t kill the session.
  • Using WiFi Fast Transfer (see below)

WiFi Fast Transfer

An alternative to a BLE (Bluetooth Low Energy) file transfer, WiFi Fast Transfer is ~10x faster than BLE transfers. A transfer spans two objects: PlaudDeviceAgent opens the hotspot, starts and ends the session, and decodes each recording; IWifiTransferAgent (reached with getWifiAgent()) lists and deletes files during the session.
getWifiAgent() returns IWifiTransferAgent? — it is null until the SDK has a WiFi agent to hand out. Always reach it with a safe call (getWifiAgent()?.…).
1

Ask the device to open its hotspot

The device opens a WiFi hotspot on request over BLE. The credentials arrive on the bleWiFiOpen listener callback.
Kotlin
Boolean
required
true opens the device hotspot, false closes it.
2

Start the transfer session

Start the session from the bleWiFiOpen callback. The SDK handles joining the hotspot and the secure handshake for you.
Kotlin
String
required
Identifier for the transfer session.
IWifiTransferAgent.WifiTransferCallback
required
Drives the rest of the flow. See WifiTransferCallback below.
Returns false if the session could not be opened.
3

Wait for READY, then list files

Connection state advances NONE → CONNECTING → CONNECTED → HANDSHAKING → READY. No file command works before READY.
Kotlin
4

Export each recording

exportAudioViaWiFi takes the same arguments as exportAudio and reports on the same AudioExporter.ExportCallback, so your existing export handling works unchanged over WiFi.
Kotlin
Use exportAudioViaWiFi(...) rather than the raw downloadFile() / downloadAllFiles() path on IWifiTransferAgent. Those write undecrypted .opus bytes straight to disk; exportAudioViaWiFi runs the same decode pipeline as BLE.
5

End the session

Kotlin
End the session with endWiFiTransfer(), not getWifiAgent().stopWifiTransfer(). Only endWiFiTransfer() also tells the device to close its hotspot over BLE — otherwise it stays open and drains the device battery.
Use PlaudDeviceAgent.isWifiTransferActive() to check whether a session is currently open.

Deleting files over WiFi

IWifiTransferAgent.deleteFiles(...) removes several recordings in one call — the only batch delete in the SDK. The result arrives on onFileDeleteCompleted.
Kotlin
List<Long>
required
Session IDs of the recordings to delete from the device.

WifiTransferCallback

The WiFi Fast Transfer flow is driven by the IWifiTransferAgent.WifiTransferCallback. All members are required — there are no default implementations.
While faster than BLE transfers, we still recommend the following UX considerations for WiFi Fast Transfer syncs:
  • Progress indicators and setting expectations for long transfers (i.e. “This file is large. May take ~X minutes”)
  • Supporting background/resumable transfer so closing the app doesn’t kill the session.

Firmware Update (OTA)

The Embedded SDK handles the entire OTA flow across three calls: version query → download → MD5 verify → CRC → BLE packet push → device restart → reconnect. Each phase reports through the FirmwareUpdateCallback.
Firmware updates will wipe recordings from your users’ Plaud device. Make sure their recordings are synced/exported to your API services before pushing firmware updates!

Check for Firmware Updates

Kotlin
FirmwareUpdateCallback
required
Callback that reports the check result as a FirmwareUpdateInfo.

Download Firmware Update

Kotlin
FirmwareUpdateInfo
required
The FirmwareUpdateInfo returned by checkFirmwareUpdate.
FirmwareUpdateCallback
required
Reports download progress and completion.

Install Firmware Update

Kotlin
File
required
The firmware file from FirmwareDownloadResult.file
FirmwareUpdateInfo
required
The FirmwareUpdateInfo returned by checkFirmwareUpdate
FirmwareUpdateCallback
required
Reports install progress and completion.

Interfaces and Callbacks

The Embedded SDK is callback-driven. Device events flow through a single global listener you assign to PlaudDeviceAgent.listener, while per-operation flows (audio export, WiFi transfer, firmware) take their own callback interface. These four cover most use cases.
Callbacks are delivered on the SDK’s internal threads — not the main thread. Marshal to the main thread (e.g. runOnUiThread { } / a Handler) before touching UI or view state.

PlaudDeviceAgentListener

The primary listener for PlaudDeviceAgent. Assign it once to PlaudDeviceAgent.listener and it drives the entire BLE lifecycle — scan, connect, bind, device state, recording, and file sync. A single global listener receives every device event; all methods return Unit.
Kotlin
The listener also delivers the WiFi auto-sync configuration results (onWifiSyncEnabled, onWifiSyncListReceived, onWifiSyncConfigReceived, onWifiSyncConfigSet, onWifiSyncDeleteResult, onWifiSyncTestStarted, onWifiSyncTestResult). These configure the device’s own scheduled background upload and are distinct from WiFi Fast Transfer.

AudioExporter.ExportCallback

Reports progress, completion, and errors for exportAudio(...) (BLE) and exportAudioViaWiFi(...) (WiFi).
Kotlin
fun
required
Export progress, 0–100 (download bytes), with a human-readable status message.
fun
required
Called when decoding finishes; outputFile is the written file.
fun
required
Called if export fails, with a description of the error.
fun
Optional (has a default implementation). Language-neutral phase signal: DOWNLOADING (bytes still transferring off the device) → TRANSCODING (only local decode/encode remains).

IWifiTransferAgent.WifiTransferCallback

WiFi Fast Transfer is split across both high-level interfaces, so you will use IWifiTransferAgent directly for any fast transfer. PlaudDeviceAgent owns the session lifecycle and the audio pipeline; IWifiTransferAgent owns the file operations and the callback you implement to drive them.
Kotlin

WiFi Fast Transfer

The typical flow: open the hotspot over BLE, start the session, wait for READY, list files, then export each one.

WifiTransferCallback

12 required members for Wifi Transfers, generally driven by PlaudDeviceAgent.exportAudioViaWiFi. WifiFileInfo carries sessionId, fileName, fileSize, duration (ms), timestamp (epoch seconds), and scene — more per-file metadata than BleFile exposes over BLE.

FirmwareUpdateCallback

Reports each phase of the OTA flow across checkFirmwareUpdate(...), downloadFirmware(...), and installFirmware(...). Extend SimpleFirmwareUpdateCallback() to override only the methods you need — each has an empty default implementation.
Kotlin