Authentication
The SDK uses a two-tier authentication model: your backend signs JWTs for end customers using an API key, and your frontend uses those tokens to chat.
Authentication Flow
┌─────────────┐ 1. Request token ┌─────────────────┐
│ Frontend │ ─────────────────────────────▶│ Your Backend │
│ (Browser/ │ │ │
│ Mobile) │◀───────────────────────────── │ │
└─────────────┘ 5. Return JWT └─────────────────┘
│ │
│ │ 2. sdk.auth.sign()
│ 6. sdk.chat.start() │ [apiKey]
│ sdk.chat.send() │
│ [token] │
│ │
│ ┌─────────────────┐ │
│ │ │ │
└─────────────▶│ WonderfulSDK │◀─────────────┘
│ │
└────────┬────────┘
│
│ 3. POST /api/v1/jwt/sign
│ 4. Return JWT
│ 7. Chat API calls
▼
┌─────────────────┐
│ Wonderful API │
└─────────────────┘
Flow:
- Frontend requests a token from your backend (authenticated via your app’s auth)
- Your backend calls
sdk.auth.sign() with API key
- SDK calls Wonderful API to sign the JWT
- JWT returned to backend
- Backend returns JWT to frontend
- Frontend calls
sdk.chat.start(), sdk.chat.send() with JWT token
- SDK makes chat API calls to Wonderful API
Authentication Methods
The SDK supports three authentication methods:
| Method | Config Field | Use Case | WebSocket Protocol |
|---|
| API Key | apiKey | Server-side only — JWT signing, backend integrations | apikey |
| Signed JWT | signedToken | Frontend apps — issued by your backend via sdk.auth.sign() | jwt |
| Wonderful Token | token | Cognito/SSO flows | token |
Never expose your API key in frontend/browser code. Use signedToken with refreshToken for client-side apps.
Authentication Requirements
The user must have at least the External Entity role to use the SDK and receive real-time events via WebSocket.
Auth Module (sdk.auth)
Backend only — requires apiKey in the SDK config.
The Auth module provides JWT token signing for SDK authentication. This allows server-side applications to generate short-lived tokens for end users.
sign(request): Promise<SignResponse>
Sign a JWT token for SDK authentication.
const { jwt, expiresAt, id } = await sdk.auth.sign({
id: 'a1b2c3d4-e5f6-...', // Optional - end-user UUID (auto-generated if omitted)
tenantId: 'tenant-456', // Optional - tenant identifier for multi-tenant deployments
metadata: { // Optional - custom claims to include in the token
name: 'John Doe',
role: 'customer'
},
expiresIn: 7200 // Optional - expiration in seconds (default: 3600)
});
// Returns:
// {
// jwt: 'eyJhbGciOiJSUzI1NiIs...', // Signed JWT token
// expiresAt: 1700000000, // Unix timestamp when token expires
// id: 'a1b2c3d4-e5f6-...' // End-user identifier (UUID)
// }
SignRequest
interface SignRequest {
/** End-user identifier (UUID). Auto-generated if omitted. */
id?: string;
/** Tenant identifier (optional, for multi-tenant deployments) */
tenantId?: string;
/** Custom metadata to include in the token */
metadata?: Record<string, unknown>;
/** Token expiration time in seconds (default: 3600, min: 60, max: 86400) */
expiresIn?: number;
}
SignResponse
interface SignResponse {
/** The signed JWT token */
jwt: string;
/** Token expiration timestamp (Unix epoch seconds) */
expiresAt: number;
/** End-user identifier embedded in the JWT sub claim */
id: string;
}
Validation Rules
| Field | Constraint |
|---|
id | Valid UUID string, or omit for auto-generation |
expiresIn | Integer between 60 and 86400 seconds (1 minute to 24 hours) |
metadata | JSON object, max 4KB when serialized |
Error Codes
| Code | Description |
|---|
VALIDATION_ERROR | Request validation failed (invalid expiresIn, metadata too large, etc.) |
UNAUTHORIZED | Invalid or missing API key |
NETWORK_ERROR | Connection to server failed |
Backend JWT Signing
For server-side applications, use the auth client to sign JWTs for your customers:
import { WonderfulSDK } from '@wonderful-sdk/contact-center';
const sdk = new WonderfulSDK({
baseUrl: 'https://api.wonderful.ai',
apiKey: 'your-api-key' // Required for signing
});
// Sign a JWT for a customer
const { jwt, expiresAt, id } = await sdk.auth.sign({
id: 'user-uuid', // Optional: end-user UUID (auto-generated if omitted)
metadata: { plan: 'premium', region: 'us-west' },
expiresIn: 3600 // Optional: seconds until expiration (default: 3600)
});
Express.js Token Endpoint
import express from 'express';
import { WonderfulSDK } from '@wonderful-sdk/contact-center';
const app = express();
const sdk = new WonderfulSDK({
baseUrl: process.env.WONDERFUL_API_URL,
apiKey: process.env.WONDERFUL_API_KEY
});
// Endpoint for frontend to get SDK tokens
app.post('/api/sdk-token', async (req, res) => {
const { userId } = req.user; // From your auth middleware
const { jwt, expiresAt, id } = await sdk.auth.sign({
id: userId, // Ties communications to this user
metadata: {
email: req.user.email,
plan: req.user.subscription
}
});
res.json({ token: jwt, expiresAt, id });
});
Error Handling
try {
const { jwt } = await sdk.auth.sign({ metadata: { id: 'customer-123' } });
} catch (error) {
if (error.code === 'INVALID_REQUEST') {
// API key not configured
}
}
The auth.sign() method requires an API key. JWT tokens are for client-side use only.
Token Refresh
The SDK supports automatic token refresh via the refreshToken callback. When a 401 is received, the SDK calls your callback to get a fresh token and retries the request.
const sdk = new WonderfulSDK({
baseUrl: 'https://api.wonderful.ai',
signedToken: initialToken,
refreshToken: async (entityId) => {
// Call your backend to get a fresh token
// Pass entityId to preserve the user's identity across token refreshes
const response = await fetch('/api/refresh-sdk-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ entityId }),
});
const { token } = await response.json();
return token;
}
});
Always pass the entityId parameter when refreshing tokens. This ensures the new JWT is issued for the same end-user identity, preserving session continuity.
Usage Patterns
Server-side: Generate tokens for users
import { WonderfulSDK } from '@wonderful-sdk/contact-center';
const sdk = new WonderfulSDK({
baseUrl: 'https://api.wonderful.ai',
apiKey: process.env.WONDERFUL_API_KEY // Use API key for server-side
});
// When user authenticates, generate a short-lived SDK token
async function generateSDKToken(userId: string) {
const { jwt, expiresAt, id } = await sdk.auth.sign({
id: userId, // Use your app's user ID (must be UUID)
metadata: { role: 'customer' },
expiresIn: 3600 // 1 hour
});
return { token: jwt, expiresAt, id };
}
Client-side: Use the generated token
import { WonderfulSDK } from '@wonderful-sdk/contact-center';
const sdk = new WonderfulSDK({
baseUrl: 'https://api.wonderful.ai',
signedToken: tokenFromServer // JWT from server
});
const session = await sdk.chat.start({
from: 'user-123',
fromType: 'name'
});