omnichannel-sdk
CLAUDE.md24 starsChanged 32 days agoMITView on GitHub
This workspace may contain up to 6 related repositories. Not all teams have all repos. Always be aware of which repository you're in when making changes. What is omnichannel-sdk (@microsoft/ocsdk)? This is the lowest-level HTTP client in the Omnichannel frontend stack. It provides typed wrappers around the Omnichannel livechatconnector REST APIs, handling request construction, retry logic, timeout management, authentication headers, and telemetry logging. It does not manage WebSocket connections, message rendering, or conversation adapters — those are handled by higher-level…
- Deletes or force-pushes
- Installs packages
The file
Show its rules and text
We split this CLAUDE.md into the rules it gives an agent
19 rules. About 0 are common, 12 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈12
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 5
This workspace may contain up to 6 related repositories: Not all teams have all repos.
- Distinctive: line 44
No WebSocket/real-time messaging (ACS handles this)
- Distinctive: line 45
No message rendering or UI (chat-widget handles this)
- Distinctive: line 46
No conversation adapter management (chat-sdk handles this)
- Distinctive: line 47
No file uploads (amsclient handles this)
- Distinctive: line 149
Every method follows the same structure:
- Very specific: line 351
Update CHANGELOG.md under [Unreleased] with all changes
- Distinctive: line 352
Use semantic versioning: major version for breaking changes
- Very specific: line 372
Use async/await patterns (not mixing done callbacks with promises)
- Distinctive: line 373
Test both success and error paths for every API method
- Very specific: line 374
Test timeout handling (verify ClientHTTPTimeoutError is thrown)
- Distinctive: line 375
Test auth vs anonymous endpoint selection
- Very specific: line 389
Test mocks must include headers property on response objects
- Very specific: line 411
Code standards: Follow TypeScript strict mode, avoid any types
- Distinctive: line 412
Testing: All Karma tests must pass, add tests for new API methods
- Very specific: line 413
Telemetry: Every API method must log start/succeed/fail events with timers
- Very specific: line 415
CHANGELOG: Update CHANGELOG.md under [Unreleased] section
- Distinctive: line 416
Auth parity: If adding a new endpoint, provide both authenticated and anonymous paths
- Distinctive: line 417
Retry configuration: Add per-endpoint timeout and wait-time-between-retries configs
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
omnichannel-sdk - Claude Code Instructions
Repository Ecosystem
This workspace may contain up to 6 related repositories. Not all teams have all repos. Always be aware of which repository you're in when making changes.
| Repository | Type | Purpose | Typical Location |
| CRM.Omnichannel | Monorepo (Backend) | 20+ microservices for Omnichannel platform | <workspace-root>/CRM.Omnichannel/ |
| ConversationControl | Frontend (Agent UI) | Agent experience and conversation management UI | <workspace-root>/CRM.OmniChannel.ConversationControl/ |
| LiveChatWidget | Frontend (Customer) | Customer-facing chat widget | <workspace-root>/CRM.OmniChannel.LiveChatWidget/ |
| omnichannel-chat-sdk | Public SDK | TypeScript SDK for chat integration | <workspace-root>/omnichannel-chat-sdk/ |
| omnichannel-chat-widget | Public Components | React component library | <workspace-root>/omnichannel-chat-widget/ |
| omnichannel-amsclient | Shared Library | File upload/download client | <workspace-root>/omnichannel-amsclient/ |
Quick Context
- Purpose: Low-level HTTP client for Omnichannel REST APIs (session lifecycle, chat config, tokens, transcripts)
- npm Name:
@microsoft/ocsdk - Version: 0.5.21
- Type: TypeScript Library
- Tech Stack: TypeScript 4.9, Axios + axios-retry, crypto-js, Webpack 5, Karma + Jasmine
- Distribution: npm registry (
@microsoft/ocsdk), UMD bundle (dist/SDK.min.js), CJS (lib/) - Consumers:
@microsoft/omnichannel-chat-sdk(primary consumer),CRM.OmniChannel.LiveChatWidget
Architecture Overview
What is omnichannel-sdk (@microsoft/ocsdk)?
This is the lowest-level HTTP client in the Omnichannel frontend stack. It provides typed wrappers around the Omnichannel livechatconnector REST APIs, handling request construction, retry logic, timeout management, authentication headers, and telemetry logging. It does not manage WebSocket connections, message rendering, or conversation adapters — those are handled by higher-level packages (chat-sdk, chat-widget).
Key Responsibilities:
- REST API calls to Omnichannel backend (
livechatconnector/*endpoints) - Automatic retry with exponential backoff via
axios-retry(retries on 5xx, network errors, optionally 429) - Per-endpoint configurable request timeouts
- Authentication header management (
AuthenticatedUserToken,AuthCodeNoncerotation) - Browser/device/OS detection for session context
- Data masking and PII sanitization in telemetry logs
- Cache key generation via SHA-256 hashing (crypto-js)
- UMD bundle for script-tag usage (
window.Microsoft.CRM.Omnichannel.SDK)
What This Package Does NOT Do:
- No WebSocket/real-time messaging (ACS handles this)
- No message rendering or UI (chat-widget handles this)
- No conversation adapter management (chat-sdk handles this)
- No file uploads (amsclient handles this)
Source Code Structure
omnichannel-sdk/
├── src/
│ ├── index.ts # Entry point, UMD global setup
│ ├── SDK.ts # Main SDK class (implements ISDK) — all API methods
│ ├── SDKProvider.ts # Factory: SDKProvider.getSDK() creates SDK instances
│ ├── Common/
│ │ ├── Constants.ts # Default headers, status codes, config params
│ │ ├── Enums.ts # ChannelId, LiveChatVersion, OCSDKTelemetryEvent, SDKError
│ │ ├── EventNames.ts # Custom DOM event names
│ │ ├── Locales.ts # Supported locale list
│ │ ├── Mappings.ts # StringMap type alias
│ │ ├── OCSDKLogger.ts # Logger wrapper
│ │ ├── OmnichannelEndpoints.ts # All REST endpoint path constants
│ │ ├── OmnichannelHTTPHeaders.ts # HTTP header name constants
│ │ └── RequestTimeoutConfig.ts # Per-endpoint timeout type
│ ├── Interfaces/
│ │ ├── ISDK.ts # SDK public interface (all API method signatures)
│ │ ├── IOmnichannelConfiguration.ts # { orgUrl, orgId, widgetId, channelId }
│ │ ├── ISDKConfiguration.ts # Retry counts, timeouts, nonce, ocUserAgent
│ │ ├── IAxiosRetryOptions.ts # Retry handler config
│ │ └── I*OptionalParams.ts # Per-method optional parameter interfaces
│ ├── Model/
│ │ ├── FetchChatTokenResponse.ts
│ │ ├── InitContext.ts # Session init payload shape
│ │ ├── ILogger.ts / LogLevel.ts
│ │ ├── QueueAvailability.ts / ReconnectAvailability.ts / ReconnectMappingRecord.ts
│ │ └── Location.ts
│ └── Utils/
│ ├── axiosRetryHandler.ts # Core retry logic (exponential backoff, 429 handling, header overwrites)
│ ├── BrowserInfo.ts / DeviceInfo.ts / OSInfo.ts # User agent detection
│ ├── endpointsCreators.ts # Dynamic endpoint path builders (version-aware)
│ ├── EventManager.ts # Custom DOM event dispatch
│ ├── httpHeadersUtils.ts # OC-User-Agent header builder
│ ├── LoggingSanitizer.ts # PII stripping for telemetry
│ ├── SessionInitRetryHandler.ts
│ ├── uuid.ts # UUID v4 generator
│ └── Timer.ts / sleep.ts / ...
├── test/
│ ├── index.ts # Test entry point
│ ├── SDK.spec.ts # Main SDK tests
│ ├── SDKProvider.spec.ts
│ ├── Common/OCSDKLogger.spec.ts
│ └── Utils/*.spec.ts # Utility tests
├── lib/ # TSC + Babel output (CJS, published to npm)
├── dist/ # Webpack output (UMD bundle)
├── karma.conf.js # Test runner config (Karma + Jasmine + ChromeHeadless)
├── webpack.config.js # Webpack entry (merges common + dev/prod)
├── webpack.common.js # Shared webpack config (entry, resolve, output)
├── webpack.prod.js # Production webpack config (minified SDK.min.js)
└── webpack.dev.js # Development webpack configKey Files
| File | Purpose |
src/SDK.ts | Core file — all 18+ API methods (getChatConfig, getChatToken, sessionInit, sessionClose, etc.) |
src/SDKProvider.ts | Factory pattern — SDKProvider.getSDK(config, sdkConfig, logger) |
src/Interfaces/ISDK.ts | Public contract — all method signatures |
src/Interfaces/IOmnichannelConfiguration.ts | Required config: { orgUrl, orgId, widgetId, channelId } |
src/Interfaces/ISDKConfiguration.ts | Optional config: retry counts, timeouts, nonce, ocUserAgent |
src/Common/OmnichannelEndpoints.ts | All REST API path constants (39 endpoints) |
src/Common/Enums.ts | ChannelId, LiveChatVersion (V1=IC3, V2=ACS, V3), OCSDKTelemetryEvent |
src/Utils/axiosRetryHandler.ts | Retry logic: exponential backoff, 429 handling, retry-after header |
SDK API Methods
The SDK class (src/SDK.ts) implements ISDK with these methods:
| Method | HTTP | Purpose |
getLcwFcsDetails() | GET | Fetch LCW FCS details for the org |
getChatConfig(requestId, bypassCache?) | GET | Fetch widget configuration (sets liveChatVersion) |
getLWIDetails(requestId, opts?) | GET | Get live work item details (anon or auth) |
getChatToken(requestId, opts?, retryCount?) | GET | Get chat token to join ACS thread (with retry) |
getReconnectableChats(params) | GET | Fetch reconnectable chats for authenticated user |
getReconnectAvailability(reconnectId, opts?) | GET | Check reconnect availability |
getAgentAvailability(requestId, opts?) | POST | Check agent/queue availability |
sessionInit(requestId, opts?) | POST | Start a chat session (sends browser/device context) |
createConversation(requestId, opts?) | POST | Create conversation via connector path |
sessionClose(requestId, opts?) | POST | Close a chat session |
validateAuthChatRecord(requestId, opts) | GET | Validate authenticated chat record exists |
submitPostChatResponse(requestId, response, opts?) | POST | Submit post-chat survey response |
getSurveyInviteLink(ownerId, body, opts?) | POST | Get survey invite link |
getChatTranscripts(requestId, chatId, token, opts?) | GET | Get chat transcripts (V1 and V2 paths) |
getPersistentChatHistory(requestId, opts?) | GET | Get persistent chat history (auth required) |
emailTranscript(requestId, token, body, opts?) | POST | Email transcript to customer |
fetchDataMaskingInfo(requestId) | GET | Fetch org data masking rules |
makeSecondaryChannelEventRequest(requestId, body, opts?) | POST | Send secondary channel event |
sendTypingIndicator(requestId, version, opts?) | POST | Send typing indicator (V2 only) |
Method Pattern
Every method follows the same structure:
public async methodName(requestId: string, optionalParams: IMethodOptionalParams = {}): Promise<ReturnType> {
// 1. Start timer + log telemetry start event
const timer = Timer.TIMER();
this.logWithLogger(LogLevel.INFO, OCSDKTelemetryEvent.METHOD_STARTED, "...", requestId);
// 2. Build request path (auth vs anon endpoint, version-aware)
let requestPath = authenticatedUserToken
? `/${OmnichannelEndpoints.AuthPath}/...`
: `/${OmnichannelEndpoints.AnonPath}/...`;
// 3. Create axios instance with retry handler
const axiosInstance = axios.create();
axiosRetryHandler(axiosInstance, { retries: ..., waitTimeInMsBetweenRetries: ... });
// 4. Build headers (Content-Type, auth token, nonce, session ID, user agent, correlation ID)
const requestHeaders: StringMap = { ...Constants.defaultHeaders };
if (authenticatedUserToken) {
requestHeaders[OmnichannelHTTPHeaders.authenticatedUserToken] = authenticatedUserToken;
requestHeaders[OmnichannelHTTPHeaders.authCodeNonce] = this.configuration.authCodeNonce;
}
this.addDefaultHeaders(requestId, requestHeaders);
// 5. Execute request with configurable timeout
try {
const response = await axiosInstance(options);
this.setAuthCodeNonce(response.headers); // Rotate nonce from response
this.logWithLogger(LogLevel.INFO, OCSDKTelemetryEvent.METHOD_SUCCEEDED, ...);
return response.data;
} catch (error) {
this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.METHOD_FAILED, ...);
if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) {
throw new Error(this.HTTPTimeOutErrorMessage);
}
throw error;
}
}Build & Test Workflow
Prerequisites
- Node.js (v12+ per README, but modern versions work)
- npm package manager
NODE_OPTIONS=--openssl-legacy-provideris required for the production build
Setup
cd omnichannel-sdk
npm installCommon Commands
Build (3-stage pipeline: TSC -> Babel -> Webpack):
- Full production build:
npm run build:prod— runs all 3 stages, outputslib/(CJS) anddist/SDK.min.js(UMD) - TypeScript only:
npm run build:tsc— outputs tolib/with declarations - Babel only:
npm run build:babel— transpilessrc/tolib/(ES5) - Webpack dev:
npm run build:dev— outputsdist/SDK.js(unminified UMD) - Watch mode:
npm run watch— Webpack in watch mode
Test:
- Unit tests:
npm test— Karma + Jasmine in headless Chrome - Lint:
npm run lint— ESLint - Format:
npm run format— Prettier
Build Pipeline Detail
Source (src/*.ts)
│
├─ TSC ──────────→ lib/*.js + lib/*.d.ts (CJS + declarations, published to npm)
│ target: ES5, strict: true
│
├─ Babel ────────→ lib/*.js (overwritten, ES5 with polyfills)
│ presets: @babel/env, @babel/typescript
│
└─ Webpack ──────→ dist/SDK.min.js (UMD bundle, libraryTarget: window)
entry: lib/index.js (prod) or src/index.ts (dev)Why 3 stages? TSC generates type declarations. Babel handles the actual transpilation to ES5 with runtime polyfills. Webpack bundles everything into a single UMD file for <script> tag usage.
Test Infrastructure
- Runner: Karma
- Framework: Jasmine
- Browser: ChromeHeadless (ChromeHeadlessCI with
--no-sandboxin CI) - Coverage: Istanbul via
@jsdevtools/coverage-istanbul-loader - Mocking:
axios-mock-adapterfor HTTP mocking - CI Reporter:
@dhigroup/karma-vsts-reporterfor Azure DevOps
Coding Standards
TypeScript Best Practices
- Strict mode enabled in
tsconfig.json - Avoid
anytype — use proper interfaces (existinganycasts are marked witheslint-disablecomments) - Explicit return types — all public methods have return types
- Async/await — preferred over
.then()chains - Use
constunless the pointer will mutate, thenlet
SDK-Specific Patterns
Always use the timer + telemetry pattern:
const timer = Timer.TIMER();
this.logWithLogger(LogLevel.INFO, OCSDKTelemetryEvent.STARTED, "Description", requestId);
try {
// ... operation
const elapsedTimeInMilliseconds = timer.milliSecondsElapsed;
this.logWithLogger(LogLevel.INFO, OCSDKTelemetryEvent.SUCCEEDED, "Description", requestId, response, elapsedTimeInMilliseconds, ...);
} catch (error) {
const elapsedTimeInMilliseconds = timer.milliSecondsElapsed;
this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.FAILED, "Description", requestId, undefined, elapsedTimeInMilliseconds, ...);
}Always check for timeout errors and wrap them:
if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) {
reject(new Error(this.HTTPTimeOutErrorMessage));
}Always rotate AuthCodeNonce from response headers:
const { headers } = response;
this.setAuthCodeNonce(headers); // Updates this.configuration.authCodeNonceAlways add default headers:
this.addDefaultHeaders(requestId, requestHeaders);
// Adds: Oc-Sessionid, Ms-Oc-User-Agent, correlation IDAuth vs Anonymous endpoints: Most methods have two endpoint paths — anonymous and authenticated. Switch based on the presence of authenticatedUserToken:
const basePath = authenticatedUserToken
? OmnichannelEndpoints.LiveChatAuthSessionInitPath
: OmnichannelEndpoints.LiveChatSessionInitPath;Reconnect ID handling: There are two modes for passing reconnect IDs — as a URL path segment or as a sig query parameter, controlled by useUnauthReconnectIdSigQueryParam:
const shouldUseSigQueryParam = !authenticatedUserToken && this.configuration.useUnauthReconnectIdSigQueryParam;
if (reconnectId) {
if (shouldUseSigQueryParam) {
params.sig = reconnectId;
} else {
requestPath += `/${reconnectId}`;
}
}Retry & Timeout Configuration
Default Retry Config
- Max retries on failure: 5 (
maxRequestRetriesOnFailure) - getChatToken retry count: 10 (
getChatTokenRetryCount) - getChatToken retry interval: 10,000ms (
getChatTokenTimeBetweenRetriesOnFailure) - Retry on 429: true for getChatToken (
getChatTokenRetryOn429), getChatConfig (getChatConfigRetryOn429), sessionInit, and createConversation; false by default for others - Retry strategy: Exponential backoff (
2^retryCount * waitTime), respectsretry-afterheader (seconds or HTTP-date), 5s fallback on 429; all delays add up to 1s of positive jitter - Retryable conditions: 5xx status, network errors, no response, optionally 429
Default Timeouts (per endpoint)
| Endpoint | Timeout |
| getChatConfig | 120,000ms |
| getChatTranscripts | 30,000ms |
| getChatToken, sessionInit, sessionClose, getLWIDetails, getReconnectableChats, getReconnectAvailability, submitPostChatResponse, getSurveyInviteLink, getAgentAvailability, validateAuthChatRecord, getPersistentChatHistory | 15,000ms |
| emailTranscript, fetchDataMaskingInfo, sendTypingIndicator | 5,000ms |
Integration with Other Repos
This SDK integrates with:
- CRM.Omnichannel (Backend): All
livechatconnector/*REST APIs in the Omnichannel gateway
Consumed by:
- omnichannel-chat-sdk (
@microsoft/omnichannel-chat-sdk) — Primary consumer. Wraps ocsdk methods and adds conversation lifecycle management, adapter handling, and WebSocket coordination. - CRM.OmniChannel.LiveChatWidget — Indirect consumer via chat-sdk
Dependency direction:
LiveChatWidget → chat-widget → chat-sdk → ocsdk (this package)
→ amsclientWhen changing public APIs:
- This is consumed by
omnichannel-chat-sdk— breaking changes require coordinated releases - The
ISDKinterface (src/Interfaces/ISDK.ts) is the public contract - Update
CHANGELOG.mdunder[Unreleased]with all changes - Use semantic versioning: major version for breaking changes
Testing Strategy
Unit Tests (Karma + Jasmine):
- Location:
test/directory - Run:
npm test - Mocking:
axios-mock-adapterfor HTTP call mocking
Test files:
test/SDK.spec.ts— Tests for all SDK API methodstest/SDKProvider.spec.ts— Factory teststest/Common/OCSDKLogger.spec.ts— Logger teststest/Utils/*.spec.ts— Utility function tests
Test Best Practices:
- Mock axios responses using
axios-mock-adapter - Include
headersin all response mocks (required since Webpack 5 migration) - Use async/await patterns (not mixing
donecallbacks with promises) - Test both success and error paths for every API method
- Test timeout handling (verify
ClientHTTPTimeoutErroris thrown) - Test auth vs anonymous endpoint selection
Common Issues & Troubleshooting
Build Issues:
NODE_OPTIONS=--openssl-legacy-providerrequired for production build — the build script includescross-envto set this- 3-stage build is slow — TSC, then Babel, then Webpack. For development, use
npm run build:dev(Webpack only from source) - Clear node_modules:
rm -rf node_modules && npm install
Test Issues:
- Tests run in ChromeHeadless via Karma — ensure Chrome/Chromium is installed
- In CI, use
ChromeHeadlessCIlauncher (setCI=trueenv var) for--no-sandboxflags - Test mocks must include
headersproperty on response objects
Authentication Issues:
vsts-npm-auth -config .npmrcif internal dependency resolution fails
Runtime Issues:
process is not definedin browser — Webpack 5 removed Node.js polyfills;webpack.common.jsusesProvidePluginto polyfillprocess- AuthCodeNonce rotation — if requests fail with auth errors, verify the nonce is being properly rotated from response headers via
setAuthCodeNonce()
Documentation
- README.md — SDK usage examples, initialization, API calls
- CHANGELOG.md — Release history from v0.1.0 to v0.5.21
- SECURITY.md — Security policies
- CODE_OF_CONDUCT.md — Microsoft Open Source Code of Conduct
Pull Request Guidelines
- Code standards: Follow TypeScript strict mode, avoid
anytypes - Testing: All Karma tests must pass, add tests for new API methods
- Telemetry: Every API method must log start/succeed/fail events with timers
- Error handling: Wrap timeout errors as
ClientHTTPTimeoutError, propagate others - CHANGELOG: Update
CHANGELOG.mdunder[Unreleased]section - Auth parity: If adding a new endpoint, provide both authenticated and anonymous paths
- Retry configuration: Add per-endpoint timeout and wait-time-between-retries configs
Summary: This is the lowest-level HTTP client (@microsoft/ocsdk) in the Omnichannel frontend stack. It wraps livechatconnector REST APIs with axios + retry logic, per-endpoint timeouts, auth header management, and telemetry logging. Uses a 3-stage build (TSC -> Babel -> Webpack). Consumed primarily by @microsoft/omnichannel-chat-sdk. Always follow the timer + telemetry + retry pattern for new methods.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

