Skip to main content
reactor::Reactor is one session, and the tracks and commands on it. It speaks raw JSON over the wire: open a session, send commands by name, receive generic message events. It works against any model — for the commands and events a specific one accepts, see the Model API Reference, or ask the running model itself with request_schema().
Async calls return std::future<T>, and failures arrive as exceptions from .get() — the same typed hierarchy a synchronous call throws. See ReactorError.
Movable, not copyable — a session has one owner. Destroying a connected client releases the native handle, but a creator that goes away without disconnect() leaves the session orphaned, and the next run cannot start until it clears.

Constructors

Signature
ApiKey and Jwt are distinct one-field structs rather than two std::string parameters, so the credential you meant is the credential that is used:
Example
The third constructor takes no credential at all, for a local runtime that needs none.
Never ship an API key to an end-user’s machine. Mint a short-lived JWT on your server and pass it as Jwt. See Authentication.
reactor::fetch_jwt() mints that token without a client or a session, so a backend can hand one to somebody else. FetchJwtOptions scopes it: models (unset with sessions, it mints an unscoped token), max_sessions and max_session_duration_seconds for what the holder may open, and expires_after for how long the token lives. sessions binds the token to sessions that already exist, by id. Without models and max_sessions, that token acts only on those sessions and cannot create new ones.
Example

Options

Definition

Executor

Signature
By default the SDK runs control-event handlers on one thread of its own, serialised, never on a library thread. Give it an executor and it hands that callable each event instead — for a host with a loop of its own (Qt, ASIO, a game loop) that would rather own when handlers run.
Example
It is called from a library thread, so it must be safe to call from any thread.
Futures do not go through the executor. A promise is settled on the library’s own completion thread, so connect().get() on the same thread that would have run the executor cannot deadlock against it.
Media handlers do not go through it either. on_frame() runs inline on the library’s delivery thread, for video and audio both, on purpose — blocking in one is the backpressure. See VideoFrame.

Connecting

connect()

Creates — or adopts — a session and brings up the transport. Resolves when the session is Ready.
Signature
Definition
Throws the typed failure: UnauthorizedError for a token problem, ConflictError for a session a previous run left orphaned.
Example

reconnect()

Cycles the connection without ending the session — after a transient failure, or deliberately from Ready.
Signature
It tears the live connection down itself, so there is no need to disconnect() first — and doing so would end the very session this is about to reuse. Throws when there is no session to reconnect to.
RecvOnly tracks resume automatically unless auto_resume_tracks is false. SendOnly tracks do not: a track published before the reconnect is not published after it, so publish again for anything you were sending. Track::published() says which side of that you are on, and push_frame() throws rather than pushing into a slot with nothing behind it.

disconnect()

Ends the session server-side and tears down the transport.
Signature
Not recoverable — there is no parameter that keeps the session alive instead. To disconnect and later resume the same session, call reconnect(). Only ends sessions this client created; one adopted via session_id is left running for its owner.

status()

Signature
Where the session is now, as a Status. Readable before connect() — a client that never connected reports Disconnected rather than nothing.

session_id()

Signature
The session’s id, once there is a session.

Commands and uploads

send_command()

Sends a command to the model and waits for its correlated reply.
Signature
The SDK carries a payload over 256 KiB, up to 64 MiB, when the runtime accepts data-channel chunking. A runtime that does not accept it keeps the 256 KiB limit, and a larger payload fails with MESSAGE_TOO_LARGE. The reply is {type, data}, or empty when the handler ran and acknowledged the command without returning a message, as an auto-generated set_<field> setter does. Empty is not a failure and is not folded into one:
Example
To fire a command without waiting on the reply, keep the future and call .get() later — or drop it, which sends the command all the same. These futures are settled by a promise rather than by std::async, so dropping one neither blocks nor cancels; it only means a failure has nowhere to be thrown.

upload_file()

Uploads a local file and returns a FileRef to pass into a command.
Signature
Needs a Ready session — the upload is created against it. Throws NotFoundError when the path does not exist.
Example
A parameter that takes one file is passed separately rather than embedded in args. The Python SDK finds a FileRef sitting in the arguments and pulls it out; C++ has no way to recognise one inside a Json, so it is named in the third parameter instead. Explicit costs a few characters and cannot silently miss one.A parameter that takes several has no named slot. Put the references in args instead: a FileRef converts to Json as the upload reference the model reads.

upload_bytes()

The same result as upload_file(), for a caller who has the bytes rather than a path — a frame just rendered, a buffer just decoded.
Signature
data is borrowed for the call only.
Example

request_schema()

Signature
The model’s command schema, as an OpenAPI document — the same schema published on the Model API Reference pages, fetched from the running model. What to read when a command is rejected: it is the model’s own account of what it accepts, which is more current than any documentation.

Recordings

request_clip()

Asks for a clip covering the last duration_seconds of the session.
Signature
Resolves when the platform has accepted the request, which is not the same as the clip being ready — Clip::download() is what waits for that.
Example
See Recordings for the full flow.

request_recording()

Signature
The same, covering the whole session up to now.

Tracks

track()

The track called name, as a Track — the only way to push frames into one, receive its frames, or pause it.
Signature
Example
Throws NotFoundError, listing what the session does declare, for a name that is not among them. Before the session has declared anything, any name is allowed: there is nothing yet to contradict, and the refusals that matter happen when a handler is registered or a frame is pushed.

tracks()

Every track the session declared, as a TrackList — for discovery, and for a caller who would rather not hardcode a name.
Signature
Example
Empty until the model’s capabilities arrive, shortly after connect().

set_bitrate()

Bounds what the whole connection may allocate, in bits per second.
Signature
Definition
There are two bitrate ceilings and they are conjunctive — the lower one wins. This is the connection-wide one. Track::set_bitrate() bounds one sender’s share of it, and that is the one that lifts WebRTC’s 2.5 Mbps video default. Raising max_bps here alone will not make a video track exceed it.
Throws on a session that is not Ready. The bounds outlive a reconnect.

get_stats()

A snapshot of the live connection: RTT, jitter, packet loss, bitrates, the transport ICE selected, and the WebRTC engine’s own per-stream counters.
Signature
Returns a ConnectionStats. Throws on a session that is not Ready.
Example

Events

Each method takes one handler callback. Its argument type is listed in the table below. Every on_* returns a Subscription: an RAII token that unregisters the handler when it goes out of scope.
Discarding the return value unregisters immediately. client.on_status(...); as a statement registers a handler and cancels it on the same line. Hold the token, or call .detach() to say the handler should live as long as the client does. This is the one place the C++ surface diverges from the Python one, which offers off(event, handler) — two std::functions cannot be compared, so a token is the only honest removal.
Signature
Register the last two before connect() to catch the first capabilities and the first session id. Capabilities are delivered after the declared tracks are refreshed, so tracks() is already current inside that handler.
Example
Model messages and platform messages are separate events because they are separate things: a caller reading only on_message never has to filter the platform’s out of it.
There is no client-wide frame event. Media is delivered per track, through Track::on_frame() — a single handler fed every incoming track of a kind at once could not tell them apart.
on_error hands you a ReactorError, the same type a failed call throws. Match on code(), or branch on recoverable() when the specific code does not matter. See ReactorError.

time_micros()

The engine’s monotonic clock, in microseconds — the epoch a frame’s capture time is read in.
Signature
Read it once per unit of produced media and stamp every track with that one value: tracks are synchronised by sharing a capture time, not by reaching the encoder at the same moment.
Example
Unrelated to the system clock — a UNIX timestamp is not a substitute.