Voxify Studio Docs Sign in
Configuration · Authentication

Authentication

The SDK never holds your Voxify API key. Instead, it asks your backend for a short-lived bearer token, through two callbacks you provide. Your server is the only place that knows the API key — the browser only ever sees a disposable token scoped to one end user.

How the token flow works

Four steps, and three of them happen behind the scenes:

  1. On startup, the SDK calls your auth.getToken(userId) callback.
  2. Your callback POSTs to your token endpoint (same-origin with your app, so no secret is exposed).
  3. Your backend calls Voxify's POST /v1/auth/token server-to-server, using your API key, and returns the token to the browser.
  4. From then on, the editor attaches that token to every /v1/* request. If a request comes back 401, the SDK calls auth.refreshToken(expiredToken) and retries once — automatically.
Why two endpoints, not a key in the page? A long-lived API key in client-side JavaScript can be copied by anyone who opens dev tools. Minting short-lived tokens on your server keeps the key private, lets you authorise per user, and lets you revoke access without redeploying the embed.

Configuring it

In the Embed Builder, the Authentication section has three fields. The Builder turns the two endpoint fields into the auth.getToken / auth.refreshToken callbacks in your snippet — you don't write the callback bodies yourself unless you want to.

Token Endpoint

What it is — your backend endpoint that exchanges your credentials for a Voxify bearer token. Required.

Why / when — this is the heart of the integration: it's how a browser session gets authorised without ever seeing your API key. Every embed needs it.

How — enter your endpoint path (e.g. /api/voxify/token) in the Builder. It becomes the URL the generated getToken callback POSTs to. The SDK passes the resolved end-user id as the argument, so you can scope the token per user:

javascript
auth: {
  getToken: async (userId) => {
    // Call YOUR backend. It calls Voxify POST /v1/auth/token with your API key.
    const res = await fetch('/api/voxify/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId }),
    });
    return (await res.json()).token;   // a string bearer token
  },
  // refreshToken below…
}

Your callback must return either a token string, or an object { token, type } if you use a non-default scheme (type defaults to Bearer). Anything else is treated as an auth failure.

Refresh Endpoint

What it is — your backend endpoint that issues a fresh token when the current one expires. Required.

Why / when — tokens are deliberately short-lived. Without a refresh path, a user mid-session would hit a wall when their token aged out. The SDK calls this automatically on a 401, so the user never notices.

How — enter your refresh path in the Builder. The generated refreshToken callback receives the expired token in the Authorization header so your backend can validate and reissue:

javascript
  refreshToken: async (expiredToken) => {
    const res = await fetch('/api/voxify/refresh', {
      method: 'POST',
      headers: { 'Authorization': 'Bearer ' + expiredToken },
    });
    return (await res.json()).token;
  },

User ID

What it is — your end-user's unique identifier, an opaque string. Optional.

Why / when — set it to tie each embed session to a known user — for per-user credit tracking and project isolation in your Voxify dashboard. If you leave it blank, the SDK generates a stable id from a browser fingerprint, which is fine for anonymous or trial flows but won't line up with your own user records.

How — pass it as auth.userId. The SDK forwards whatever it resolves (your id, or the generated one) to your getToken callback as the userId argument, so you can scope the minted token to that user:

javascript
auth: {
  userId: 'acct_1837::user_42',   // your opaque id; omit for an anonymous fingerprint
  getToken:     async (userId) => { /* … */ },
  refreshToken: async (expiredToken) => { /* … */ },
}
Builder note. The Embed Builder's User ID field is a convenience for the live Playground; it is not yet written into the copied snippet. To bind a real user in production, add auth.userId to the config yourself, as shown above.

The full auth block

Put together, the authentication portion of your create() call looks like this:

javascript
window.VoxifyStudioEditor.create({
  target: '#voxify-script-editor-root',
  auth: {
    userId: 'acct_1837::user_42',
    getToken: async (userId) => {
      const res = await fetch('/api/voxify/token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ userId }),
      });
      return (await res.json()).token;
    },
    refreshToken: async (expiredToken) => {
      const res = await fetch('/api/voxify/refresh', {
        method: 'POST',
        headers: { 'Authorization': 'Bearer ' + expiredToken },
      });
      return (await res.json()).token;
    },
  },
  // …the rest of your configuration
});

Security checklist

Troubleshooting

401 on every request

The session never starts