Skip to main content

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:
  1. Frontend requests a token from your backend (authenticated via your app’s auth)
  2. Your backend calls sdk.auth.sign() with API key
  3. SDK calls Wonderful API to sign the JWT
  4. JWT returned to backend
  5. Backend returns JWT to frontend
  6. Frontend calls sdk.chat.start(), sdk.chat.send() with JWT token
  7. SDK makes chat API calls to Wonderful API

Authentication Methods

The SDK supports three authentication methods:
MethodConfig FieldUse CaseWebSocket Protocol
API KeyapiKeyServer-side only — JWT signing, backend integrationsapikey
Signed JWTsignedTokenFrontend apps — issued by your backend via sdk.auth.sign()jwt
Wonderful TokentokenCognito/SSO flowstoken
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

FieldConstraint
idValid UUID string, or omit for auto-generation
expiresInInteger between 60 and 86400 seconds (1 minute to 24 hours)
metadataJSON object, max 4KB when serialized

Error Codes

CodeDescription
VALIDATION_ERRORRequest validation failed (invalid expiresIn, metadata too large, etc.)
UNAUTHORIZEDInvalid or missing API key
NETWORK_ERRORConnection 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'
});