Header Logo

Troubleshooting 401s and WebSocket Auth Failures in Realtime AI Avatar Kiosk Deployments

Troubleshooting 401s and WebSocket Auth Failures in Realtime AI Avatar Kiosk Deployments

Debug 401s and WebSocket auth failures in realtime AI avatar kiosks: API keys, token expiry, origin policy, reconnects, and clock drift.

Introduction


When a realtime avatar kiosk starts returning 401s or the video face connects once and then WebSocket auth falls apart, the failure is usually not “the avatar service is flaky.” It’s almost always an integration issue: the wrong credential on the wrong surface, an expired token, an origin mismatch, or a browser/network path that doesn’t match your deployment model.


This post breaks down the failure modes I see most often in kiosk-style deployments: a browser-based front end talking to a voice or session backend, a WebSocket or WebRTC connection that has to stay authenticated long enough to be useful, and an avatar service that is intentionally strict about who can create sessions. By the end, you should be able to isolate whether the problem is API auth, session auth, CORS/origin policy, or transport-level connectivity, and you should know what to check first when a kiosk goes from “works in staging” to “401 in production.”


Start by identifying which connection is failing


“401” is overloaded. In a realtime avatar stack, it can happen in at least three different places:


  • REST API requests to create avatars, sessions, or fetch metadata.

  • WebSocket upgrade/auth for realtime session signaling or agent backends.

  • Browser iframe/embed setup where the parent page is allowed to load the avatar, but the embed rejects the origin or session parameters.


The first debugging step is to find the exact request that failed and which identity it used. A kiosk often has more than one credential path:


  • Server-side API key for creating/managing sessions.

  • Short-lived session token or signed parameters for the realtime connection.

  • Browser-only embed policy that should never expose an API key at all.


If you treat all of those as “auth,” you end up checking the wrong thing. Instead, inspect the failing request:


  • Is it an HTTPS request to the REST API?

  • Is it a WebSocket handshake returning 401 before the socket opens?

  • Is it an iframe or browser-origin error that looks like auth but is really policy enforcement?


REST 401s: bearer token, environment, and scope mistakes


For server-side calls, the most common bug is surprisingly mundane: the kiosk backend is sending the wrong API key. In mixed environments it’s easy to point a production kiosk at a staging key, a stale key, or a key copied with extra whitespace. It’s also common to accidentally ship a browser-visible app that tries to call the REST API directly, which should not happen for a key-based backend surface.


A correct REST call should look like this, with the API key kept on the server:


curl -X POST https://api.protoface.com/v1/sessions \
-d '{"avatar_id":"avt_123","quality":"standard"}'
curl -X POST https://api.protoface.com/v1/sessions \
-d '{"avatar_id":"avt_123","quality":"standard"}'
curl -X POST https://api.protoface.com/v1/sessions \
-d '{"avatar_id":"avt_123","quality":"standard"}'


When you get a 401 here, check these items in order:


  1. Key prefix and environment: production kiosk using a production key, not a test key.

  2. Header formatting: the token must be in the Authorization header as a Bearer token; don’t pass it in JSON.

  3. Whitespace and rotation: copied secrets often include leading/trailing spaces or a stale value after a deploy.

  4. Server clock and expiration: if you mint any short-lived token in your own backend, time skew can make a valid-looking token fail immediately.


If you’re using the Python SDK, the same rule applies: initialize it in a trusted backend process, not in the kiosk browser.


from protoface import ProtofaceClient

print(session)
from protoface import ProtofaceClient

print(session)
from protoface import ProtofaceClient

print(session)


The exact request fields depend on the endpoint and are documented in the docs, but the auth pattern does not change: keep API keys server-side, and assume any direct browser use is a misconfiguration unless you are explicitly using a customer-managed embed.


WebSocket auth failures: upgrade timing, token lifetime, and reconnect behavior


WebSocket auth problems are nastier because the failure often happens before you have useful application logs. The socket may fail during the HTTP Upgrade request, or it may connect and then close on the first authenticated message. In kiosk deployments, that usually comes down to one of three issues:


1. You are authenticating too early or too late. If the kiosk obtains a short-lived session token and then sits idle at a welcome screen for several minutes, the token can expire before the user starts speaking. The next socket open fails with a 401 even though the kiosk “just loaded.” The fix is to mint tokens closer to connection time or refresh them before connect.


2. Your reconnect logic reuses stale credentials. A WebSocket reconnect that blindly reuses the previous token will often fail after the first disconnect. If you have offline networking, kiosk sleep/resume, or captive portal transitions, build reconnect so it can fetch a fresh session credential from your backend.


