The SDK acts on behalf of a user, whether that’s a full-access Subscriber or a guest with limited access. It authenticates with a Subscriber Access Token (SAT), a short-lived credential that identifies that user and carries the capabilities granted to them. Your backend creates the SAT using your Project API Token, then hands it to the browser, where the SDK uses it to open a WebSocket session with SignalWire.
The sections below cover which kind of SAT to create, how to deliver it to the browser, and how to keep the session alive past the SAT’s expiry.
Before you start. You need a SignalWire space, a Project ID, and an API token with at least one of the Voice / Messaging / Fax / Video scopes. All three are in the API Credentials section of the dashboard. The Project API Token is what creates SATs. Keep it server-side only.
The SDK supports four authentication patterns, each shaped by who is holding the credential and what they need to do with it.
For apps where users sign in with an account. Each user can place and receive calls.
For users without an account who need limited calling, typically to a short list of destinations you allow.
For embedding a “call us” button on a public webpage. Anyone visiting can dial one preset destination.
For giving a specific recipient a way to connect to a call through a shareable invite.
Each pattern uses a different credential: three Subscriber Access Token (SAT) flavors issued for a user, guest, or invitee, and a separate Embed token for public widgets. The credential’s capabilities determine what the holder can do:
Match the credential’s reach to the trust level of whoever holds it. If a credential can dial anyone, then anyone who can read it can dial anyone, so use the delivery model below that keeps the credential out of untrusted hands.
Credentials reach the SDK one of two ways. Embed tokens live in the page itself. Every other variant is created server-side and handed to the browser; the only thing that differs is whether the SDK should keep the session alive past the credential’s first expiry.
Embed tokens are the only credential designed to sit in a public page. They are pinned to one Click-to-Call resource: anyone who reads the page can only dial the resource the embed token was created for. That fixed scope is what makes them safe to expose to every visitor.
Getting an embed token is a two-step setup:
c2c_ prefix) tied to that resource.POST /api/embeds/tokens. The embed token is what the SDK is built to consume for public widgets.The SDK also accepts a C2C token directly as a shortcut (convenient for testing), but production widgets should pass the exchanged embed token. The examples below use the shortcut form so they can run with only the dashboard value.
Shortcut for a single call. embeddableCall() handles credential exchange, client construction, and dial in one call:
Full SDK setup for multiple calls or client-level subscriptions. Pass EmbedTokenCredentialProvider to the SDK directly. You keep a long-lived SignalWire client that can dial repeatedly and exposes observables you can subscribe to. The provider exchanges the embed token for a Guest SAT and refreshes automatically:
The browser asks for a SAT, your backend creates one using the Project API Token, and the SDK uses it for the session.
The browser never talks to SignalWire directly here. Creating any SAT requires the Project API Token, which can issue a SAT for any user in your project. That is why it stays server-side. The hop through your backend is what enforces “this browser session can only get the SAT it’s authorized for.”
Three SAT variants come through this path:
POST /api/fabric/subscribers/tokens): full user identity for a signed-in user; can receive inbound calls. Also called a default-scope SAT when you need to distinguish it from the variants below.POST /api/fabric/guests/tokens): outbound-only, pinned to up to 10 allowed_addresses.POST /api/fabric/subscriber/invites): outbound-only, pinned to one address; created client-side by a signed-in user and delivered out-of-band (URL, email, QR code) to one recipient.Once the SAT is in the browser, the next decision is whether the session needs to outlive a single SAT. For one-shot sessions (typical for Guest and Invite SATs), use StaticCredentialProvider. The SDK uses the fetched SAT until it expires, then the session ends. For sessions that must outlive a single SAT, pick a refresh strategy below.
SATs are short-lived (two hours by default), which limits the damage if one ever leaks. When a SAT expires, the SDK’s WebSocket session ends with it unless a fresh SAT is supplied first. Refreshing is the process of swapping in a fresh SAT before the current one expires, so the session continues uninterrupted: the user stays connected, ongoing calls aren’t dropped, and they don’t need to re-authenticate.
There are two ways to refresh a SAT, depending on where the rotation logic should live.
The backend rotates the SAT. Your CredentialProvider exposes a refresh() method that fetches a fresh SAT from your backend; the SDK calls it shortly before the current SAT’s expiry_at. Every rotation roundtrips through your backend.
Inside /api/signalwire-token, your backend produces the fresh SAT one of two ways:
Every SAT comes back with a companion refresh_token. Your backend stores it and swaps it for a new SAT/refresh-token pair via POST /api/fabric/subscribers/tokens/refresh. This keeps the session going without re-checking the user’s app session on every rollover.
The new access token carries the standard SAT lifetime; the new refresh token outlives it by five minutes so the swap has slack. Store refresh tokens encrypted, server-side only.
The SDK rotates the SAT directly with SignalWire after it is first issued. Your backend is involved only at startup.
This path binds the SAT to the browser session that requested it. The SDK provides a public fingerprint at authentication time; the backend includes that fingerprint plus scope: "sat:refresh" on the create request. Refresh calls are then signed against the matching private key the browser holds, so a SAT lifted off the wire can’t be rotated from anywhere else.
On the backend side, forward the fingerprint and request the refresh scope when creating the SAT:
For the rotation endpoints, refresh events you can subscribe to, and failure modes, see CredentialProvider.
You can provide a refresh() method and request sat:refresh scope. The SDK
picks one mechanism per session: if the SAT carries sat:refresh scope, the
client-side (Client Bound SAT) path wins and your refresh() is never called;
otherwise it falls back to your refresh(). This makes refresh() a safe
backstop — if the backend ever drops the scope, the session keeps rotating
instead of dying at expiry.
That fallback is silent by default. To observe it — for example, to alert when
a deployment expected to use bound tokens has downgraded to a developer-managed
refresh — subscribe to client.warnings$:
Constructing SignalWire runs three steps in sequence: authenticate the SAT, open the WebSocket, and register the user as online. Each step runs by default, and each can be deferred with a constructor option in SignalWireOptions so your UI can drive it later.
register() tells SignalWire the user is online on this session, so inbound calls and presence events route here. It runs automatically when the client constructs unless skipRegister: true is set — defer it when the user needs to opt in (microphone prompt, “Go online” toggle, permissions step) before they start receiving calls.
unregister() is the opposite: the user goes offline for inbound calls, but the WebSocket stays open so outbound calls and observable subscriptions keep working. Use it for Do Not Disturb, app-background, or “available / away” toggles.
Closing the session entirely is a separate step. disconnect() closes the WebSocket, and destroy() wipes persisted state on explicit logout.
Only credentials issued with full user (Subscriber) identity can register. Guest SATs, Invite SATs, and embed-derived Guest SATs are outbound-only, so register() is a no-op on those clients — inbound calls require a full Subscriber Access Token.
Create a Subscriber Access Token (SAT) for your project using the request snippet below. Have your space name and an API token ready, with at least one of the Voice / Messaging / Fax / Video scopes. Both come from the API Credentials section of the SignalWire dashboard. Open the Create Subscriber Token reference to send the request with your space and credentials filled in.
The Project API Token can issue a SAT for any user in your project. Use a development project, or rotate the API token afterward.
Copy the returned token, save the page below as auth-demo.html, and open it in a browser. Paste the SAT into the input, click Authenticate, and watch the log. It reports whether the SDK was able to open a session with the SAT.
You should see Authenticated — WebSocket open. in the log. If you see Failed: InvalidCredentialsError, the SAT is expired, malformed, or issued for a different SignalWire space than the SDK is connecting to. Create a fresh one and try again.