Skip to content

SDK and API Examples

These examples form one runnable chain: login, refresh token, create room, create WebSocket ticket, connect Realtime, read playback state, handle errors, and call Providers. Complete fields are defined by OpenAPI JSON and protobuf.

Read Client Integration Guide first for the protocol boundaries.

GoalInterface
Generate multi-language SDKsHTTP OpenAPI
Strongly typed backend or desktop clientsgRPC/protobuf
Room realtime messages and playback syncWebSocket Realtime
Instance operationsCLI or management gRPC
Provider browse, login, proxyHTTP Provider routes or provider gRPC

Start with OpenAPI enabled:

Terminal window
cargo run -p synctv --features openapi --bin synctv -- serve

Export the schema:

Terminal window
curl -fsSL http://localhost:8080/api-docs/openapi.json -o openapi.json

Generate TypeScript types:

Terminal window
npx openapi-typescript openapi.json -o synctv-api.d.ts

See OpenAPI Access for details.

Terminal window
BASE_URL=http://localhost:8080
curl -sS "$BASE_URL/api/auth/opaque/login/start" \
-H 'Content-Type: application/json' \
-d '{"username":"root","credentialRequest":[/* bytes from your OPAQUE client */]}'

Finish the OPAQUE exchange with /api/auth/opaque/login/finish. If the finish response has mfa.required=true, complete MFA before treating the user as logged in. Passwordless email-token login uses /api/auth/email/confirm.

const ws = new WebSocket(`wss://app.example.com/ws/rooms/${roomId}?ticket=${ticket}`);
ws.binaryType = 'arraybuffer';
ws.onmessage = async (event) => {
const bytes =
event.data instanceof Blob
? new Uint8Array(await event.data.arrayBuffer())
: new Uint8Array(event.data);
const message = ServerMessage.decode(bytes);
dispatchServerMessage(message);
};
function send(message: ClientMessage) {
ws.send(ClientMessage.encode(message).finish());
}
send({
observeResource: {
observeId: 'playback-state',
version: cachedVersion ?? '',
deliveryMode: 2,
playbackState: {},
},
});

See Realtime API for resource observation, delivery modes, reconnects, and errors.

Public gRPC uses the main port:

Terminal window
grpcurl -plaintext localhost:8080 list synctv.client.AuthService

The public AuthService local password flow uses OPAQUE. Generate the credential request in your client and call StartOpaqueLogin, then finish with FinishOpaqueLogin:

Terminal window
grpcurl -plaintext \
-d '{"username":"root","credentialRequest":"<base64-opaque-request>"}' \
localhost:8080 synctv.client.AuthService/StartOpaqueLogin

AuthService/Login is reserved for passwordless email-token login.

Room RPCs need room metadata:

Terminal window
grpcurl -plaintext \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-room-id: $ROOM_ID" \
-d '{}' \
localhost:8080 synctv.client.RoomService/GetPlayback

Do not expose management gRPC to normal clients.

Terminal window
curl -sS "$BASE_URL/api/providers/instances/available" \
-H "Authorization: Bearer $ACCESS_TOKEN"

Alist list request:

Terminal window
curl -sS "$BASE_URL/api/providers/alist/list" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"serverId":"<serverId-from-login-or-binds>","path":"/","instanceName":"alist-main"}'

When adding media, the sourceProvider, providerInstanceName, and sourceConfig combination is defined by the Provider. Provider credentials are resolved by the server-side Provider credential layer.

HTTP error response:

{
"error": "Invalid username or password",
"status": 401,
"code": 1002,
"requestId": "01HX..."
}

Client rules:

  • Use HTTP status for the broad category.
  • Use code for programmatic branches.
  • Log requestId for support.
  • Respect Retry-After on 429.
  • Do not depend on raw 5xx text; it is sanitized.

See Errors.

OperationHandling
Login, MFA, OAuth2 exchangeDo not blindly retry; state or rate limits may be affected
ReadsRetry with backoff; use Realtime versions where possible
Create room, add media, update settingsOn timeout, read back state before repeating
WebSocket reconnectCreate a new ticket and rebuild observations
Provider requestBack off upstream failures and distinguish credential expiry