Connections
Connection Types
The SDK supports different connection modes based on the provider:
API Key Connection
{
apiKey: string;
config?: BaseConfig;
}
Sofya Compliance Provider Connection
{
provider: "sofya_compliance";
endpoint: string;
config: SofyaComplianceConfig;
}
Sofya As Service Provider Connection
{
provider: "sofya_as_service";
endpoint: string;
config: SofyaSpeechConfig;
}
Configuration Types
BaseConfig
interface BaseConfig {
language: string;
external_id?: string;
record?: boolean;
}
| Field | Type | Description |
|---|---|---|
external_id | string (optional) | Identifier of the consultation in your application. Sent to the server as the x-external-id query param on every connection — including reconnections — and used to group all recordings belonging to the same consultation. Must be opaque: a UUID, hash, or internal id — never a CPF, name, readable medical record number, or email. To be used literally in the recording's storage path it must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$ (starts with a letter or digit, up to 64 characters, no :, /, spaces or accents); outside that pattern the server stores the recording under a hash and keeps the original value only in the metadata. Maximum 512 characters. Use the same value on every reconnection for a given consultation. |
record | boolean (optional) | Requests (true) or refuses (false) server-side recording of this session. When omitted, the SDK sends nothing and the environment's default decides. true only results in a recording if the environment allows it — otherwise the session proceeds normally. Recording never changes the transcription itself. |
const transcriber = new SofyaTranscriber({
provider: "sofya_as_service",
endpoint: "<wss-url>",
config: {
language: "pt-BR",
external_id: "<consultation-id>",
record: true,
},
});
external_id and record are additive and optional. Applications that don't set them keep working exactly as before, including against older servers.
SofyaComplianceConfig
interface SofyaComplianceConfig extends BaseConfig {
token: string;
compartmentId: string;
region: string;
}
SofyaSpeechConfig
interface SofyaSpeechConfig extends BaseConfig {
auth?: DirectModeAuth;
}
Authentication in Direct Mode (auth)
Direct connections ({ provider, endpoint, config }) can send the STT API key through the optional config.auth field. It is available on the configs of the direct Whisper providers (sofya_as_service, sofya_whisper_flow, stt_wvad); the API key connection is unchanged, because it already authenticates through the Sofya API.
type DirectModeAuth =
| { type: "none" }
| {
type: "api_key";
key: string;
transport?: "subprotocol" | "query";
}
| { type: "jwt"; token: string };
| Field | Type | Description |
|---|---|---|
type | "none" | "api_key" | "jwt" | "none" is the default when auth is omitted and sends no credentials. "api_key" sends the STT API key. "jwt" sends a JWT instead (see JWT). |
key | string | The STT API key. Required when type is "api_key". The SDK never transforms it. |
transport | "subprotocol" | "query" (optional) | How the key reaches the server. Defaults to "subprotocol". Browsers cannot set headers on a WebSocket handshake, so these are the only two options. |
token | string | The raw JWT. Required when type is "jwt". Without Bearer — the SDK adds it. |
transport: "subprotocol" (default)
The key travels as an extra Sec-WebSocket-Protocol token, x-api-key.<key>. The server strips that token before the application sees it and echoes back the first requested subprotocol, so the SDK always keeps your own protocols in front and appends the key token last.
const transcriber = new SofyaTranscriber({
provider: "sofya_as_service",
endpoint: "<wss-url>",
config: {
language: "pt-BR",
auth: { type: "api_key", key: "<stt-api-key>" },
},
});
config.protocols | Subprotocols sent on the handshake |
|---|---|
| omitted | ["sofya-stt.v1", "x-api-key.<key>"] |
"bearer.jwt" | ["bearer.jwt", "x-api-key.<key>"] |
["bearer.jwt", "sofya-stt.v1"] | ["bearer.jwt", "sofya-stt.v1", "x-api-key.<key>"] |
- The server rejects a handshake whose only subprotocol is the key token (
subprotocol_companion_required), so the SDK adds thesofya-stt.v1companion token when you configure no protocol. - RFC 6455 subprotocol tokens accept only letters, digits and
!#$%&'*+-.^_`|~—=,/,,, spaces and quotes are not allowed. With a key outside that set the SDK throwsSofyaAuthErrorbefore connecting; issue a token-safe key or use thequerytransport.
transport: "query"
The key is appended to the realtime URL as the x-api-key query parameter and config.protocols are forwarded untouched.
const transcriber = new SofyaTranscriber({
provider: "sofya_as_service",
endpoint: "<wss-url>",
config: {
language: "pt-BR",
auth: {
type: "api_key",
key: "<stt-api-key>",
transport: "query",
},
},
});
URLs are recorded by proxies, gateways, access logs and browser history. Prefer subprotocol and use query only when the key cannot be an RFC 6455 token.
- Reconnections reuse the same authentication — the URL and the subprotocol list are rebuilt identically on every attempt.
- The key is also sent as the
x-api-keyheader on the HTTP calls the SDK makes (batch reprocess and audit ingestion), unless that header is already configured. - The SDK never logs the key: it appears as
[redacted]in debug output. - When the server rejects the key it denies the handshake with HTTP 401 or closes with code
4401. On4401the SDK emitserrorwith aSofyaAuthError(codeAUTH_REJECTED) and stops reconnecting instead of retrying the same key.
auth is additive and optional. Omitting it (or using { type: "none" }) keeps the previous behavior, including the configured protocols and the URL.
type: "jwt"
Sends a JWT instead of the API key. The two strategies are exclusive: a session authenticates with one of them, and every request the SDK makes follows it.
const transcriber = new SofyaTranscriber({
provider: "sofya_as_service",
endpoint: "<wss-url>",
config: {
language: "pt-BR",
auth: { type: "jwt", token: "<jwt>" }, // "eyJ...", not "Bearer eyJ..."
},
});
Do not add Bearer to the token: the SDK adds the prefix to the HTTP requests. A token that starts with Bearer makes the SDK throw SofyaAuthError with the code INVALID_JWT before connecting; the prefix is never removed silently.
| Request | api_key | jwt |
|---|---|---|
| WebSocket | x-api-key.<key> subprotocol (last) or x-api-key query | the JWT as the first subprotocol, before config.protocols, so the server echoes it |
| Batch, batch reprocess, audit ingestion | x-api-key header | Authorization: Bearer <token> |
| Final upload | none | none |
- Realtime sessions send the JWT as a subprotocol, so it must be an RFC 6455 token. A standard JWT (base64url, no padding) always is; anything with
=,/,,, spaces or quotes throwsSofyaAuthError(INVALID_JWT_CHARACTERS) before connecting. Batch sessions send it only as a header and accept any character. - Do not also put the JWT in
config.protocols, or it is sent twice. - An
Authorizationheader set inconfig.headerswins over the JWT. - The Sofya STT does not validate JWTs. Use
jwtwith STTs that have authentication off or sit behind a gateway that validates the token; an STT that requires an API key rejects the connection. Audit ingestion requiresx-api-key, so withjwtalone it is skipped with themissing_api_keywarning. - The JWT never appears in debug output (
[redacted]).
config.tokenIn direct mode, config.token is deprecated and will be removed in 1.0.0; use auth: { type: "jwt", token }. Until then it still becomes Authorization: Bearer <token>, the SDK logs a deprecation warning, and auth.token wins when both are set.
Connection Flow
API Key Connection Flow
- The SDK authenticates with the Sofya API using the provided API key.
- The API returns a list of available providers and their configurations.
- The SDK attempts to connect to each provider in order until a successful connection is established.
- Once connected, the SDK emits a 'ready' event.
Provider-Specific Connection Flow
- The SDK attempts to connect directly to the specified provider using the provided endpoint and configuration.
- Once connected, the SDK emits a 'ready' event.