Every RPC request, response, audio frame, and event uses the same envelope.

Frame layout

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------------------------------------------------------+
|                       length (u32 LE)                         |
+---------------------------------------------------------------+
|                      method_id (u32 LE)                       |
+---------------------------------------------------------------+
|                                                               |
.                  serde envelope body (length-4 bytes)         .
|                                                               |
+---------------------------------------------------------------+
FieldWidthMeaning
length4 bytesLittle-endian byte count of method_id + body. Excludes itself. Wire total = 4 + length.
method_id4 bytesLittle-endian. One of the Method IDs.
bodylength - 4A serde envelope (see below).
length does not include the four bytes of the length field itself. A zero-arg request (Empty) has length = 4 + sizeof(serde-envelope-header), not zero.

Serde envelope

The body of every frame is a serde envelope:
+----+----+--------------------+-----------------------+
| u8 | u8 | i32 LE             | fields...             |
+----+----+--------------------+-----------------------+
  v    cv   payload_size            (payload_size B)
FieldWidthMeaning
version1 byteSchema version of the producer.
compat_version1 byteOldest version the producer knows it is compatible with.
payload_size4 bytes (signed LE)Byte count of the fields that follow. Used to skip past unknown trailing fields.
fieldsvariableEach field encoded inline in declaration order.
A receiver that doesn’t recognize a tail of newer fields simply skips (payload_size - bytes_consumed) bytes and moves on. This is how forward compatibility works.

Field encoding

Primitives are little-endian, two’s-complement, no padding:
Logical typeWire encoding
boolu8 (0 or 1)
int32 / uint324 bytes LE
int64 / uint648 bytes LE
double8 bytes IEEE 754 LE
enumencoded as int32 LE
stringi32 LE length followed by length bytes of UTF-8
vector<T>i32 LE length followed by length elements
nested structanother full serde envelope (recursive)
Strings are length-prefixed, not null-terminated. Binary payloads (e.g. µ-law audio) are also encoded with the string shape — the bytes are treated as opaque.

Worked example: BargeRequest

Schema:
struct BargeRequest {
    string call_sid;
};
Serialize call_sid = "abc" with method_id = 3854301714 (Barge):
length        = 14   (4 method_id + 10 envelope body)
method_id     = 3854301714
envelope:
  version     = 0
  compat_ver  = 0
  payload_sz  = 7    (4 length-prefix + 3 string bytes)
  call_sid    = "abc"  → 03 00 00 00 61 62 63
On the wire (hex, spaces for clarity):
0e 00 00 00     length = 14
12 64 b0 e5     method_id = 3854301714
00              version
00              compat_version
07 00 00 00     payload_size = 7
03 00 00 00     string length = 3
61 62 63        "abc"
Total bytes written: 18 (4 + length).

Reading a frame

import struct

def read_frame(stream):
    header = stream.recv_exact(4)
    length = struct.unpack("<I", header)[0]
    body = stream.recv_exact(length)
    method_id = struct.unpack("<I", body[:4])[0]
    payload = body[4:]               # serde envelope
    return method_id, payload
The serde envelope can then be parsed structurally per the schema for that method_id.