Protocol

Core Protocol

Nexting Developer Documentation | August 2026

1. Introduction

The OpenClaw Core Protocol defines the communication between four participants: Device (Nexting PIN), Client (iPhone app), Gateway (plugin server), and AI Service. It covers BLE audio transport, WebSocket messaging, HTTP fallback, session lifecycle, and gateway RPC.

Version 0.2.0, July 2026.

Design Philosophy

PrincipleDescription
Voice-firstAll interactions optimized for spoken input/output.
Offline-resilientPending queue and HTTP fallback ensure messages survive disconnections.
Explicit captureAudio capture begins only when the user initiates it. STT runs via the Nexting Cloud Whisper proxy; on-device Apple Speech recognition is the fallback when the cloud is unreachable.
Transport agnosticCore message semantics are independent of BLE, WebSocket, or HTTP.
The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in RFC 2119.

2. Architecture

The protocol spans four layers connected by three distinct transports:

Architecture Overview
Device (Nexting PIN)  ─── BLE ───  Client (iPhone App)  ─── WebSocket/HTTP ───  Gateway (Plugin Server)  ─── RPC ───  AI Service
In production, the iPhone app connects through the Nexting Cloud relay (wss://api.nexting.ai/ws). The local protocol documented here is spoken between the cloud relay client and the plugin server on port 18790.
LayerRole
DeviceCaptures audio via PDM microphone, encodes and transmits BLE packets, sends heartbeats.
ClientAssembles BLE audio, runs STT (cloud Whisper proxy, on-device Apple Speech fallback)/TTS, manages WebSocket connection and HTTP fallback.
GatewayAccepts client connections, routes messages to AI via RPC.
AI ServiceProcesses natural language, returns agent responses (text).

3. Data Types

TypeFormatExample
DeviceIdstring"iphone-Eric's iPhone", "pin-001"
TokenstringBearer token for auth
TimestampnumberUnix milliseconds (Date.now())
MessageIdstring (UUID v4)crypto.randomUUID()
AudioCodecuint80x01=AAC (legacy), 0x03=IMA ADPCM, 0x14 (20)=Opus
PacketTypeuint80x01=START, 0x02=DATA, 0x03=END, 0x04=HEARTBEAT

4. Transport: BLE

BLE is the link between the Nexting PIN and the iPhone app. The device exposes a main GATT service with five characteristics, plus separate button, storage, and DFU services.

BLE Service

Nexting service UUID: 12345678-1234-1234-1234-123456789ABC

CharacteristicUUIDProperties
Text / Format12345678-1234-1234-1234-123456789ABDRead, Write, Notify — phone writes commands (e.g. 0x01 start, 0x40 shutdown)
Audio12345678-1234-1234-1234-123456789ABERead, Notify
Heartbeat / Speaker12345678-1234-1234-1234-123456789ABFWrite, Notify
Speaker12345678-1234-1234-1234-123456789AC0Write
Hardware ID12345678-1234-1234-1234-123456789AC1Read

The device additionally exposes separate button, storage, and DFU (firmware update) services.

MTU: negotiated (firmware supports up to a 498-byte L2CAP payload; minimum 100 bytes required).

Packet Format

START

[0x01][codec:1B][totalSize/reserved:4B BE] — Announces a new audio recording. Codec 0x14 (Opus) carries 4 reserved zero bytes; codec 0x03 (ADPCM) carries totalSize (total expected bytes of the encoded audio); legacy codec 0x01 (AAC) omits the codec byte.

DATA

[0x02][seqNo:2B BE][payload ≤320B] — Sequential audio chunks. seqNo is used for ordering and gap detection.

END

[0x03][totalFrames:4B BE] — End of recording. Frame count for Opus; legacy AAC/ADPCM send a CRC32 checksum instead.

HEARTBEAT

[0x04][counter:2B BE][flags:1B][battMv:2B BE] — Periodic keepalive. counter increments each heartbeat; flags bit0 = recording active, bit1 = charging; battMv is the battery voltage in millivolts.

Binary Packet Layout
┌──────────────────────────────────────────────────────────────┐
│ START   │ 0x01 │ codec (1B) │ totalSize/reserved (4B BE)    │
├──────────────────────────────────────────────────────────────┤
│ DATA    │ 0x02 │ seqNo (2B BE) │ payload (up to 320 bytes)  │
├──────────────────────────────────────────────────────────────┤
│ END     │ 0x03 │ totalFrames (4B BE)                        │
├──────────────────────────────────────────────────────────────┤
│ HEARTBT │ 0x04 │ counter (2B BE) │ flags (1B) │ battMv (2B)│
└──────────────────────────────────────────────────────────────┘

(Legacy AAC/ADPCM only) CRC32 algorithm: Standard CRC32 (polynomial 0xEDB88320), computed over the fully assembled audio data.

5. Transport: WebSocket

ParameterValue
Connection URIws://{host}:{port} or wss://{host}:{port}
Default port18790
Auth timeout10 seconds (server closes with code 4001 if not authenticated)
KeepaliveServer pings every 30 seconds
ReconnectionExponential backoff: delay = min(2^attempt, 15) seconds

Connection States

WebSocket State Machine
disconnected → connecting → authenticating → connected ↔ reconnecting

The client MUST send an auth message within 10 seconds of connection. The gateway MUST respond with auth_ok or close with code 4001.

6. Message Types

All messages are JSON-encoded and transmitted over WebSocket.

Inbound (Client → Gateway)

auth

Authenticates the client. MUST be the first message sent.

auth
{ "type": "auth", "deviceId": "string", "token": "string" }

text

Sends user input (typically STT output) to the gateway.

text
{ "type": "text", "content": "string" }

ping

Client-initiated keepalive.

ping
{ "type": "ping" }

Outbound (Gateway → Client)

auth_ok

Confirms successful authentication.

auth_ok
{ "type": "auth_ok", "deviceId": "string" }

agent_message

AI response delivered to the client.

agent_message
{
  "type": "agent_message",
  "content": "string",
  "proactive": true | false,
  "requestId": "string?"
}
FieldDescription
contentFull AI response text.
proactivetrue if AI-initiated (not user-requested), e.g. cron reminders.
requestIdOptional request correlation id.

error

Reports a gateway or AI error.

error
{ "type": "error", "message": "string" }

pong

Response to client ping.

pong
{ "type": "pong" }

7. HTTP Fallback

Used when WebSocket is disconnected (e.g., app woken from background by BLE). The client SHOULD prefer WebSocket but MUST support HTTP fallback.

POST /message

Send user text, get AI response synchronously.

POST /message
Request:  { "token": "string", "deviceId": "string", "content": "string" }
Response: { "content": "string" }

POST /push

Proactive push from agent.

POST /push
Request:  { "token": "string", "deviceId": "string", "text": "string" }
Response: { "ok": true, "deviceId": "string" }

GET /health

Health check endpoint.

GET /health
Response: { "ok": true, "devices": 1, "gateway": true }

8. Session Lifecycle

BLE State Machine

BLE States
idle → scanning → connecting → discovering → ready → disconnected (auto-reconnect)

WebSocket State Machine

WebSocket States
disconnected → connecting → authenticating → connected ↔ reconnecting

Voice Pipeline State Machine

Voice Pipeline States
idle → receivingAudio → transcribing → thinking → done / error

9. Audio Pipeline

End-to-end voice flow from button press to spoken response:

StepDescription
1User presses PTT (push-to-talk) button on PIN.
2Device captures audio via the onboard PDM microphone.
3Audio encoded (Opus) and split into BLE packets (START → DATA... → END).
4iPhone receives packets, assembles, verifies frame count (CRC32 for legacy codecs).
5Cloud STT via the Nexting Whisper proxy (on-device Apple Speech as fallback).
6Transcribed text sent via WebSocket (or HTTP fallback if WS disconnected).
7Gateway routes to AI (Gateway RPC primary, direct AI fallback).
8AI response returned to client.
9TTS playback on iPhone speaker.
Privacy: raw audio is sent to Nexting cloud speech-to-text (streaming Volcengine and Whisper file fallback). Takes that produce text are kept in Voice History for up to 30 days. When the cloud is unreachable, on-device Apple Speech recognition is used as a fallback. See the Privacy Policy.

10. Gateway RPC

The gateway communicates with the AI service via JSON-RPC over WebSocket, authenticated with Ed25519 signatures.

Device Identity

ParameterDescription
Keypair location~/.openclaw/pinclaw-device-identity.json
DeviceId derivationSHA-256 of raw Ed25519 public key (hex encoded)
Signing payloadv2|{deviceId}|{clientId}|{clientMode}|{role}|{scopes}|{signedAt}|{token}|{nonce}

chat.send RPC

chat.send request
{
  "type": "req",
  "id": "uuid",
  "method": "chat.send",
  "params": {
    "sessionKey": "main",
    "message": "string",
    "idempotencyKey": "uuid"
  }
}

Response Events (Streaming)

The gateway receives streaming events. A delta event carries partial content; final signals completion.

chat event — delta
{ "type": "event", "event": "chat", "payload": { "state": "delta", "message": { ... } } }
chat event — final
{ "type": "event", "event": "chat", "payload": { "state": "final", "message": { ... } } }

11. Pending Queue

The plugin itself has no pending queue. The offline queue lives in Nexting Cloud as a Postgres offline_queue table: when the plugin is unreachable, the cloud relay queues outbound messages and drains them when the plugin reconnects.

ParameterValue
StorageNexting Cloud Postgres offline_queue table
TTL24 hours
Max per deviceNo per-device cap
DeliveryFIFO drain, up to 100 messages per pass, when the plugin reconnects.

12. Security

ConcernMechanism
AuthenticationBearer token per device.
TransportWS/WSS (TLS RECOMMENDED in production).
Audio privacySTT via cloud speech-to-text; Voice History keeps replayable takes for up to 30 days.
Gateway RPCEd25519 signatures with challenge-response nonce.
In production, all WebSocket connections SHOULD use wss:// (TLS). The gateway MUST validate bearer tokens on every connection and HTTP request.

13. Conformance

M = Mandatory, O = Optional, - = Not applicable.

FeatureDeviceClientGateway
BLE PeripheralM--
BLE Central-M-
Audio CaptureM--
Audio Codec (Opus)MM-
Packet Assembly-M-
CRC32 Verify-M-
WebSocket Client-M-
WebSocket Server--M
HTTP Fallback-MM
STT (cloud, on-device fallback)-M-
TTS Playback-M-
Gateway RPC--M
Pending Queue-OM
Heartbeat SendM--
Heartbeat Recv-M-