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:
- On startup, the SDK calls your
auth.getToken(userId)callback. - Your callback POSTs to your token endpoint (same-origin with your app, so no secret is exposed).
- Your backend calls Voxify's
POST /v1/auth/tokenserver-to-server, using your API key, and returns the token to the browser. - From then on, the editor attaches that token to every
/v1/*request. If a request comes back401, the SDK callsauth.refreshToken(expiredToken)and retries once — automatically.
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:
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:
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:
auth: {
userId: 'acct_1837::user_42', // your opaque id; omit for an anonymous fingerprint
getToken: async (userId) => { /* … */ },
refreshToken: async (expiredToken) => { /* … */ },
}
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:
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
- Keep your API key server-side. It belongs only in the backend that calls
POST /v1/auth/token— never in page JavaScript. - Authorise in your endpoint. Your token endpoint runs in your app's session, so check the caller is a logged-in user before minting a token.
- Scope per user. Pass a real
userIdso credits and projects attribute correctly and you can revoke one user without affecting others. - Don't ship demo tokens. The Playground's demo token is rate-limited and tied to an anonymous account — production must mint tokens through your own backend.
Troubleshooting
401 on every request
- Your
getTokenmust return a string or{ token, type }— anything else fails silently as "no token". - The token must be issued for the same Voxify account/key the embed expects. Mixing dev and prod keys is a common cause.
The session never starts
create()returns a Promise that rejects with a descriptive error if auth fails — check the console.- Confirm your token endpoint is reachable from the browser (same-origin, or CORS-enabled) and returns JSON with a
tokenfield.