3. You are confusing signaling auth with media transport. In realtime systems, the control plane and media plane are separate. The auth error may be on the signaling path while the actual audio/video transport is failing for network reasons. Don’t assume a socket failure means the media layer is broken.


A practical debugging pattern is to log the lifecycle explicitly:


  • When the backend mints the session credential.

  • When the kiosk receives it.

  • When the socket connect starts.

  • When the socket upgrade succeeds or fails.

  • When the socket reconnects and whether the credential changed.


For kiosk systems, I also recommend attaching a request ID or session ID to every backend log line so you can correlate the REST session creation call with the later WebSocket attempt. Without that, you’ll waste time diagnosing a socket that was doomed by an earlier session creation failure.


Browser and iframe deployments: auth errors that are really origin policy


For customer-facing website embeds, a lot of “auth failures” are actually policy mismatches. If the product uses an iframe model, the browser should never see an API key; instead, the embed is constrained by parent-origin allowlisting, per-embed instructions, and rate limits. That means the failure mode is usually not “bad bearer token” but “this parent origin is not allowed” or “this embed configuration doesn’t match the current site.”


This matters in kiosks too, especially when you deploy the same web app across multiple domains, local preview hosts, or vendor-managed kiosk shells. A kiosk on https://kiosk.example.com is not the same origin as a test machine running http://localhost:3000, and production allowlists need to reflect that exactly.


When you see an iframe-related auth failure, check:


  • Origin exactness: scheme, host, and port all matter.

  • Redirects: the page that loads the embed may not be the final origin after redirects.

  • Proxy layers: reverse proxies can change the apparent origin or strip headers that your setup expects.

  • Multiple kiosk hosts: if you have several deployment URLs, make sure every one is on the allowlist.


In other words, if the browser is supposed to be “keyless,” do not start inventing client-side auth workarounds. Fix the embed policy and the origin configuration instead.


A useful debugging sequence in production


When a kiosk is down, I use a short, repeatable sequence:


  1. Reproduce with curl or a backend log: verify the REST API key works from the server that should be trusted.

  2. Check session creation timestamps: confirm the realtime credential is still valid when the kiosk tries to connect.

  3. Inspect the exact failing handshake: if possible, capture the HTTP status on the WebSocket upgrade, not just “socket closed.”

  4. Verify origin and redirect behavior: especially for iframe-based or browser-delivered embeds.

  5. Try a clean reconnect: force minting a fresh session token and see if the problem disappears.


One subtle failure mode in kiosk environments is clock drift. If the kiosk machine’s clock is badly skewed, any locally computed expiry or signed token validation can fail even though your backend is correct. In practice, if auth seems random across devices, verify NTP before you chase application code.


How Protoface handles the common cases


Protoface is strict about separating these surfaces, which is helpful in exactly this kind of deployment. The REST API is for server-side creation and management of avatars and realtime sessions. The Python SDK follows the same pattern, so your backend can mint or manage sessions without exposing secrets in the browser. For browser-only kiosk or site embeds, the customer-managed iframe flow avoids shipping API keys to the client at all; access is controlled by parent-origin allowlisting and embed-level limits rather than by a bearer token in front-end code.


That separation prevents a large class of 401s, but it also means you need to use the right surface for the job. If you are building a LiveKit voice agent, the livekit-plugins-protoface integration is usually the right place to start; the plugin drops a synchronized talking face into the agent pipeline without forcing you to hand-roll video/session glue. If you want a working reference for the integration patterns, the quickstarts linked from the repository are the fastest way to compare your setup against a known-good path, and the plugin-specific guidance in the docs is worth reading before you debug transport issues as “auth.”


Conclusion


Most 401s and WebSocket auth failures in realtime avatar kiosks come from one of four problems: the wrong credential on the wrong surface, expired or stale session tokens, origin/policy mismatch in browser embeds, or reconnect logic that reuses dead auth. The fix is usually not to loosen security; it’s to make the boundaries explicit and log the lifecycle so you can see where the failure starts.


If you’re integrating a kiosk or voice agent and you want a fast sanity check, start with a server-side REST call, then validate session lifetime, then verify browser origin behavior, and only after that look at the media transport path. For implementation details and the current request shapes, use docs.protoface.com. If you’re working from a known integration path, the relevant GitHub repos linked in the docs are a better debugging aid than guessing at the handshake.

Add a face to your AI.

No credit card needed.

Add a face to your AI.

No credit card needed.

Add a face to your AI.

No credit card needed.