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
| Principle | Description |
|---|---|
| Voice-first | All interactions optimized for spoken input/output. |
| Offline-resilient | Pending queue and HTTP fallback ensure messages survive disconnections. |
| Explicit capture | Audio 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 agnostic | Core message semantics are independent of BLE, WebSocket, or HTTP. |
2. Architecture
The protocol spans four layers connected by three distinct transports:
Device (Nexting PIN) ─── BLE ─── Client (iPhone App) ─── WebSocket/HTTP ─── Gateway (Plugin Server) ─── RPC ─── AI Servicewss://api.nexting.ai/ws). The local protocol documented here is spoken between the cloud relay client and the plugin server on port 18790.| Layer | Role |
|---|---|
| Device | Captures audio via PDM microphone, encodes and transmits BLE packets, sends heartbeats. |
| Client | Assembles BLE audio, runs STT (cloud Whisper proxy, on-device Apple Speech fallback)/TTS, manages WebSocket connection and HTTP fallback. |
| Gateway | Accepts client connections, routes messages to AI via RPC. |
| AI Service | Processes natural language, returns agent responses (text). |
3. Data Types
| Type | Format | Example |
|---|---|---|
DeviceId | string | "iphone-Eric's iPhone", "pin-001" |
Token | string | Bearer token for auth |
Timestamp | number | Unix milliseconds (Date.now()) |
MessageId | string (UUID v4) | crypto.randomUUID() |
AudioCodec | uint8 | 0x01=AAC (legacy), 0x03=IMA ADPCM, 0x14 (20)=Opus |
PacketType | uint8 | 0x01=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
| Characteristic | UUID | Properties |
|---|---|---|
| Text / Format | 12345678-1234-1234-1234-123456789ABD | Read, Write, Notify — phone writes commands (e.g. 0x01 start, 0x40 shutdown) |
| Audio | 12345678-1234-1234-1234-123456789ABE | Read, Notify |
| Heartbeat / Speaker | 12345678-1234-1234-1234-123456789ABF | Write, Notify |
| Speaker | 12345678-1234-1234-1234-123456789AC0 | Write |
| Hardware ID | 12345678-1234-1234-1234-123456789AC1 | Read |
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.
┌──────────────────────────────────────────────────────────────┐
│ 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
| Parameter | Value |
|---|---|
| Connection URI | ws://{host}:{port} or wss://{host}:{port} |
| Default port | 18790 |
| Auth timeout | 10 seconds (server closes with code 4001 if not authenticated) |
| Keepalive | Server pings every 30 seconds |
| Reconnection | Exponential backoff: delay = min(2^attempt, 15) seconds |
Connection States
disconnected → connecting → authenticating → connected ↔ reconnectingThe 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.
{ "type": "auth", "deviceId": "string", "token": "string" }text
Sends user input (typically STT output) to the gateway.
{ "type": "text", "content": "string" }ping
Client-initiated keepalive.
{ "type": "ping" }Outbound (Gateway → Client)
auth_ok
Confirms successful authentication.
{ "type": "auth_ok", "deviceId": "string" }agent_message
AI response delivered to the client.
{
"type": "agent_message",
"content": "string",
"proactive": true | false,
"requestId": "string?"
}| Field | Description |
|---|---|
content | Full AI response text. |
proactive | true if AI-initiated (not user-requested), e.g. cron reminders. |
requestId | Optional request correlation id. |
error
Reports a gateway or AI error.
{ "type": "error", "message": "string" }pong
Response to client ping.
{ "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.
Request: { "token": "string", "deviceId": "string", "content": "string" }
Response: { "content": "string" }POST /push
Proactive push from agent.
Request: { "token": "string", "deviceId": "string", "text": "string" }
Response: { "ok": true, "deviceId": "string" }GET /health
Health check endpoint.
Response: { "ok": true, "devices": 1, "gateway": true }8. Session Lifecycle
BLE State Machine
idle → scanning → connecting → discovering → ready → disconnected (auto-reconnect)WebSocket State Machine
disconnected → connecting → authenticating → connected ↔ reconnectingVoice Pipeline State Machine
idle → receivingAudio → transcribing → thinking → done / error9. Audio Pipeline
End-to-end voice flow from button press to spoken response:
| Step | Description |
|---|---|
| 1 | User presses PTT (push-to-talk) button on PIN. |
| 2 | Device captures audio via the onboard PDM microphone. |
| 3 | Audio encoded (Opus) and split into BLE packets (START → DATA... → END). |
| 4 | iPhone receives packets, assembles, verifies frame count (CRC32 for legacy codecs). |
| 5 | Cloud STT via the Nexting Whisper proxy (on-device Apple Speech as fallback). |
| 6 | Transcribed text sent via WebSocket (or HTTP fallback if WS disconnected). |
| 7 | Gateway routes to AI (Gateway RPC primary, direct AI fallback). |
| 8 | AI response returned to client. |
| 9 | TTS playback on iPhone speaker. |
10. Gateway RPC
The gateway communicates with the AI service via JSON-RPC over WebSocket, authenticated with Ed25519 signatures.
Device Identity
| Parameter | Description |
|---|---|
| Keypair location | ~/.openclaw/pinclaw-device-identity.json |
| DeviceId derivation | SHA-256 of raw Ed25519 public key (hex encoded) |
| Signing payload | v2|{deviceId}|{clientId}|{clientMode}|{role}|{scopes}|{signedAt}|{token}|{nonce} |
chat.send RPC
{
"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.
{ "type": "event", "event": "chat", "payload": { "state": "delta", "message": { ... } } }{ "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.
| Parameter | Value |
|---|---|
| Storage | Nexting Cloud Postgres offline_queue table |
| TTL | 24 hours |
| Max per device | No per-device cap |
| Delivery | FIFO drain, up to 100 messages per pass, when the plugin reconnects. |
12. Security
| Concern | Mechanism |
|---|---|
| Authentication | Bearer token per device. |
| Transport | WS/WSS (TLS RECOMMENDED in production). |
| Audio privacy | STT via cloud speech-to-text; Voice History keeps replayable takes for up to 30 days. |
| Gateway RPC | Ed25519 signatures with challenge-response nonce. |
wss:// (TLS). The gateway MUST validate bearer tokens on every connection and HTTP request.13. Conformance
M = Mandatory, O = Optional, - = Not applicable.
| Feature | Device | Client | Gateway |
|---|---|---|---|
| BLE Peripheral | M | - | - |
| BLE Central | - | M | - |
| Audio Capture | M | - | - |
| Audio Codec (Opus) | M | M | - |
| Packet Assembly | - | M | - |
| CRC32 Verify | - | M | - |
| WebSocket Client | - | M | - |
| WebSocket Server | - | - | M |
| HTTP Fallback | - | M | M |
| STT (cloud, on-device fallback) | - | M | - |
| TTS Playback | - | M | - |
| Gateway RPC | - | - | M |
| Pending Queue | - | O | M |
| Heartbeat Send | M | - | - |
| Heartbeat Recv | - | M | - |