diff --git a/README.md b/README.md index 498dcf1..8d6a02b 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,18 @@ To use Zoom Rivet effectively, you should understand three important concepts: a Zoom Rivet handles authentication for developers. All you have to do is provide your app's `ClientId` and `ClientSecret`. See the matrix in the table below to better how authentication works in each Rivet module: -| Module | Auth Type | -| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -| Chatbot | [Client Credentials](https://developers.zoom.us/docs/team-chat-apps/installation-and-authentication/#authentication) | -| Video SDK | [JWT](https://developers.zoom.us/docs/video-sdk/api-request/) | -| Team Chat, Meetings, Phone, Accounts, Users | [User OAuth](https://developers.zoom.us/docs/integrations/), [Server OAuth](https://developers.zoom.us/docs/internal-apps/) | +| Supported Module | Auth Type | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| Accounts | [User OAuth](<(https://developers.zoom.us/docs/integrations/)>)
[Server OAuth](https://developers.zoom.us/docs/internal-apps/) | +| Chatbot | [Client Credentials](https://developers.zoom.us/docs/team-chat-apps/installation-and-authentication/#authentication) | +| Commerce | [Server OAuth](https://developers.zoom.us/docs/internal-apps/) | +| Marketplace | [User OAuth](<(https://developers.zoom.us/docs/integrations/)>)
[Server OAuth](https://developers.zoom.us/docs/internal-apps/) | +| Meetings | [User OAuth](<(https://developers.zoom.us/docs/integrations/)>)
[Server OAuth](https://developers.zoom.us/docs/internal-apps/) | +| Meetings | [User OAuth](<(https://developers.zoom.us/docs/integrations/)>)
[Server OAuth](https://developers.zoom.us/docs/internal-apps/) | +| Phone | [User OAuth](<(https://developers.zoom.us/docs/integrations/)>)
[Server OAuth](https://developers.zoom.us/docs/internal-apps/) | +| Team Chat | [User OAuth](<(https://developers.zoom.us/docs/integrations/)>)
[Server OAuth](https://developers.zoom.us/docs/internal-apps/) | +| Users | [User OAuth](<(https://developers.zoom.us/docs/integrations/)>)
[Server OAuth](https://developers.zoom.us/docs/internal-apps/) | +| Video SDK | [JSON Web Token (JWT)](https://developers.zoom.us/docs/video-sdk/api-request/) | ### Listening to Events diff --git a/accounts/accounts.cjs b/accounts/accounts.cjs index a30f182..8b7cd76 100644 --- a/accounts/accounts.cjs +++ b/accounts/accounts.cjs @@ -426,6 +426,9 @@ class JwtStateStore { const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; const hasInstallerOptions = (obj) => typeof obj.installerOptions.redirectUri !== "undefined" && typeof obj.installerOptions.stateStore !== "undefined"; @@ -452,7 +455,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -462,14 +468,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -581,9 +593,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -597,6 +606,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? node_https.createServer : node_http.createServer; } @@ -611,8 +633,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -629,69 +663,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, exports.StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, exports.StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -725,18 +774,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(exports.StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -745,87 +800,13 @@ class HttpReceiver { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -854,10 +835,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${node_path.basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -1003,6 +997,12 @@ class AccountsEndpoints extends WebEndpoints { urlPathBuilder: ({ roleId, memberId }) => `/roles/${roleId}/members/${memberId}` }) }; + surveyManagement = { + getSurveys: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/surveys` }), + getSurveyInfo: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ surveyId }) => `/surveys/${surveyId}` }), + getSurveyAnswers: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ surveyId }) => `/surveys/${surveyId}/answers` }), + getSurveyInstances: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ surveyId }) => `/surveys/${surveyId}/instances` }) + }; } class AccountsEventProcessor extends EventManager { @@ -1092,7 +1092,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -1113,7 +1115,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. diff --git a/accounts/accounts.d.ts b/accounts/accounts.d.ts index a8f146c..6a153f0 100644 --- a/accounts/accounts.d.ts +++ b/accounts/accounts.d.ts @@ -1,88 +1,8 @@ -import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { AxiosResponse } from 'axios'; +import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { Server } from 'node:http'; import { ServerOptions } from 'node:https'; -type AllPropsOptional = Exclude<{ - [P in keyof T]: undefined extends T[P] ? True : False; -}[keyof T], undefined> extends True ? True : False; -type Constructor = new (...args: any[]) => T; -type MaybeArray = T | T[]; -type MaybePromise = T | Promise; -type StringIndexed = Record; - -/** - * {@link StateStore} defines methods for generating and verifying OAuth state. - * - * This interface is implemented internally for the default state store; however, - * it can also be implemented and passed to an OAuth client as well. - */ -interface StateStore { - /** - * Generate a new state string, which is directly appended to the OAuth `state` parameter. - */ - generateState(): MaybePromise; - /** - * Verify that the state received during OAuth callback is valid and not forged. - * - * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. - * - * @param state The state parameter that was received during OAuth callback - */ - verifyState(state: string): MaybePromise; -} -/** - * Guard if an object implements the {@link StateStore} interface — most notably, - * `generateState()` and `verifyState(state: string)`. - */ -declare const isStateStore: (obj: unknown) => obj is StateStore; - -interface TokenStore { - getLatestToken(): MaybePromise; - storeToken(token: Token): MaybePromise; -} - -interface RivetError extends Error { - readonly errorCode: ErrorCode; -} - -declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ - readonly ApiResponseError: "zoom_rivet_api_response_error"; - readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; - readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; - readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; - readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; - readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; - readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; - readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; - readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; - readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; - readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; - readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; - readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; - readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; - readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; - readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; - readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; -}[K]>; -declare const ApiResponseError: Constructor; -declare const AwsReceiverRequestError: Constructor; -declare const ClientCredentialsRawResponseError: Constructor; -declare const S2SRawResponseError: Constructor; -declare const CommonHttpRequestError: Constructor; -declare const ReceiverInconsistentStateError: Constructor; -declare const ReceiverOAuthFlowError: Constructor; -declare const HTTPReceiverConstructionError: Constructor; -declare const HTTPReceiverPortNotNumberError: Constructor; -declare const HTTPReceiverRequestError: Constructor; -declare const OAuthInstallerNotInitializedError: Constructor; -declare const OAuthTokenDoesNotExistError: Constructor; -declare const OAuthTokenFetchFailedError: Constructor; -declare const OAuthTokenRawResponseError: Constructor; -declare const OAuthTokenRefreshFailedError: Constructor; -declare const OAuthStateVerificationFailedError: Constructor; -declare const ProductClientConstructionError: Constructor; - declare enum LogLevel { ERROR = "error", WARN = "warn", @@ -142,6 +62,19 @@ declare class ConsoleLogger implements Logger { private static isMoreOrEqualSevere; } +type AllPropsOptional = Exclude<{ + [P in keyof T]: undefined extends T[P] ? True : False; +}[keyof T], undefined> extends True ? True : False; +type Constructor = new (...args: any[]) => T; +type MaybeArray = T | T[]; +type MaybePromise = T | Promise; +type StringIndexed = Record; + +interface TokenStore { + getLatestToken(): MaybePromise; + storeToken(token: Token): MaybePromise; +} + interface AuthOptions { clientId: string; clientSecret: string; @@ -187,6 +120,17 @@ declare abstract class Auth { }>, "grant_type">): Promise; } +interface ClientCredentialsToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} + +interface JwtToken { + token: string; + expirationTimeIso: string; +} + interface S2SAuthToken { accessToken: string; expirationTimeIso: string; @@ -225,12 +169,31 @@ declare class EventManager { protected withContext, Context>(): ContextListener; } +declare enum StatusCode { + OK = 200, + TEMPORARY_REDIRECT = 302, + BAD_REQUEST = 400, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + INTERNAL_SERVER_ERROR = 500 +} +interface ReceiverInitOptions { + eventEmitter?: GenericEventManager | undefined; + interactiveAuth?: InteractiveAuth | undefined; +} +interface Receiver { + canInstall(): true | false; + init(options: ReceiverInitOptions): void; + start(...args: any[]): MaybePromise; + stop(...args: any[]): MaybePromise; +} + interface HttpReceiverOptions extends Partial { endpoints?: MaybeArray | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; port?: number | string | undefined; - webhooksSecretToken: string; - logger?: Logger; - logLevel?: LogLevel; + webhooksSecretToken?: string | undefined; } type SecureServerOptions = { [K in (typeof secureServerOptionKeys)[number]]: ServerOptions[K]; @@ -243,10 +206,15 @@ declare class HttpReceiver implements Receiver { private logger; constructor(options: HttpReceiverOptions); canInstall(): true; + private buildDeletedStateCookieHeader; + private buildStateCookieHeader; + private getRequestCookie; private getServerCreator; private hasEndpoint; private hasSecureOptions; init({ eventEmitter, interactiveAuth }: ReceiverInitOptions): void; + private setResponseCookie; + private areNormalizedUrlsEqual; start(port?: number | string): Promise; stop(): Promise; private writeTemporaryRedirect; @@ -269,6 +237,7 @@ interface WebEndpointOptions { baseUrl?: string | undefined; doubleEncodeUrl?: boolean | undefined; timeout?: number | undefined; + userAgentName?: string | undefined; } type EndpointArguments = (PathSchema extends NoParams ? object : AllPropsOptional extends "t" ? { path?: PathSchema; @@ -290,6 +259,7 @@ declare class WebEndpoints { constructor(options: WebEndpointOptions); protected buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }: BuildEndpointOptions): (_: EndpointArguments) => Promise>; private buildUserAgent; + private getCustomUserAgentName; private getHeaders; private getRequestBody; private isOk; @@ -297,7 +267,7 @@ declare class WebEndpoints { private makeRequest; } -type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & { +type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & Pick & { disableReceiver?: boolean | undefined; logger?: Logger | undefined; logLevel?: LogLevel | undefined; @@ -305,38 +275,68 @@ type CommonClientOptions = GetAuthOptions interface ClientReceiverOptions { receiver: R; } -type ClientConstructorOptions, R extends Receiver> = IsReceiverDisabled extends true ? O : O & (ClientReceiverOptions | HttpReceiverOptions); +type ClientConstructorOptions, R extends Receiver> = (O & { + disableReceiver: true; +}) | (O & (ClientReceiverOptions | HttpReceiverOptions)); type ExtractInstallerOptions = A extends InteractiveAuth ? [ ReturnType ] extends [true] ? WideInstallerOptions : object : object; type ExtractAuthTokenType = A extends Auth ? T : never; -type GenericClientOptions = CommonClientOptions; type GetAuthOptions = AuthOptions> & (A extends S2SAuth ? S2SAuthOptions : object); -type IsReceiverDisabled> = [ - O["disableReceiver"] -] extends [true] ? true : false; type WideInstallerOptions = { installerOptions: InstallerOptions; }; declare abstract class ProductClient, ReceiverType extends Receiver> { private readonly auth; readonly endpoints: EndpointsType; - readonly webEventConsumer: EventProcessorType; + readonly webEventConsumer?: EventProcessorType | undefined; private readonly receiver?; constructor(options: ClientConstructorOptions); protected abstract initAuth(options: OptionsType): AuthType; protected abstract initEndpoints(auth: AuthType, options: OptionsType): EndpointsType; - protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType; + protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType | undefined; private initDefaultReceiver; - start(this: IsReceiverDisabled extends true ? never : this): Promise>; + start(): Promise>; +} + +/** + * {@link StateStore} defines methods for generating and verifying OAuth state. + * + * This interface is implemented internally for the default state store; however, + * it can also be implemented and passed to an OAuth client as well. + */ +interface StateStore { + /** + * Generate a new state string, which is directly appended to the OAuth `state` parameter. + */ + generateState(): MaybePromise; + /** + * Verify that the state received during OAuth callback is valid and not forged. + * + * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. + * + * @param state The state parameter that was received during OAuth callback + */ + verifyState(state: string): MaybePromise; } +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +declare const isStateStore: (obj: unknown) => obj is StateStore; +interface AuthorizationUrlResult { + fullUrl: string; + generatedState: string; +} interface InstallerOptions { directInstall?: boolean | undefined; installPath?: string | undefined; redirectUri: string; redirectUriPath?: string | undefined; stateStore: StateStore | string; + stateCookieName?: string | undefined; + stateCookieMaxAge?: number | undefined; } /** * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication @@ -350,36 +350,87 @@ interface InstallerOptions { */ declare abstract class InteractiveAuth extends Auth { installerOptions?: ReturnType; - getAuthorizationUrl(): Promise; + getAuthorizationUrl(): Promise; getFullRedirectUri(): string; - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }: InstallerOptions): { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }: InstallerOptions): { directInstall: boolean; installPath: string; redirectUri: string; redirectUriPath: string; stateStore: StateStore; + stateCookieName: string; + stateCookieMaxAge: number; }; } -declare enum StatusCode { - OK = 200, - TEMPORARY_REDIRECT = 302, - BAD_REQUEST = 400, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - INTERNAL_SERVER_ERROR = 500 +/** + * Credentials for access token & refresh token, which are used to access Zoom's APIs. + * + * As access token is short-lived (usually a single hour), its expiration time is checked + * first. If it's possible to use the access token, it's used; however, if it has expired + * or is close to expiring, the refresh token should be used to generate a new access token + * before the API call is made. Refresh tokens are generally valid for 90 days. + * + * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} + * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. + * It's likely that this error will be rare, but it _can_ be thrown. + */ +interface OAuthToken { + accessToken: string; + expirationTimeIso: string; + refreshToken: string; + scopes: string[]; } -interface ReceiverInitOptions { - eventEmitter: GenericEventManager; - interactiveAuth?: InteractiveAuth | undefined; +declare class OAuth extends InteractiveAuth { + private assertResponseAccessToken; + private fetchAccessToken; + getToken(): Promise; + initRedirectCode(code: string): Promise; + private mapOAuthToken; + private refreshAccessToken; } -interface Receiver { - canInstall(): true | false; - init(options: ReceiverInitOptions): void; - start(...args: any[]): MaybePromise; - stop(...args: any[]): MaybePromise; + +interface RivetError extends Error { + readonly errorCode: ErrorCode; } +declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ + readonly ApiResponseError: "zoom_rivet_api_response_error"; + readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; + readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; + readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; + readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; + readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; + readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; + readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; + readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; + readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; + readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; + readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; + readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; + readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; + readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; + readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; + readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; +}[K]>; +declare const ApiResponseError: Constructor; +declare const AwsReceiverRequestError: Constructor; +declare const ClientCredentialsRawResponseError: Constructor; +declare const S2SRawResponseError: Constructor; +declare const CommonHttpRequestError: Constructor; +declare const ReceiverInconsistentStateError: Constructor; +declare const ReceiverOAuthFlowError: Constructor; +declare const HTTPReceiverConstructionError: Constructor; +declare const HTTPReceiverPortNotNumberError: Constructor; +declare const HTTPReceiverRequestError: Constructor; +declare const OAuthInstallerNotInitializedError: Constructor; +declare const OAuthTokenDoesNotExistError: Constructor; +declare const OAuthTokenFetchFailedError: Constructor; +declare const OAuthTokenRawResponseError: Constructor; +declare const OAuthTokenRefreshFailedError: Constructor; +declare const OAuthStateVerificationFailedError: Constructor; +declare const ProductClientConstructionError: Constructor; + interface AwsLambdaReceiverOptions { webhooksSecretToken: string; } @@ -421,6 +472,24 @@ type AccountsGetLockedSettingsResponse = { send_data_to_third_party_archiving_service?: boolean; translate_messages?: boolean; search_and_send_animated_gif_images?: boolean; + shared_spaces?: boolean; + allow_create_channels_and_group_chats?: boolean; + allow_huddles_from_channels?: boolean; + download_file?: boolean; + share_screen_in_chat?: boolean; + chat_email_address?: boolean; + read_receipts?: boolean; + allow_delete_message?: boolean; + allow_edit_message?: boolean; + presence_on_meeting?: boolean; + presence_away_when_screen_saver?: boolean; + ai_summary?: boolean; + ai_compose?: boolean; + ai_recommend?: boolean; + ai_reply?: boolean; + ai_sentence_completion?: boolean; + ai_quick_schedule?: boolean; + survey_poll?: boolean; }; email_notification?: { alternative_host_reminder?: boolean; @@ -572,6 +641,24 @@ type AccountsUpdateLockedSettingsRequestBody = { send_data_to_third_party_archiving_service?: boolean; translate_messages?: boolean; search_and_send_animated_gif_images?: boolean; + shared_spaces?: boolean; + allow_create_channels_and_group_chats?: boolean; + allow_huddles_from_channels?: boolean; + download_file?: boolean; + share_screen_in_chat?: boolean; + chat_email_address?: boolean; + read_receipts?: boolean; + allow_delete_message?: boolean; + allow_edit_message?: boolean; + presence_on_meeting?: boolean; + presence_away_when_screen_saver?: boolean; + ai_summary?: boolean; + ai_compose?: boolean; + ai_recommend?: boolean; + ai_reply?: boolean; + ai_sentence_completion?: boolean; + ai_quick_schedule?: boolean; + survey_poll?: boolean; }; email_notification?: { alternative_host_reminder?: boolean; @@ -689,7 +776,7 @@ type AccountsUpdateLockedSettingsRequestBody = { block_user_domain?: boolean; chat_etiquette_tool?: boolean; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; only_authenticated_can_join_from_webclient?: boolean; @@ -719,14 +806,14 @@ type AccountsGetAccountSettingsPathParams = { accountId: string; }; type AccountsGetAccountSettingsQueryParams = { - option?: string; + option?: "meeting_authentication" | "recording_authentication" | "security" | "meeting_security"; custom_query_fields?: string; }; type AccountsGetAccountSettingsResponse = { security?: { admin_change_name_pic?: boolean; admin_change_user_info?: boolean; - user_modifiable_info_by_admin?: string[]; + user_modifiable_info_by_admin?: ("name" | "profile_picture" | "sign_in_email" | "host_key")[]; signin_with_sso?: { enable?: boolean; require_sso_for_domains?: boolean; @@ -746,7 +833,7 @@ type AccountsGetAccountSettingsResponse = { }; sign_again_period_for_inactivity_on_client?: number; sign_again_period_for_inactivity_on_web?: number; - sign_in_with_two_factor_auth?: string; + sign_in_with_two_factor_auth?: "all" | "group" | "role" | "none"; sign_in_with_two_factor_auth_groups?: string[]; sign_in_with_two_factor_auth_roles?: string[]; }; @@ -767,25 +854,28 @@ type AccountsGetAccountSettingsResponse = { allow_bots_chat?: boolean; share_files?: { enable?: boolean; - share_option?: string; + share_option?: "disable" | "anyone" | "account" | "organization"; + view_option?: "anyone" | "account" | "organization"; restrictions?: { only_allow_specific_file_types?: boolean; - file_type_restrictions?: string[]; - file_type_restrictions_for_external?: string[]; + file_type_restrictions?: (".gz" | ".rar" | ".zip" | ".xls" | ".xlsx" | ".json" | ".png" | ".pptx" | ".ppt" | ".7z" | ".xmind" | ".pdf" | ".pps" | ".txt" | ".docx" | ".doc")[]; + file_type_restrictions_for_external?: (".gz" | ".rar" | ".zip" | ".xls" | ".xlsx" | ".json" | ".png" | ".pptx" | ".ppt" | ".7z" | ".xmind" | ".pdf" | ".pps" | ".txt" | ".docx" | ".doc")[]; maximum_file_size?: boolean; - file_size_restrictions?: number; - file_size_restrictions_for_external?: number; + file_size_restrictions?: 50 | 100 | 200 | 300 | 400 | 500; + file_size_restrictions_for_external?: 50 | 100 | 200 | 300 | 400 | 500; + file_restrictions_apply_to?: "sharing_and_viewing" | "sharing"; }; }; chat_emojis?: { enable?: boolean; - emojis_option?: string; + emojis_option?: "all" | "selected"; }; record_voice_messages?: boolean; record_video_messages?: boolean; screen_capture?: boolean; create_public_channels?: boolean; create_private_channels?: boolean; + create_group_chat?: boolean; share_links_in_chat?: boolean; schedule_meetings_in_chat?: boolean; set_retention_period_in_cloud?: { @@ -800,12 +890,12 @@ type AccountsGetAccountSettingsResponse = { }; allow_users_to_add_contacts?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; allow_users_to_chat_with_others?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; chat_etiquette_tool?: { @@ -817,14 +907,14 @@ type AccountsGetAccountSettingsResponse = { keywords?: string[]; name?: string; regular_expression?: string; - status?: string; - trigger_action?: number; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; }[]; policy_max_count?: number; }; send_data_to_third_party_archiving_service?: { enable?: boolean; - type?: string; + type?: "global_relay" | "smarsh"; smtp_delivery_address?: string; user_name?: string; passcode?: string; @@ -837,16 +927,21 @@ type AccountsGetAccountSettingsResponse = { translate_messages?: boolean; search_and_send_animated_gif_images?: { enable?: boolean; - giphy_content_rating?: number; + giphy_content_rating?: 1 | 2 | 3 | 4; + }; + external_collab_restrict?: { + enable?: boolean; + external_chat?: "allowed" | "not_allowed"; + group_id?: string; }; external_user_control?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3; external_account?: boolean; }; external_invite_approve?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2; channel_id?: string; external_account?: boolean; }; @@ -856,10 +951,56 @@ type AccountsGetAccountSettingsResponse = { }; external_join_approve?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2; channel_id?: string; external_account?: boolean; }; + download_file?: boolean; + share_screen_in_chat?: boolean; + code_snippet?: boolean; + personal_channel?: boolean; + store_revise_chat?: boolean; + set_chat_as_default_tab?: boolean; + hyper_link?: boolean; + suppress_removal_notification?: boolean; + suppress_user_group_notification?: boolean; + allow_remove_msg_by_owner_and_admins?: boolean; + allow_huddles_from_channels?: boolean; + shared_spaces?: boolean; + chat_email_address?: { + enable?: boolean; + only_allow_specific_domains?: boolean; + specific_domains?: string[]; + }; + read_receipts?: { + enable?: boolean; + allow_users_opt_out?: boolean; + }; + allow_delete_message?: { + enable?: boolean; + time?: 0 | 5 | 30 | 60 | 1440 | 10080; + }; + allow_edit_message?: { + enable?: boolean; + time?: 0 | 5 | 30 | 60 | 1440 | 10080; + }; + show_status_to_internal_contact?: boolean; + presence_on_meeting?: boolean; + presence_away_when_screen_saver?: boolean; + show_h323_contact_tab?: boolean; + ai_summary?: { + enable?: boolean; + shown_in_team_chat?: boolean; + }; + ai_compose?: { + enable?: boolean; + shown_in_team_chat?: boolean; + }; + ai_recommend?: boolean; + ai_quick_reply?: boolean; + ai_sentence_completion?: boolean; + ai_quick_schedule?: boolean; + survey_poll?: boolean; }; email_notification?: { alternative_host_reminder?: boolean; @@ -879,14 +1020,14 @@ type AccountsGetAccountSettingsResponse = { language_item_pairList?: { trans_lang_config?: { speak_language?: { - name?: string; - code?: string; + name?: "Chinese (Simplified)" | "Dutch" | "English" | "French" | "German" | "Italian" | "Japanese" | "Korean" | "Portuguese" | "Russian" | "Spanish" | "Ukrainian"; + code?: "zh" | "nl" | "en" | "fr" | "de" | "it" | "ja" | "ko" | "pt" | "ru" | "es" | "uk"; }; translate_to?: { all?: boolean; language_config?: { - name?: string; - code?: string; + name?: "English"; + code?: "en"; }[]; }; }[]; @@ -897,10 +1038,10 @@ type AccountsGetAccountSettingsResponse = { alert_guest_join?: boolean; allow_host_to_enable_focus_mode?: boolean; allow_live_streaming?: boolean; - allow_participants_chat_with?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; allow_participants_to_rename?: boolean; allow_show_zoom_windows?: boolean; - allow_users_save_chats?: number; + allow_users_save_chats?: 1 | 2 | 3; annotation?: boolean; anonymous_question_answer?: boolean; attendee_on_hold?: boolean; @@ -923,8 +1064,8 @@ type AccountsGetAccountSettingsResponse = { custom_data_center_regions?: boolean; custom_live_streaming_service?: boolean; custom_service_instructions?: string; - meeting_data_transit_and_residency_method?: string; - data_center_regions?: string[]; + meeting_data_transit_and_residency_method?: "cloud" | "On-Prem"; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; dscp_audio?: number; @@ -932,7 +1073,7 @@ type AccountsGetAccountSettingsResponse = { dscp_video?: number; dscp_dual?: boolean; e2e_encryption?: boolean; - entry_exit_chime?: string; + entry_exit_chime?: "host" | "all" | "none"; far_end_camera_control?: boolean; feedback?: boolean; file_transfer?: boolean; @@ -946,12 +1087,12 @@ type AccountsGetAccountSettingsResponse = { enable_language_interpretation_by_default?: boolean; allow_participants_to_speak_in_listening_channel?: boolean; allow_up_to_25_custom_languages_when_scheduling_meetings?: boolean; - languages?: string[]; + languages?: ("English" | "Chinese" | "Japanese" | "German" | "French" | "Russian" | "Portuguese" | "Spanish" | "Korean")[]; }; sign_language_interpretation?: { enable?: boolean; enable_sign_language_interpretation_by_default?: boolean; - languages?: string[]; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; custom_languages?: string[]; }; live_streaming_facebook?: boolean; @@ -973,7 +1114,7 @@ type AccountsGetAccountSettingsResponse = { enable?: boolean; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; meeting_survey?: boolean; @@ -995,7 +1136,7 @@ type AccountsGetAccountSettingsResponse = { show_meeting_control_toolbar?: boolean; slide_control?: boolean; stereo_audio?: boolean; - unchecked_data_center_regions?: string[]; + unchecked_data_center_regions?: ("EU" | "HK" | "AU" | "IN" | "TY" | "CN" | "US" | "CA" | "DE" | "NL" | "LA")[]; use_html_format_email?: boolean; virtual_background?: boolean; virtual_background_settings?: { @@ -1012,20 +1153,20 @@ type AccountsGetAccountSettingsResponse = { }; watermark?: boolean; webinar_chat?: { - allow_attendees_chat_with?: number; + allow_attendees_chat_with?: 1 | 2 | 3; allow_auto_save_local_chat_file?: boolean; - allow_panelists_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; + allow_users_save_chats?: 0 | 1 | 2; allow_users_to_delete_messages_in_meeting_chat?: boolean; - default_attendees_chat_with?: number; + default_attendees_chat_with?: 1 | 2; enable?: boolean; }; webinar_live_streaming?: { custom_service_instructions?: string; enable?: boolean; live_streaming_reminder?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; }; webinar_polling?: { advanced_polls?: boolean; @@ -1038,11 +1179,21 @@ type AccountsGetAccountSettingsResponse = { webinar_question_answer?: boolean; webinar_survey?: boolean; whiteboard?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; workplace_by_facebook?: boolean; transfer_meetings_between_devices?: boolean; + meeting_summary_with_ai_companion?: { + enable?: boolean; + auto_enable?: boolean; + who_will_receive_summary?: 1 | 2 | 3 | 4; + }; + ai_companion_questions?: { + enable?: boolean; + auto_enable?: boolean; + who_can_ask_questions?: 1 | 2 | 3 | 4 | 5; + }; }; integration?: { box?: boolean; @@ -1058,9 +1209,9 @@ type AccountsGetAccountSettingsResponse = { allow_users_enter_and_share_pronouns?: boolean; blur_snapshot?: boolean; display_meetings_scheduled_for_others?: boolean; - meeting_qos_and_mos?: number; + meeting_qos_and_mos?: 0 | 1 | 2 | 3; show_one_user_meeting_on_dashboard?: boolean; - use_cdn?: string; + use_cdn?: "none" | "default" | "wangsu"; webinar_registration_options?: { allow_host_to_enable_join_info?: boolean; allow_host_to_enable_social_share_buttons?: boolean; @@ -1086,16 +1237,16 @@ type AccountsGetAccountSettingsResponse = { chat_with_sender_email?: boolean; video_file?: boolean; chat_with_direct_message?: boolean; - archive_retention?: number; - action_when_archive_failed?: number; - notification_when_archiving_starts?: string; - play_voice_prompt_when_archiving_starts?: string; + archive_retention?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30; + action_when_archive_failed?: 1 | 2; + notification_when_archiving_starts?: "participants" | "guest"; + play_voice_prompt_when_archiving_starts?: "participants" | "guest" | "none"; }; - type?: number; + type?: 1 | 2 | 3; }; auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; - auto_recording?: string; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; + auto_recording?: "local" | "cloud" | "none"; cloud_recording?: boolean; cloud_recording_download?: boolean; cloud_recording_download_host?: boolean; @@ -1142,17 +1293,17 @@ type AccountsGetAccountSettingsResponse = { show_timestamp?: boolean; }; schedule_meeting?: { - audio_type?: string; + audio_type?: "both" | "telephony" | "voip" | "thirdParty"; enforce_login?: boolean; enforce_login_domains?: string; enforce_login_with_domains?: boolean; force_pmi_jbh_password?: boolean; host_video?: boolean; enable_dedicated_group_chat?: boolean; - jbh_time?: number; + jbh_time?: 0 | 5 | 10 | 15; join_before_host?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -1166,7 +1317,7 @@ type AccountsGetAccountSettingsResponse = { allow_host_to_disable_participant_video?: boolean; personal_meeting?: boolean; require_password_for_instant_meetings?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "jbh_only" | "all" | "none"; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; use_pmi_for_instant_meetings?: boolean; @@ -1234,7 +1385,7 @@ type AccountsGetAccountSettingsResponse = { domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_same_account" | "enforce_login_with_domains"; visible?: boolean; }[]; meeting_authentication?: boolean; @@ -1244,7 +1395,7 @@ type AccountsGetAccountSettingsResponse = { domains?: string; id?: string; name?: string; - type?: string; + type?: "internally" | "enforce_login" | "enforce_login_with_domains"; visible?: boolean; }[]; recording_authentication?: boolean; @@ -1262,17 +1413,17 @@ type AccountsGetAccountSettingsResponse = { keywords?: string[]; name?: string; regular_expression?: string; - status?: string; - trigger_action?: number; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; }[]; policy_max_count?: number; }; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -1288,19 +1439,19 @@ type AccountsGetAccountSettingsResponse = { require_password_for_scheduled_webinar?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - users_who_can_admit_participants_from_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; whitelisted_domains_for_waiting_room?: string; }; webinar_password?: boolean; waiting_room_options?: { enable?: boolean; locked?: boolean; - admit_type?: number; - internal_user_auto_admit?: number; + admit_type?: 1 | 2 | 3 | 4; + internal_user_auto_admit?: 1 | 2 | 3 | 4 | 5; admit_domain_allowlist?: string; - who_can_admit_participants?: number; - sort_order_of_people?: number; + who_can_admit_participants?: 0 | 1; + sort_order_of_people?: 0 | 1; more_options?: { user_invited_by_host_can_bypass_waiting_room?: boolean; move_participants_to_waiting_room_when_host_dropped?: boolean; @@ -1311,13 +1462,13 @@ type AccountsGetAccountSettingsResponse = { } | { in_meeting?: { custom_data_center_regions?: boolean; - data_center_regions?: string[]; - unchecked_data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; + unchecked_data_center_regions?: ("EU" | "HK" | "AU" | "IN" | "TY" | "CN" | "US" | "CA" | "DE" | "NL" | "LA")[]; }; in_session?: { custom_data_center_regions?: boolean; - data_center_regions?: string[]; - unchecked_data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; + unchecked_data_center_regions?: ("EU" | "HK" | "AU" | "IN" | "TY" | "CN" | "US" | "CA" | "DE" | "NL" | "LA")[]; p2p_connetion?: boolean; p2p_ports?: boolean; ports_range?: string; @@ -1332,7 +1483,7 @@ type AccountsGetAccountSettingsResponse = { approved_list?: string[]; denied_list?: string[]; enable?: boolean; - method?: string; + method?: "approve" | "deny"; }; }; recording?: { @@ -1345,26 +1496,26 @@ type AccountsGetAccountSettingsResponse = { recording_audio_transcript?: boolean; cloud_recording_download?: boolean; auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; }; }; type AccountsUpdateAccountSettingsPathParams = { accountId: string; }; type AccountsUpdateAccountSettingsQueryParams = { - option?: string; + option?: "meeting_authentication" | "recording_authentication" | "security" | "meeting_security"; }; type AccountsUpdateAccountSettingsRequestBody = { security?: { admin_change_name_pic?: boolean; admin_change_user_info?: boolean; - user_modifiable_info_by_admin?: string[]; + user_modifiable_info_by_admin?: ("name" | "profile_picture" | "sign_in_email" | "host_key")[]; signin_with_sso?: { enable?: boolean; require_sso_for_domains?: boolean; domains?: string[]; sso_bypass_user_ids?: string[]; - operation?: string; + operation?: "add" | "remove"; }; hide_billing_info?: boolean; import_photos_from_devices?: boolean; @@ -1376,7 +1527,7 @@ type AccountsUpdateAccountSettingsRequestBody = { }; sign_again_period_for_inactivity_on_client?: number; sign_again_period_for_inactivity_on_web?: number; - sign_in_with_two_factor_auth?: string; + sign_in_with_two_factor_auth?: "all" | "group" | "role" | "none"; sign_in_with_two_factor_auth_groups?: string[]; sign_in_with_two_factor_auth_roles?: string[]; }; @@ -1397,25 +1548,28 @@ type AccountsUpdateAccountSettingsRequestBody = { allow_bots_chat?: boolean; share_files?: { enable?: boolean; - share_option?: string; + share_option?: "disable" | "anyone" | "account" | "organization"; + view_option?: "anyone" | "account" | "organization"; restrictions?: { only_allow_specific_file_types?: boolean; - file_type_restrictions?: string[]; - file_type_restrictions_for_external?: string[]; + file_type_restrictions?: (".gz" | ".rar" | ".zip" | ".xls" | ".xlsx" | ".json" | ".png" | ".pptx" | ".ppt" | ".7z" | ".xmind" | ".pdf" | ".pps" | ".txt" | ".docx" | ".doc")[]; + file_type_restrictions_for_external?: (".gz" | ".rar" | ".zip" | ".xls" | ".xlsx" | ".json" | ".png" | ".pptx" | ".ppt" | ".7z" | ".xmind" | ".pdf" | ".pps" | ".txt" | ".docx" | ".doc")[]; maximum_file_size?: boolean; - file_size_restrictions?: number; - file_size_restrictions_for_external?: number; + file_size_restrictions?: 50 | 100 | 200 | 300 | 400 | 500; + file_size_restrictions_for_external?: 50 | 100 | 200 | 300 | 400 | 500; + file_restrictions_apply_to?: "sharing_and_viewing" | "sharing"; }; }; chat_emojis?: { enable?: boolean; - emojis_option?: string; + emojis_option?: "all" | "selected"; }; record_voice_messages?: boolean; record_video_messages?: boolean; screen_capture?: boolean; create_public_channels?: boolean; create_private_channels?: boolean; + create_group_chat?: boolean; share_links_in_chat?: boolean; schedule_meetings_in_chat?: boolean; set_retention_period_in_cloud?: { @@ -1430,17 +1584,17 @@ type AccountsUpdateAccountSettingsRequestBody = { }; allow_users_to_add_contacts?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; allow_users_to_chat_with_others?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; chat_etiquette_tool?: { enable?: boolean; - operate?: string; + operate?: "create" | "update" | "delete"; policies?: { description?: string; id?: string; @@ -1448,13 +1602,13 @@ type AccountsUpdateAccountSettingsRequestBody = { keywords?: string[]; name?: string; regular_expression?: string; - status?: string; - trigger_action?: number; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; }[]; }; send_data_to_third_party_archiving_service?: { enable?: boolean; - type?: string; + type?: "global_relay" | "smarsh"; smtp_delivery_address?: string; user_name?: string; passcode?: string; @@ -1467,8 +1621,80 @@ type AccountsUpdateAccountSettingsRequestBody = { translate_messages?: boolean; search_and_send_animated_gif_images?: { enable?: boolean; - giphy_content_rating?: number; + giphy_content_rating?: 1 | 2 | 3 | 4; + }; + external_collab_restrict?: { + enable?: boolean; + external_chat?: "allowed" | "not_allowed"; + group_id?: string; + }; + external_user_control?: { + enable?: boolean; + selected_option?: 1 | 2 | 3; + external_account?: boolean; + }; + external_invite_approve?: { + enable?: boolean; + selected_option?: 1 | 2; + channel_id?: string; + external_account?: boolean; + }; + external_member_join?: { + enable?: boolean; + external_account?: boolean; + }; + external_join_approve?: { + enable?: boolean; + selected_option?: 1 | 2; + channel_id?: string; + external_account?: boolean; + }; + download_file?: boolean; + share_screen_in_chat?: boolean; + code_snippet?: boolean; + personal_channel?: boolean; + store_revise_chat?: boolean; + set_chat_as_default_tab?: boolean; + hyper_link?: boolean; + suppress_removal_notification?: boolean; + suppress_user_group_notification?: boolean; + allow_remove_msg_by_owner_and_admins?: boolean; + allow_huddles_from_channels?: boolean; + shared_spaces?: boolean; + chat_email_address?: { + enable?: boolean; + only_allow_specific_domains?: boolean; + specific_domains?: string[]; + }; + read_receipts?: { + enable?: boolean; + allow_users_opt_out?: boolean; + }; + allow_delete_message?: { + enable?: boolean; + time?: 0 | 5 | 30 | 60 | 1440 | 10080; + }; + allow_edit_message?: { + enable?: boolean; + time?: 0 | 5 | 30 | 60 | 1440 | 10080; + }; + show_status_to_internal_contact?: boolean; + presence_on_meeting?: boolean; + presence_away_when_screen_saver?: boolean; + show_h323_contact_tab?: boolean; + ai_summary?: { + enable?: boolean; + shown_in_team_chat?: boolean; + }; + ai_compose?: { + enable?: boolean; + shown_in_team_chat?: boolean; }; + ai_recommend?: boolean; + ai_quick_reply?: boolean; + ai_sentence_completion?: boolean; + ai_quick_schedule?: boolean; + survey_poll?: boolean; }; email_notification?: { alternative_host_reminder?: boolean; @@ -1488,10 +1714,10 @@ type AccountsUpdateAccountSettingsRequestBody = { allow_host_to_enable_focus_mode?: boolean; allow_live_streaming?: boolean; allow_users_to_delete_messages_in_meeting_chat?: boolean; - allow_participants_chat_with?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; allow_participants_to_rename?: boolean; allow_show_zoom_windows?: boolean; - allow_users_save_chats?: number; + allow_users_save_chats?: 1 | 2 | 3; annotation?: boolean; anonymous_question_answer?: boolean; attendee_on_hold?: boolean; @@ -1514,8 +1740,8 @@ type AccountsUpdateAccountSettingsRequestBody = { custom_data_center_regions?: boolean; custom_live_streaming_service?: boolean; custom_service_instructions?: string; - meeting_data_transit_and_residency_method?: string; - data_center_regions?: string[]; + meeting_data_transit_and_residency_method?: "cloud" | "On-Prem"; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; dscp_audio?: number; @@ -1523,7 +1749,7 @@ type AccountsUpdateAccountSettingsRequestBody = { dscp_video?: number; dscp_dual?: boolean; e2e_encryption?: boolean; - entry_exit_chime?: string; + entry_exit_chime?: "host" | "all" | "none"; far_end_camera_control?: boolean; feedback?: boolean; file_transfer?: boolean; @@ -1535,14 +1761,14 @@ type AccountsUpdateAccountSettingsRequestBody = { language_item_pairList?: { trans_lang_config?: { speak_language?: { - name?: string; - code?: string; + name?: "Chinese (Simplified)" | "Dutch" | "English" | "French" | "German" | "Italian" | "Japanese" | "Korean" | "Portuguese" | "Russian" | "Spanish" | "Ukrainian"; + code?: "zh" | "nl" | "en" | "fr" | "de" | "it" | "ja" | "ko" | "pt" | "ru" | "es" | "uk"; }; translate_to?: { all?: boolean; language_config?: { - name?: string; - code?: string; + name?: "English"; + code?: "en"; }[]; }; }[]; @@ -1581,7 +1807,7 @@ type AccountsUpdateAccountSettingsRequestBody = { enable?: boolean; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; meeting_survey?: boolean; @@ -1619,19 +1845,19 @@ type AccountsUpdateAccountSettingsRequestBody = { }; watermark?: boolean; webinar_chat?: { - allow_attendees_chat_with?: number; + allow_attendees_chat_with?: 1 | 2 | 3; allow_auto_save_local_chat_file?: boolean; - allow_panelists_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; - default_attendees_chat_with?: number; + allow_users_save_chats?: 0 | 1 | 2; + default_attendees_chat_with?: 1 | 2; enable?: boolean; }; webinar_live_streaming?: { custom_service_instructions?: string; enable?: boolean; live_streaming_reminder?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; }; webinar_polling?: { advanced_polls?: boolean; @@ -1644,9 +1870,9 @@ type AccountsUpdateAccountSettingsRequestBody = { webinar_question_answer?: boolean; webinar_survey?: boolean; whiteboard?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; workplace_by_facebook?: boolean; transfer_meetings_between_devices?: boolean; }; @@ -1664,9 +1890,9 @@ type AccountsUpdateAccountSettingsRequestBody = { allow_users_enter_and_share_pronouns?: boolean; blur_snapshot?: boolean; display_meetings_scheduled_for_others?: boolean; - meeting_qos_and_mos?: number; + meeting_qos_and_mos?: 0 | 1 | 2 | 3; show_one_user_meeting_on_dashboard?: boolean; - use_cdn?: string; + use_cdn?: "none" | "default" | "wangsu"; webinar_registration_options?: { allow_host_to_enable_join_info?: boolean; allow_host_to_enable_social_share_buttons?: boolean; @@ -1692,16 +1918,16 @@ type AccountsUpdateAccountSettingsRequestBody = { chat_with_sender_email?: boolean; video_file?: boolean; chat_with_direct_message?: boolean; - archive_retention?: number; - action_when_archive_failed?: number; - notification_when_archiving_starts?: string; - play_voice_prompt_when_archiving_starts?: string; + archive_retention?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30; + action_when_archive_failed?: 1 | 2; + notification_when_archiving_starts?: "participants" | "guest"; + play_voice_prompt_when_archiving_starts?: "participants" | "guest" | "none"; }; - type?: number; + type?: 1 | 2 | 3; }; auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; - auto_recording?: string; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; + auto_recording?: "local" | "cloud" | "none"; cloud_recording?: boolean; cloud_recording_download?: boolean; cloud_recording_download_host?: boolean; @@ -1748,17 +1974,17 @@ type AccountsUpdateAccountSettingsRequestBody = { show_timestamp?: boolean; }; schedule_meeting?: { - audio_type?: string; + audio_type?: "both" | "telephony" | "voip" | "thirdParty"; enforce_login?: boolean; enforce_login_domains?: string; enforce_login_with_domains?: boolean; force_pmi_jbh_password?: boolean; host_video?: boolean; enable_dedicated_group_chat?: boolean; - jbh_time?: number; + jbh_time?: 0 | 5 | 10 | 15; join_before_host?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -1772,7 +1998,7 @@ type AccountsUpdateAccountSettingsRequestBody = { allow_host_to_disable_participant_video?: boolean; personal_meeting?: boolean; require_password_for_instant_meetings?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "jbh_only" | "all" | "none"; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; use_pmi_for_instant_meetings?: boolean; @@ -1835,22 +2061,22 @@ type AccountsUpdateAccountSettingsRequestBody = { } | ({ allow_authentication_exception?: boolean; authentication_option?: { - action?: string; + action?: "update" | "delete" | "add"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_same_account" | "enforce_login_with_domains"; }; meeting_authentication?: boolean; } | { authentication_option?: { - action?: string; + action?: "update" | "delete" | "add"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "internally" | "enforce_login" | "enforce_login_with_domains"; }; recording_authentication?: boolean; }) | { @@ -1860,7 +2086,7 @@ type AccountsUpdateAccountSettingsRequestBody = { block_user_domain_list?: string[]; chat_etiquette_tool?: { enable?: boolean; - operate?: string; + operate?: "create" | "update" | "delete"; policies?: { description?: string; id?: string; @@ -1868,16 +2094,16 @@ type AccountsUpdateAccountSettingsRequestBody = { keywords?: string[]; name?: string; regular_expression?: string; - status?: string; - trigger_action?: number; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; }[]; }; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -1893,19 +2119,19 @@ type AccountsUpdateAccountSettingsRequestBody = { require_password_for_scheduled_webinar?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - users_who_can_admit_participants_from_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; whitelisted_domains_for_waiting_room?: string; }; webinar_password?: boolean; waiting_room_options?: { enable?: boolean; locked?: boolean; - admit_type?: number; - internal_user_auto_admit?: number; + admit_type?: 1 | 2 | 3 | 4; + internal_user_auto_admit?: 1 | 2 | 3 | 4 | 5; admit_domain_allowlist?: string; - who_can_admit_participants?: number; - sort_order_of_people?: number; + who_can_admit_participants?: 0 | 1; + sort_order_of_people?: 0 | 1; more_options?: { user_invited_by_host_can_bypass_waiting_room?: boolean; move_participants_to_waiting_room_when_host_dropped?: boolean; @@ -1916,11 +2142,11 @@ type AccountsUpdateAccountSettingsRequestBody = { } | { in_meeting?: { custom_data_center_regions?: boolean; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; }; in_session?: { custom_data_center_regions?: boolean; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; p2p_connetion?: boolean; p2p_ports?: boolean; ports_range?: string; @@ -1935,7 +2161,7 @@ type AccountsUpdateAccountSettingsRequestBody = { approved_list?: string[]; denied_list?: string[]; enable?: boolean; - method?: string; + method?: "approve" | "deny"; }; }; recording?: { @@ -1946,14 +2172,14 @@ type AccountsUpdateAccountSettingsRequestBody = { show_timestamp?: boolean; cloud_recording_download?: boolean; auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; }; }; type AccountsGetAccountsWebinarRegistrationSettingsPathParams = { accountId: string; }; type AccountsGetAccountsWebinarRegistrationSettingsQueryParams = { - type?: string; + type?: "webinar"; }; type AccountsGetAccountsWebinarRegistrationSettingsResponse = { options?: { @@ -1963,14 +2189,14 @@ type AccountsGetAccountsWebinarRegistrationSettingsResponse = { show_social_share_buttons?: boolean; }; questions?: { - field_name?: string; + field_name?: "last_name" | "address" | "city" | "country" | "zip" | "state" | "phone" | "industry" | "org" | "job_title" | "purchasing_time_frame" | "role_in_purchase_process" | "no_of_employees" | "comments"; required?: boolean; selected?: boolean; }[]; - approve_type?: number; + approve_type?: 0 | 1; custom_questions?: { title?: string; - type?: string; + type?: "short" | "single_dropdown" | "single_radio" | "multiple"; required?: boolean; selected?: boolean; answers?: string[]; @@ -1980,7 +2206,7 @@ type AccountsUpdateAccountsWebinarRegistrationSettingsPathParams = { accountId: string; }; type AccountsUpdateAccountsWebinarRegistrationSettingsQueryParams = { - type?: string; + type?: "webinar"; }; type AccountsUpdateAccountsWebinarRegistrationSettingsRequestBody = { options?: { @@ -1990,14 +2216,14 @@ type AccountsUpdateAccountsWebinarRegistrationSettingsRequestBody = { show_social_share_buttons?: boolean; }; questions?: { - field_name?: string; + field_name?: "last_name" | "address" | "city" | "country" | "zip" | "state" | "phone" | "industry" | "org" | "job_title" | "purchasing_time_frame" | "role_in_purchase_process" | "no_of_employees" | "comments"; required?: boolean; selected?: boolean; }[]; - approve_type?: number; + approve_type?: 0 | 1; custom_questions?: { title?: string; - type?: string; + type?: "short" | "single_dropdown" | "single_radio" | "multiple"; required?: boolean; selected?: boolean; answers?: string[]; @@ -2171,15 +2397,15 @@ type DashboardsGetIssuesOfZoomRoomsResponse = { }[]; }; type DashboardsListMeetingsQueryParams = { - type?: string; + type?: "past" | "pastOne" | "live"; from: string; to: string; page_size?: number; next_page_token?: string; group_id?: string; group_include_participant?: boolean; - include_fields?: string; - query_date_type?: string; + include_fields?: "tracking_fields"; + query_date_type?: "start_time" | "end_time"; }; type DashboardsListMeetingsResponse = { from?: string; @@ -2192,7 +2418,7 @@ type DashboardsListMeetingsResponse = { } & { meetings?: { host?: string; - audio_quality?: string; + audio_quality?: "good" | "fair" | "poor" | "bad"; custom_keys?: { key?: string; value?: string; @@ -2213,7 +2439,7 @@ type DashboardsListMeetingsResponse = { has_automated_captions?: boolean; id?: number; participants?: number; - screen_share_quality?: string; + screen_share_quality?: "good" | "fair" | "poor" | "bad"; session_key?: string; start_time?: string; topic?: string; @@ -2223,14 +2449,18 @@ type DashboardsListMeetingsResponse = { }[]; user_type?: string; uuid?: string; - video_quality?: string; + video_quality?: "good" | "fair" | "poor" | "bad"; + has_poll?: boolean; + has_qa?: boolean; + has_survey?: boolean; + avg_jointime_cost?: number; }[]; }; type DashboardsGetMeetingDetailsPathParams = { meetingId: string; }; type DashboardsGetMeetingDetailsQueryParams = { - type?: string; + type?: "past" | "pastOne" | "live"; }; type DashboardsGetMeetingDetailsResponse = { host?: string; @@ -2260,15 +2490,19 @@ type DashboardsGetMeetingDetailsResponse = { user_type?: string; uuid?: string; has_meeting_summary?: boolean; + has_poll?: boolean; + has_qa?: boolean; + has_survey?: boolean; + avg_jointime_cost?: number; }; type DashboardsListMeetingParticipantsPathParams = { meetingId: string; }; type DashboardsListMeetingParticipantsQueryParams = { - type?: string; + type?: "past" | "pastOne" | "live"; page_size?: number; next_page_token?: string; - include_fields?: string; + include_fields?: "registrant_id"; }; type DashboardsListMeetingParticipantsResponse = { next_page_token?: string; @@ -2277,12 +2511,14 @@ type DashboardsListMeetingParticipantsResponse = { total_records?: number; } & { participants?: { - audio_quality?: string; + audio_quality?: "" | "good" | "fair" | "poor" | "bad"; camera?: string; - connection_type?: string; + connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + video_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + as_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; customer_key?: string; data_center?: string; - device?: string; + device?: "Phone" | "H.323/SIP" | "Windows" | "Mac" | "iOS" | "Android"; domain?: string; email?: string; from_sip_uri?: string; @@ -2293,49 +2529,53 @@ type DashboardsListMeetingParticipantsResponse = { internal_ip_addresses?: string[]; ip_address?: string; join_time?: string; - leave_reason?: string; + leave_reason?: "$name left the meeting." | "$name got disconnected from the meeting." | "Host ended the meeting." | "Host closed the meeting." | "Host started a new meeting." | "Network connection error." | "Host did not join." | "Exceeded free meeting minutes limit." | "Removed by host." | "Unknown reason." | "Leave waiting room." | "Removed by host from waiting room."; leave_time?: string; location?: string; mac_addr?: string; microphone?: string; - network_type?: string; + network_type?: "Wired" | "Wifi" | "PPP" | "Cellular" | "Others"; participant_user_id?: string; pc_name?: string; recording?: boolean; registrant_id?: string; - role?: string; - screen_share_quality?: string; + role?: "host" | "attendee"; + screen_share_quality?: "" | "good" | "fair" | "poor" | "bad"; share_application?: boolean; share_desktop?: boolean; share_whiteboard?: boolean; sip_uri?: string; speaker?: string; - status?: string; + status?: "in_meeting" | "in_waiting_room"; user_id?: string; participant_uuid?: string; user_name?: string; version?: string; - video_quality?: string; + video_quality?: "" | "good" | "fair" | "poor" | "bad"; bo_mtg_id?: string; audio_call?: { call_number?: string; - call_type?: string; + call_type?: "call-in" | "call-out"; zoom_number?: string; }[]; os?: string; os_version?: string; + browser_name?: string; + browser_version?: string; device_name?: string; groupId?: string; has_archiving?: boolean; - optional_archiving?: string; + optional_archiving?: "no optional archiving" | "join without archiving" | "join with archiving"; client?: string; + total_jointime_cost?: number; + aic_disclaimer?: "no disclaimer" | "agree" | "leave meeting"; }[]; }; type DashboardsListMeetingParticipantsQoSPathParams = { meetingId: string; }; type DashboardsListMeetingParticipantsQoSQueryParams = { - type?: string; + type?: "past" | "live"; page_size?: number; next_page_token?: string; }; @@ -2347,7 +2587,7 @@ type DashboardsListMeetingParticipantsQoSResponse = { } & { participants?: { id?: string; - device?: string; + device?: "Phone" | "H.323/SIP" | "Windows" | "Mac" | "iOS" | "Android"; client?: string; domain?: string; harddisk_id?: string; @@ -2530,13 +2770,35 @@ type DashboardsListMeetingParticipantsQoSResponse = { }; }[]; version?: string; + os?: string; + os_version?: string; + browser_name?: string; + browser_version?: string; + video_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + as_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + participant_uuid?: string; + network_type?: "Wired" | "Wifi" | "PPP" | "Cellular" | "Others"; + data_center?: string; + full_data_center?: string; + connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + share_application?: boolean; + share_desktop?: boolean; + share_whiteboard?: boolean; + recording?: boolean; + device_name?: string; + groupId?: string; + has_archiving?: boolean; + optional_archiving?: "no optional archiving" | "join without archiving" | "join with archiving"; + health?: "Good" | "Warning" | "Critical"; + total_jointime_cost?: number; + issue_list?: string[]; }[]; }; type DashboardsGetPostMeetingFeedbackPathParams = { meetingId: number | string; }; type DashboardsGetPostMeetingFeedbackQueryParams = { - type?: string; + type?: "past" | "pastOne" | "live"; next_page_token?: string; page_size?: number; }; @@ -2546,7 +2808,7 @@ type DashboardsGetPostMeetingFeedbackResponse = { participants?: { date_time?: string; email?: string; - quality?: string; + quality?: "GOOD" | "NOT GOOD"; user_id?: string; comment?: string; }[]; @@ -2555,7 +2817,7 @@ type DashboardsGetMeetingSharingRecordingDetailsPathParams = { meetingId: number | string; }; type DashboardsGetMeetingSharingRecordingDetailsQueryParams = { - type?: string; + type?: "past" | "live"; page_size?: number; next_page_token?: string; }; @@ -2581,11 +2843,11 @@ type DashboardsGetMeetingParticipantQoSPathParams = { participantId: string; }; type DashboardsGetMeetingParticipantQoSQueryParams = { - type?: string; + type?: "past" | "live"; }; type DashboardsGetMeetingParticipantQoSResponse = { id?: string; - device?: string; + device?: "Phone" | "H.323/SIP" | "Windows" | "Mac" | "iOS" | "Android"; client?: string; domain?: string; harddisk_id?: string; @@ -2768,11 +3030,36 @@ type DashboardsGetMeetingParticipantQoSResponse = { }; }[]; version?: string; + os?: string; + os_version?: string; + browser_name?: string; + browser_version?: string; + video_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + as_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + participant_uuid?: string; + network_type?: "Wired" | "Wifi" | "PPP" | "Cellular" | "Others"; + microphone?: string; + speaker?: string; + camera?: string; + data_center?: string; + full_data_center?: string; + connection_type?: "TCP" | "P2P" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + share_application?: boolean; + share_desktop?: boolean; + share_whiteboard?: boolean; + recording?: boolean; + device_name?: string; + groupId?: string; + has_archiving?: boolean; + optional_archiving?: "no optional archiving" | "join without archiving" | "join with archiving"; + health?: "Good" | "Warning" | "Critical"; + total_jointime_cost?: number; + issue_list?: string[]; }; type DashboardsGetMeetingQualityScoresQueryParams = { from: string; to: string; - type?: string; + type?: "meeting" | "participants"; }; type DashboardsGetMeetingQualityScoresResponse = { from?: string; @@ -2799,12 +3086,14 @@ type DashboardsGetMeetingQualityScoresResponse = { to?: string; }; type DashboardsListWebinarsQueryParams = { - type?: string; + type?: "past" | "live"; from: string; to: string; page_size?: number; next_page_token?: string; group_id?: string; + group_include_participant?: boolean; + query_date_type?: "start_time" | "end_time"; }; type DashboardsListWebinarsResponse = { from?: string; @@ -2841,16 +3130,18 @@ type DashboardsListWebinarsResponse = { topic?: string; user_type?: string; uuid?: string; - audio_quality?: string; - video_quality?: string; - screen_share_quality?: string; + audio_quality?: "good" | "fair" | "poor" | "bad"; + video_quality?: "good" | "fair" | "poor" | "bad"; + screen_share_quality?: "good" | "fair" | "poor" | "bad"; + has_poll?: boolean; + has_survey?: boolean; }[]; }; type DashboardsGetWebinarDetailsPathParams = { webinarId: string; }; type DashboardsGetWebinarDetailsQueryParams = { - type?: string; + type?: "past" | "live"; }; type DashboardsGetWebinarDetailsResponse = { host?: string; @@ -2878,18 +3169,20 @@ type DashboardsGetWebinarDetailsResponse = { topic?: string; user_type?: string; uuid?: string; - audio_quality?: string; - video_quality?: string; - screen_share_quality?: string; + audio_quality?: "good" | "fair" | "poor" | "bad"; + video_quality?: "good" | "fair" | "poor" | "bad"; + screen_share_quality?: "good" | "fair" | "poor" | "bad"; + has_poll?: boolean; + has_survey?: boolean; }; type DashboardsGetWebinarParticipantsPathParams = { webinarId: string; }; type DashboardsGetWebinarParticipantsQueryParams = { - type?: string; + type?: "past" | "live"; page_size?: number; next_page_token?: string; - include_fields?: string; + include_fields?: "registrant_id"; }; type DashboardsGetWebinarParticipantsResponse = { next_page_token?: string; @@ -2898,11 +3191,13 @@ type DashboardsGetWebinarParticipantsResponse = { total_records?: number; } & { participants?: { - audio_quality?: string; - connection_type?: string; + audio_quality?: "" | "good" | "fair" | "poor" | "bad"; + connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + video_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + as_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; customer_key?: string; data_center?: string; - device?: string; + device?: "Phone" | "H.323/SIP" | "Windows" | "Mac" | "iOS" | "Android"; domain?: string; email?: string; from_sip_uri?: string; @@ -2912,18 +3207,18 @@ type DashboardsGetWebinarParticipantsResponse = { internal_ip_addresses?: string[]; ip_address?: string; join_time?: string; - leave_reason?: string; + leave_reason?: "$name left the webinar." | "$name got disconnected from the webinar." | "Host ended the webinar." | "Host closed the webinar." | "Host started a new webinar." | "Network connection error." | "Host did not join." | "Exceeded free webinar minutes limit." | "Removed by host." | "Unknown reason." | "Leave waiting room." | "Removed by host from waiting room."; leave_time?: string; location?: string; mac_addr?: string; microphone?: string; - network_type?: string; + network_type?: "Wired" | "Wifi" | "PPP" | "Cellular" | "Others"; participant_user_id?: string; pc_name?: string; recording?: boolean; registrant_id?: string; - role?: string; - screen_share_quality?: string; + role?: "host" | "attendee" | "panelist"; + screen_share_quality?: "" | "good" | "fair" | "poor" | "bad"; share_application?: boolean; share_desktop?: boolean; share_whiteboard?: boolean; @@ -2933,18 +3228,20 @@ type DashboardsGetWebinarParticipantsResponse = { participant_uuid?: string; user_name?: string; version?: string; - video_quality?: string; + video_quality?: "" | "good" | "fair" | "poor" | "bad"; audio_call?: { call_number?: string; - call_type?: string; + call_type?: "call-in" | "call-out"; zoom_number?: string; }[]; os?: string; os_version?: string; + browser_name?: string; + browser_version?: string; device_name?: string; client?: string; has_archiving?: boolean; - optional_archiving?: string; + optional_archiving?: "no optional archiving" | "join without archiving" | "join with archiving"; bo_mtg_id?: string; }[]; }; @@ -2952,7 +3249,7 @@ type DashboardsListWebinarParticipantQoSPathParams = { webinarId: string; }; type DashboardsListWebinarParticipantQoSQueryParams = { - type?: string; + type?: "past" | "live"; page_size?: number; next_page_token?: string; }; @@ -2964,7 +3261,7 @@ type DashboardsListWebinarParticipantQoSResponse = { } & { participants?: { id?: string; - device?: string; + device?: "Phone" | "H.323/SIP" | "Windows" | "Mac" | "iOS" | "Android"; client?: string; domain?: string; harddisk_id?: string; @@ -3147,13 +3444,34 @@ type DashboardsListWebinarParticipantQoSResponse = { }; }[]; version?: string; + os?: string; + os_version?: string; + browser_name?: string; + browser_version?: string; + video_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + as_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + participant_uuid?: string; + network_type?: "Wired" | "Wifi" | "PPP" | "Cellular" | "Others"; + data_center?: string; + full_data_center?: string; + connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + share_application?: boolean; + share_desktop?: boolean; + share_whiteboard?: boolean; + recording?: boolean; + device_name?: string; + optional_archiving?: "no optional archiving" | "join without archiving" | "join with archiving"; + has_archiving?: boolean; + groupId?: string; + health?: "Good" | "Warning" | "Critical"; + issue_list?: string[]; }[]; }; type DashboardsGetPostWebinarFeedbackPathParams = { webinarId: string; }; type DashboardsGetPostWebinarFeedbackQueryParams = { - type?: string; + type?: "past" | "pastOne" | "live"; page_size?: number; next_page_token?: string; }; @@ -3163,7 +3481,7 @@ type DashboardsGetPostWebinarFeedbackResponse = { participants?: { date_time?: string; email?: string; - quality?: string; + quality?: "GOOD" | "NOT GOOD"; user_id?: string; comment?: string; }[]; @@ -3172,7 +3490,7 @@ type DashboardsGetWebinarSharingRecordingDetailsPathParams = { webinarId: string; }; type DashboardsGetWebinarSharingRecordingDetailsQueryParams = { - type?: string; + type?: "past" | "live"; page_size?: number; next_page_token?: string; }; @@ -3198,11 +3516,11 @@ type DashboardsGetWebinarParticipantQoSPathParams = { participantId: string; }; type DashboardsGetWebinarParticipantQoSQueryParams = { - type?: string; + type?: "past" | "live"; }; type DashboardsGetWebinarParticipantQoSResponse = { id?: string; - device?: string; + device?: "Phone" | "H.323/SIP" | "Windows" | "Mac" | "iOS" | "Android"; client?: string; domain?: string; harddisk_id?: string; @@ -3385,8 +3703,27 @@ type DashboardsGetWebinarParticipantQoSResponse = { }; }[]; version?: string; - health?: string; + health?: "Good" | "Warning" | "Critical"; issue_list?: string[]; + os?: string; + os_version?: string; + browser_name?: string; + browser_version?: string; + participant_uuid?: string; + network_type?: "Wired" | "Wifi" | "PPP" | "Cellular" | "Others"; + data_center?: string; + full_data_center?: string; + connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + share_application?: boolean; + share_desktop?: boolean; + share_whiteboard?: boolean; + recording?: boolean; + device_name?: string; + has_archiving?: boolean; + optional_archiving?: "no optional archiving" | "join without archiving" | "join with archiving"; + groupId?: string; + video_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; + as_connection_type?: "P2P" | "TCP" | "UDP" | "Reliable UDP" | "SSL" | "HTTP" | "TCP+Proxy" | "UDP+Proxy" | "Reliable+Proxy" | "SSL+Proxy" | "HTTP+Proxy"; }; type DashboardsListZoomRoomsQueryParams = { page_size?: number; @@ -3539,9 +3876,9 @@ type InformationBarriersListInformationBarrierPoliciesResponse = { recording: boolean; screen_share: boolean; }; - status: number; + status: 0 | 1; to_group_id: string; - type: number; + type: 0 | 1 | 2 | 3; }[]; total_records: number; }; @@ -3559,9 +3896,9 @@ type InformationBarriersCreateInformationBarrierPolicyRequestBody = { recording: boolean; screen_share: boolean; }; - status: number; + status: 0 | 1; to_group_id: string; - type: number; + type: 0 | 1 | 2 | 3; }; type InformationBarriersCreateInformationBarrierPolicyResponse = { assigned_group_id: string; @@ -3577,9 +3914,9 @@ type InformationBarriersCreateInformationBarrierPolicyResponse = { recording: boolean; screen_share: boolean; }; - status: number; + status: 0 | 1; to_group_id: string; - type: number; + type: 0 | 1 | 2 | 3; }; type InformationBarriersGetInformationBarrierPolicyByIDPathParams = { policyId: string; @@ -3598,9 +3935,9 @@ type InformationBarriersGetInformationBarrierPolicyByIDResponse = { recording: boolean; screen_share: boolean; }; - status: number; + status: 0 | 1; to_group_id: string; - type: number; + type: 0 | 1 | 2 | 3; }; type InformationBarriersRemoveInformationBarrierPolicyPathParams = { policyId: string; @@ -3622,12 +3959,12 @@ type InformationBarriersUpdateInformationBarriersPolicyRequestBody = { recording: boolean; screen_share: boolean; }; - status: number; + status: 0 | 1; to_group_id: string; - type: number; + type: 0 | 1 | 2 | 3; }; type RolesListRolesQueryParams = { - type?: string; + type?: "common" | "iq"; }; type RolesListRolesResponse = { roles?: { @@ -3645,7 +3982,7 @@ type RolesCreateRoleRequestBody = { type?: string; privileges?: string[]; }; -type RolesCreateRoleResponse = {}; +type RolesCreateRoleResponse = object; type RolesGetRoleInformationPathParams = { roleId: string; }; @@ -3659,6 +3996,10 @@ type RolesGetRoleInformationResponse = { second_level?: number; }; total_members?: number; + privilege_scopes?: { + permission_id?: string; + group_ids?: string[]; + }[]; }; type RolesDeleteRolePathParams = { roleId: string; @@ -3674,7 +4015,7 @@ type RolesUpdateRoleInformationRequestBody = { second_level?: number; }; }; -type RolesUpdateRoleInformationResponse = {}; +type RolesUpdateRoleInformationResponse = object; type RolesListMembersInRolePathParams = { roleId: string; }; @@ -3716,6 +4057,117 @@ type RolesUnassignRolePathParams = { roleId: string; memberId: string; }; +type SurveyManagementGetSurveysQueryParams = { + page_size?: string; + next_page_token?: string; +}; +type SurveyManagementGetSurveysResponse = { + surveys?: { + survey_id?: string; + survey_name?: string; + survey_type?: "basic_poll" | "advanced_poll" | "quiz" | "survey" | "consumer_engagement_survey"; + }[]; + next_page_token?: string; +}; +type SurveyManagementGetSurveyInfoPathParams = { + surveyId: string; +}; +type SurveyManagementGetSurveyInfoResponse = { + survey_id?: string; + survey_name?: string; + survey_type?: "basic_poll" | "advanced_poll" | "quiz" | "survey" | "consumer_engagement_survey"; + published?: boolean; + anonymous?: boolean; + survey_questions?: { + question_name?: string; + question_id?: string; + question_order?: number; + question_type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; + required?: boolean; + sub_questions?: { + sub_question_name?: string; + sub_question_id?: string; + sub_question_order?: number; + }[]; + options?: { + option_id?: string; + option_value?: string; + option_label?: string; + option_order?: number; + }[]; + }[]; +}; +type SurveyManagementGetSurveyAnswersPathParams = { + surveyId: string; +}; +type SurveyManagementGetSurveyAnswersQueryParams = { + page_size?: string; + next_page_token?: string; + instance_id?: string; +}; +type SurveyManagementGetSurveyAnswersResponse = { + survey_answers?: { + email?: string; + name?: string; + instance_id?: string; + submit_time?: string; + anonymous?: boolean; + questions?: { + question_id?: string; + question_answers?: { + option_id?: string; + answer?: string; + }[]; + sub_questions?: { + sub_question_id?: string; + sub_question_answers?: { + option_id?: string; + answer?: string; + }[]; + }[]; + }[]; + }[]; + next_page_token?: string; +}; +type SurveyManagementGetSurveyInstancesPathParams = { + surveyId: string; +}; +type SurveyManagementGetSurveyInstancesQueryParams = { + page_size?: string; + next_page_token?: string; + instance_id?: string; +}; +type SurveyManagementGetSurveyInstancesResponse = { + survey_instances?: { + instance_name?: string; + instance_id?: string; + product_type?: "meeting" | "webinar" | "contact_center" | "survey_public_link" | "team_chat" | "vitual_agent"; + has_response?: boolean; + survey_id?: string; + survey_name?: string; + survey_type?: "basic_poll" | "advanced_poll" | "quiz" | "survey" | "consumer_engagement_survey"; + anonymous?: string; + survey_questions?: { + question_name?: string; + question_id?: string; + question_order?: number; + question_type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; + required?: boolean; + sub_questions?: { + sub_question_name?: string; + sub_question_id?: string; + sub_question_order?: string; + }[]; + options?: { + option_id?: string; + option_value?: string; + option_label?: string; + option_order?: string; + }[]; + }[]; + }[]; + next_page_token?: string; +}; declare class AccountsEndpoints extends WebEndpoints { readonly accounts: { getLockedSettings: (_: { @@ -3746,6 +4198,24 @@ declare class AccountsEndpoints extends WebEndpoints { send_data_to_third_party_archiving_service?: boolean; translate_messages?: boolean; search_and_send_animated_gif_images?: boolean; + shared_spaces?: boolean; + allow_create_channels_and_group_chats?: boolean; + allow_huddles_from_channels?: boolean; + download_file?: boolean; + share_screen_in_chat?: boolean; + chat_email_address?: boolean; + read_receipts?: boolean; + allow_delete_message?: boolean; + allow_edit_message?: boolean; + presence_on_meeting?: boolean; + presence_away_when_screen_saver?: boolean; + ai_summary?: boolean; + ai_compose?: boolean; + ai_recommend?: boolean; + ai_reply?: boolean; + ai_sentence_completion?: boolean; + ai_quick_schedule?: boolean; + survey_poll?: boolean; }; email_notification?: { alternative_host_reminder?: boolean; @@ -3865,7 +4335,7 @@ declare class AccountsEndpoints extends WebEndpoints { block_user_domain?: boolean; chat_etiquette_tool?: boolean; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; only_authenticated_can_join_from_webclient?: boolean; @@ -3896,13 +4366,13 @@ declare class AccountsEndpoints extends WebEndpoints { security?: { admin_change_name_pic?: boolean; admin_change_user_info?: boolean; - user_modifiable_info_by_admin?: string[]; + user_modifiable_info_by_admin?: ("name" | "profile_picture" | "sign_in_email" | "host_key")[]; signin_with_sso?: { enable?: boolean; require_sso_for_domains?: boolean; domains?: string[]; sso_bypass_user_ids?: string[]; - operation?: string; + operation?: "add" | "remove"; }; hide_billing_info?: boolean; import_photos_from_devices?: boolean; @@ -3914,7 +4384,7 @@ declare class AccountsEndpoints extends WebEndpoints { }; sign_again_period_for_inactivity_on_client?: number; sign_again_period_for_inactivity_on_web?: number; - sign_in_with_two_factor_auth?: string; + sign_in_with_two_factor_auth?: "all" | "group" | "role" | "none"; sign_in_with_two_factor_auth_groups?: string[]; sign_in_with_two_factor_auth_roles?: string[]; }; @@ -3935,25 +4405,28 @@ declare class AccountsEndpoints extends WebEndpoints { allow_bots_chat?: boolean; share_files?: { enable?: boolean; - share_option?: string; + share_option?: "disable" | "anyone" | "account" | "organization"; + view_option?: "anyone" | "account" | "organization"; restrictions?: { only_allow_specific_file_types?: boolean; - file_type_restrictions?: string[]; - file_type_restrictions_for_external?: string[]; + file_type_restrictions?: (".gz" | ".rar" | ".zip" | ".xls" | ".xlsx" | ".json" | ".png" | ".pptx" | ".ppt" | ".7z" | ".xmind" | ".pdf" | ".pps" | ".txt" | ".docx" | ".doc")[]; + file_type_restrictions_for_external?: (".gz" | ".rar" | ".zip" | ".xls" | ".xlsx" | ".json" | ".png" | ".pptx" | ".ppt" | ".7z" | ".xmind" | ".pdf" | ".pps" | ".txt" | ".docx" | ".doc")[]; maximum_file_size?: boolean; - file_size_restrictions?: number; - file_size_restrictions_for_external?: number; + file_size_restrictions?: 50 | 100 | 200 | 300 | 400 | 500; + file_size_restrictions_for_external?: 50 | 100 | 200 | 300 | 400 | 500; + file_restrictions_apply_to?: "sharing_and_viewing" | "sharing"; }; }; chat_emojis?: { enable?: boolean; - emojis_option?: string; + emojis_option?: "all" | "selected"; }; record_voice_messages?: boolean; record_video_messages?: boolean; screen_capture?: boolean; create_public_channels?: boolean; create_private_channels?: boolean; + create_group_chat?: boolean; share_links_in_chat?: boolean; schedule_meetings_in_chat?: boolean; set_retention_period_in_cloud?: { @@ -3968,17 +4441,17 @@ declare class AccountsEndpoints extends WebEndpoints { }; allow_users_to_add_contacts?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; allow_users_to_chat_with_others?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; chat_etiquette_tool?: { enable?: boolean; - operate?: string; + operate?: "create" | "update" | "delete"; policies?: { description?: string; id?: string; @@ -3986,13 +4459,13 @@ declare class AccountsEndpoints extends WebEndpoints { keywords?: string[]; name?: string; regular_expression?: string; - status?: string; - trigger_action?: number; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; }[]; }; send_data_to_third_party_archiving_service?: { enable?: boolean; - type?: string; + type?: "global_relay" | "smarsh"; smtp_delivery_address?: string; user_name?: string; passcode?: string; @@ -4005,8 +4478,80 @@ declare class AccountsEndpoints extends WebEndpoints { translate_messages?: boolean; search_and_send_animated_gif_images?: { enable?: boolean; - giphy_content_rating?: number; + giphy_content_rating?: 1 | 2 | 3 | 4; + }; + external_collab_restrict?: { + enable?: boolean; + external_chat?: "allowed" | "not_allowed"; + group_id?: string; + }; + external_user_control?: { + enable?: boolean; + selected_option?: 1 | 2 | 3; + external_account?: boolean; }; + external_invite_approve?: { + enable?: boolean; + selected_option?: 1 | 2; + channel_id?: string; + external_account?: boolean; + }; + external_member_join?: { + enable?: boolean; + external_account?: boolean; + }; + external_join_approve?: { + enable?: boolean; + selected_option?: 1 | 2; + channel_id?: string; + external_account?: boolean; + }; + download_file?: boolean; + share_screen_in_chat?: boolean; + code_snippet?: boolean; + personal_channel?: boolean; + store_revise_chat?: boolean; + set_chat_as_default_tab?: boolean; + hyper_link?: boolean; + suppress_removal_notification?: boolean; + suppress_user_group_notification?: boolean; + allow_remove_msg_by_owner_and_admins?: boolean; + allow_huddles_from_channels?: boolean; + shared_spaces?: boolean; + chat_email_address?: { + enable?: boolean; + only_allow_specific_domains?: boolean; + specific_domains?: string[]; + }; + read_receipts?: { + enable?: boolean; + allow_users_opt_out?: boolean; + }; + allow_delete_message?: { + enable?: boolean; + time?: 0 | 5 | 30 | 60 | 1440 | 10080; + }; + allow_edit_message?: { + enable?: boolean; + time?: 0 | 5 | 30 | 60 | 1440 | 10080; + }; + show_status_to_internal_contact?: boolean; + presence_on_meeting?: boolean; + presence_away_when_screen_saver?: boolean; + show_h323_contact_tab?: boolean; + ai_summary?: { + enable?: boolean; + shown_in_team_chat?: boolean; + }; + ai_compose?: { + enable?: boolean; + shown_in_team_chat?: boolean; + }; + ai_recommend?: boolean; + ai_quick_reply?: boolean; + ai_sentence_completion?: boolean; + ai_quick_schedule?: boolean; + survey_poll?: boolean; }; email_notification?: { alternative_host_reminder?: boolean; @@ -4026,10 +4571,10 @@ declare class AccountsEndpoints extends WebEndpoints { allow_host_to_enable_focus_mode?: boolean; allow_live_streaming?: boolean; allow_users_to_delete_messages_in_meeting_chat?: boolean; - allow_participants_chat_with?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; allow_participants_to_rename?: boolean; allow_show_zoom_windows?: boolean; - allow_users_save_chats?: number; + allow_users_save_chats?: 1 | 2 | 3; annotation?: boolean; anonymous_question_answer?: boolean; attendee_on_hold?: boolean; @@ -4052,8 +4597,8 @@ declare class AccountsEndpoints extends WebEndpoints { custom_data_center_regions?: boolean; custom_live_streaming_service?: boolean; custom_service_instructions?: string; - meeting_data_transit_and_residency_method?: string; - data_center_regions?: string[]; + meeting_data_transit_and_residency_method?: "cloud" | "On-Prem"; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; dscp_audio?: number; @@ -4061,7 +4606,7 @@ declare class AccountsEndpoints extends WebEndpoints { dscp_video?: number; dscp_dual?: boolean; e2e_encryption?: boolean; - entry_exit_chime?: string; + entry_exit_chime?: "host" | "all" | "none"; far_end_camera_control?: boolean; feedback?: boolean; file_transfer?: boolean; @@ -4073,14 +4618,14 @@ declare class AccountsEndpoints extends WebEndpoints { language_item_pairList?: { trans_lang_config?: { speak_language?: { - name?: string; - code?: string; + name?: "Chinese (Simplified)" | "Dutch" | "English" | "French" | "German" | "Italian" | "Japanese" | "Korean" | "Portuguese" | "Russian" | "Spanish" | "Ukrainian"; + code?: "zh" | "nl" | "en" | "fr" | "de" | "it" | "ja" | "ko" | "pt" | "ru" | "es" | "uk"; }; translate_to?: { all?: boolean; language_config?: { - name?: string; - code?: string; + name?: "English"; + code?: "en"; }[]; }; }[]; @@ -4119,7 +4664,7 @@ declare class AccountsEndpoints extends WebEndpoints { enable?: boolean; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; meeting_survey?: boolean; @@ -4157,19 +4702,19 @@ declare class AccountsEndpoints extends WebEndpoints { }; watermark?: boolean; webinar_chat?: { - allow_attendees_chat_with?: number; + allow_attendees_chat_with?: 1 | 2 | 3; allow_auto_save_local_chat_file?: boolean; - allow_panelists_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; - default_attendees_chat_with?: number; + allow_users_save_chats?: 0 | 1 | 2; + default_attendees_chat_with?: 1 | 2; enable?: boolean; }; webinar_live_streaming?: { custom_service_instructions?: string; enable?: boolean; live_streaming_reminder?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; }; webinar_polling?: { advanced_polls?: boolean; @@ -4182,9 +4727,9 @@ declare class AccountsEndpoints extends WebEndpoints { webinar_question_answer?: boolean; webinar_survey?: boolean; whiteboard?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; workplace_by_facebook?: boolean; transfer_meetings_between_devices?: boolean; }; @@ -4202,9 +4747,9 @@ declare class AccountsEndpoints extends WebEndpoints { allow_users_enter_and_share_pronouns?: boolean; blur_snapshot?: boolean; display_meetings_scheduled_for_others?: boolean; - meeting_qos_and_mos?: number; + meeting_qos_and_mos?: 0 | 1 | 2 | 3; show_one_user_meeting_on_dashboard?: boolean; - use_cdn?: string; + use_cdn?: "none" | "default" | "wangsu"; webinar_registration_options?: { allow_host_to_enable_join_info?: boolean; allow_host_to_enable_social_share_buttons?: boolean; @@ -4230,16 +4775,16 @@ declare class AccountsEndpoints extends WebEndpoints { chat_with_sender_email?: boolean; video_file?: boolean; chat_with_direct_message?: boolean; - archive_retention?: number; - action_when_archive_failed?: number; - notification_when_archiving_starts?: string; - play_voice_prompt_when_archiving_starts?: string; + archive_retention?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30; + action_when_archive_failed?: 1 | 2; + notification_when_archiving_starts?: "participants" | "guest"; + play_voice_prompt_when_archiving_starts?: "participants" | "guest" | "none"; }; - type?: number; + type?: 1 | 2 | 3; }; auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; - auto_recording?: string; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; + auto_recording?: "local" | "cloud" | "none"; cloud_recording?: boolean; cloud_recording_download?: boolean; cloud_recording_download_host?: boolean; @@ -4286,17 +4831,17 @@ declare class AccountsEndpoints extends WebEndpoints { show_timestamp?: boolean; }; schedule_meeting?: { - audio_type?: string; + audio_type?: "both" | "telephony" | "voip" | "thirdParty"; enforce_login?: boolean; enforce_login_domains?: string; enforce_login_with_domains?: boolean; force_pmi_jbh_password?: boolean; host_video?: boolean; enable_dedicated_group_chat?: boolean; - jbh_time?: number; + jbh_time?: 0 | 5 | 10 | 15; join_before_host?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -4310,7 +4855,7 @@ declare class AccountsEndpoints extends WebEndpoints { allow_host_to_disable_participant_video?: boolean; personal_meeting?: boolean; require_password_for_instant_meetings?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "jbh_only" | "all" | "none"; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; use_pmi_for_instant_meetings?: boolean; @@ -4375,24 +4920,24 @@ declare class AccountsEndpoints extends WebEndpoints { body?: { allow_authentication_exception?: boolean; authentication_option?: { - action?: string; + action?: "update" | "delete" | "add"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_same_account" | "enforce_login_with_domains"; }; meeting_authentication?: boolean; }; } | { body?: { authentication_option?: { - action?: string; + action?: "update" | "delete" | "add"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "internally" | "enforce_login" | "enforce_login_with_domains"; }; recording_authentication?: boolean; }; @@ -4404,7 +4949,7 @@ declare class AccountsEndpoints extends WebEndpoints { block_user_domain_list?: string[]; chat_etiquette_tool?: { enable?: boolean; - operate?: string; + operate?: "create" | "update" | "delete"; policies?: { description?: string; id?: string; @@ -4412,16 +4957,16 @@ declare class AccountsEndpoints extends WebEndpoints { keywords?: string[]; name?: string; regular_expression?: string; - status?: string; - trigger_action?: number; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; }[]; }; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -4437,19 +4982,19 @@ declare class AccountsEndpoints extends WebEndpoints { require_password_for_scheduled_webinar?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - users_who_can_admit_participants_from_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; whitelisted_domains_for_waiting_room?: string; }; webinar_password?: boolean; waiting_room_options?: { enable?: boolean; locked?: boolean; - admit_type?: number; - internal_user_auto_admit?: number; + admit_type?: 1 | 2 | 3 | 4; + internal_user_auto_admit?: 1 | 2 | 3 | 4 | 5; admit_domain_allowlist?: string; - who_can_admit_participants?: number; - sort_order_of_people?: number; + who_can_admit_participants?: 0 | 1; + sort_order_of_people?: 0 | 1; more_options?: { user_invited_by_host_can_bypass_waiting_room?: boolean; move_participants_to_waiting_room_when_host_dropped?: boolean; @@ -4462,11 +5007,11 @@ declare class AccountsEndpoints extends WebEndpoints { body?: { in_meeting?: { custom_data_center_regions?: boolean; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; }; in_session?: { custom_data_center_regions?: boolean; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; p2p_connetion?: boolean; p2p_ports?: boolean; ports_range?: string; @@ -4481,7 +5026,7 @@ declare class AccountsEndpoints extends WebEndpoints { approved_list?: string[]; denied_list?: string[]; enable?: boolean; - method?: string; + method?: "approve" | "deny"; }; }; recording?: { @@ -4492,7 +5037,7 @@ declare class AccountsEndpoints extends WebEndpoints { show_timestamp?: boolean; cloud_recording_download?: boolean; auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; }; }; }) & { @@ -4655,7 +5200,7 @@ declare class AccountsEndpoints extends WebEndpoints { }) => Promise>; createRole: (_: object & { body?: RolesCreateRoleRequestBody; - }) => Promise>; + }) => Promise>; getRoleInformation: (_: { path: RolesGetRoleInformationPathParams; } & object) => Promise>; @@ -4666,7 +5211,7 @@ declare class AccountsEndpoints extends WebEndpoints { path: RolesUpdateRoleInformationPathParams; } & { body?: RolesUpdateRoleInformationRequestBody; - } & object) => Promise>; + } & object) => Promise>; listMembersInRole: (_: { path: RolesListMembersInRolePathParams; } & object & { @@ -4681,6 +5226,24 @@ declare class AccountsEndpoints extends WebEndpoints { path: RolesUnassignRolePathParams; } & object) => Promise>; }; + readonly surveyManagement: { + getSurveys: (_: object & { + query?: SurveyManagementGetSurveysQueryParams; + }) => Promise>; + getSurveyInfo: (_: { + path: SurveyManagementGetSurveyInfoPathParams; + } & object) => Promise>; + getSurveyAnswers: (_: { + path: SurveyManagementGetSurveyAnswersPathParams; + } & object & { + query?: SurveyManagementGetSurveyAnswersQueryParams; + }) => Promise>; + getSurveyInstances: (_: { + path: SurveyManagementGetSurveyInstancesPathParams; + } & object & { + query?: SurveyManagementGetSurveyInstancesQueryParams; + }) => Promise>; + }; } type AccountVanityUrlRejectedEvent = Event<"account.vanity_url_rejected"> & { @@ -4728,9 +5291,9 @@ type InformationBarriersPolicyDeletedEvent = Event<"information_barriers.policy_ recording: boolean; screen_share: boolean; }; - status: number; + status: 0 | 1; to_group_id: string; - type: number; + type: 0 | 1 | 2 | 3; }; }; }; @@ -4740,7 +5303,7 @@ type AccountUpdatedEvent = Event<"account.updated"> & { payload?: { account_id?: string; operator?: string; - operation?: string; + operation?: "apply_vanity_url" | "managed_domains_added" | "managed_domains_deleted" | "managed_domains_verifying"; object?: { id?: string; account_name?: string; @@ -4773,9 +5336,9 @@ type InformationBarriersPolicyCreatedEvent = Event<"information_barriers.policy_ recording: boolean; screen_share: boolean; }; - status: number; + status: 0 | 1; to_group_id: string; - type: number; + type: 0 | 1 | 2 | 3; }; }; }; @@ -4904,6 +5467,26 @@ type AccountLockSettingsUpdatedEvent = Event<"account.lock_settings_updated"> & send_data_to_third_party_archiving_service?: boolean; translate_messages?: boolean; search_and_send_animated_gif_images?: boolean; + set_retention_period_in_cloud?: boolean; + set_retention_period_in_local?: boolean; + shared_spaces?: boolean; + allow_create_channels_and_group_chats?: boolean; + allow_huddles_from_channels?: boolean; + download_file?: boolean; + share_screen_in_chat?: boolean; + chat_email_address?: boolean; + read_receipts?: boolean; + allow_delete_message?: boolean; + allow_edit_message?: boolean; + presence_on_meeting?: boolean; + presence_away_when_screen_saver?: boolean; + ai_summary?: boolean; + ai_compose?: boolean; + ai_recommend?: boolean; + ai_reply?: boolean; + ai_sentence_completion?: boolean; + ai_quick_schedule?: boolean; + survey_poll?: boolean; }; other_options?: { blur_snapshot?: boolean; @@ -5053,6 +5636,26 @@ type AccountLockSettingsUpdatedEvent = Event<"account.lock_settings_updated"> & send_data_to_third_party_archiving_service?: boolean; translate_messages?: boolean; search_and_send_animated_gif_images?: boolean; + set_retention_period_in_cloud?: boolean; + set_retention_period_in_local?: boolean; + shared_spaces?: boolean; + allow_create_channels_and_group_chats?: boolean; + allow_huddles_from_channels?: boolean; + download_file?: boolean; + share_screen_in_chat?: boolean; + chat_email_address?: boolean; + read_receipts?: boolean; + allow_delete_message?: boolean; + allow_edit_message?: boolean; + presence_on_meeting?: boolean; + presence_away_when_screen_saver?: boolean; + ai_summary?: boolean; + ai_compose?: boolean; + ai_recommend?: boolean; + ai_reply?: boolean; + ai_sentence_completion?: boolean; + ai_quick_schedule?: boolean; + survey_poll?: boolean; }; meeting_security?: { approved_or_denied_countries_or_regions?: boolean; @@ -5070,19 +5673,8 @@ type AccountLockSettingsUpdatedEvent = Event<"account.lock_settings_updated"> & webinar_password?: boolean; }; tsp?: { - share_files?: boolean; - chat_emojis?: boolean; - record_voice_messages?: boolean; - record_video_messages?: boolean; - screen_capture?: boolean; - share_links_in_chat?: boolean; - schedule_meetings_in_chat?: boolean; - allow_users_to_add_contacts?: boolean; - allow_users_to_chat_with_others?: boolean; - chat_etiquette_tool?: boolean; - send_data_to_third_party_archiving_service?: boolean; - translate_messages?: boolean; - search_and_send_animated_gif_images?: boolean; + call_out?: boolean; + show_international_numbers_link?: boolean; }; }; }; @@ -5120,9 +5712,9 @@ type InformationBarriersPolicyUpdatedEvent = Event<"information_barriers.policy_ recording: boolean; screen_share: boolean; }; - status: number; + status: 0 | 1; to_group_id: string; - type: number; + type: 0 | 1 | 2 | 3; }; }; }; @@ -5160,11 +5752,11 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { force_pmi_jbh_password?: boolean; use_pmi_for_scheduled_meetings?: boolean; pstn_password_protected?: string; - jbh_time?: number; + jbh_time?: 0 | 5 | 10 | 15; personal_meeting?: boolean; require_password_for_instant_meetings?: boolean; mute_upon_entry?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "jbh_only" | "all" | "none"; use_pmi_for_instant_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; pmi_password?: string; @@ -5190,8 +5782,8 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { e2e_encryption?: boolean; chat?: boolean; allow_users_to_delete_messages_in_meeting_chat?: boolean; - allow_participants_chat_with?: number; - allow_users_save_chats?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; + allow_users_save_chats?: 1 | 2 | 3; private_chat?: boolean; auto_saving_chat?: boolean; file_transfer?: boolean; @@ -5243,7 +5835,7 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { non_verbal_feedback?: boolean; remote_support?: boolean; custom_data_center_regions?: boolean; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; language_interpretation?: { enable?: boolean; enable_language_interpretation_by_default?: boolean; @@ -5255,11 +5847,11 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { sign_language_interpretation?: { enable?: boolean; enable_sign_language_interpretation_by_default?: boolean; - languages?: string[]; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; custom_languages?: string[]; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; show_a_join_from_your_browser_link?: boolean; @@ -5273,17 +5865,17 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { custom_service_instructions?: string; webinar_live_streaming?: { enable?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; custom_service_instructions?: string; live_streaming_reminder?: boolean; }; webinar_chat?: { enable?: boolean; - allow_panelists_chat_with?: number; - allow_attendees_chat_with?: number; - default_attendees_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; + allow_attendees_chat_with?: 1 | 2 | 3; + default_attendees_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; + allow_users_save_chats?: 0 | 1 | 2; allow_auto_save_local_chat_file?: boolean; }; closed_captioning?: { @@ -5306,9 +5898,9 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { webinar_survey?: boolean; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; }; email_notification?: { cloud_recording_available_reminder?: boolean; @@ -5366,7 +5958,7 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { cloud_recording_download_host?: boolean; account_user_access_recording?: boolean; auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; record_files_separately?: { active_speaker?: boolean; gallery_view?: boolean; @@ -5405,18 +5997,18 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { allow_recovery_deleted_cloud_recordings?: boolean; archive?: { enable?: boolean; - type?: number; + type?: 1 | 2 | 3; settings?: { chat_file?: boolean; chat_with_sender_email?: boolean; audio_file?: boolean; video_file?: boolean; cc_transcript_file?: boolean; - chat_with_direct_message?: boolean; - archive_retention?: number; - action_when_archive_failed?: number; - notification_when_archiving_starts?: string; - play_voice_prompt_when_archiving_starts?: string; + chat_with_direct_message?: never; + archive_retention?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30; + action_when_archive_failed?: 1 | 2; + notification_when_archiving_starts?: "participants" | "guest"; + play_voice_prompt_when_archiving_starts?: "participants" | "guest" | "none"; }; }; }; @@ -5443,10 +6035,10 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { allow_auto_active_users?: boolean; blur_snapshot?: boolean; display_meetings_scheduled_for_others?: boolean; - use_cdn?: string; + use_cdn?: "none" | "default" | "wangsu"; allow_users_contact_support_via_chat?: boolean; show_one_user_meeting_on_dashboard?: boolean; - meeting_qos_and_mos?: number; + meeting_qos_and_mos?: 0 | 1 | 2 | 3; allow_users_enter_and_share_pronouns?: boolean; webinar_registration_options?: { allow_host_to_enable_social_share_buttons?: boolean; @@ -5469,25 +6061,28 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { allow_bots_chat?: boolean; share_files?: { enable?: boolean; - share_option?: string; + share_option?: "disable" | "anyone" | "account" | "organization"; + view_option?: "anyone" | "account" | "organization"; restrictions?: { only_allow_specific_file_types?: boolean; - file_type_restrictions?: string[]; - file_type_restrictions_for_external?: string[]; + file_type_restrictions?: (".gz" | ".rar" | ".zip" | ".xls" | ".xlsx" | ".json" | ".png" | ".pptx" | ".ppt" | ".7z" | ".xmind" | ".pdf" | ".pps" | ".txt" | ".docx" | ".doc")[]; + file_type_restrictions_for_external?: (".gz" | ".rar" | ".zip" | ".xls" | ".xlsx" | ".json" | ".png" | ".pptx" | ".ppt" | ".7z" | ".xmind" | ".pdf" | ".pps" | ".txt" | ".docx" | ".doc")[]; maximum_file_size?: boolean; - file_size_restrictions?: number; - file_size_restrictions_for_external?: number; + file_size_restrictions?: 50 | 100 | 200 | 300 | 400 | 500; + file_size_restrictions_for_external?: 50 | 100 | 200 | 300 | 400 | 500; + file_restrictions_apply_to?: "sharing_and_viewing" | "sharing"; }; }; chat_emojis?: { enable?: boolean; - emojis_option?: string; + emojis_option?: "all" | "selected"; }; record_voice_messages?: boolean; record_video_messages?: boolean; screen_capture?: boolean; create_public_channels?: boolean; create_private_channels?: boolean; + create_group_chat?: boolean; share_links_in_chat?: boolean; schedule_meetings_in_chat?: boolean; set_retention_period_in_cloud?: { @@ -5502,12 +6097,12 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { }; allow_users_to_add_contacts?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; allow_users_to_chat_with_others?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; chat_etiquette_tool?: { @@ -5519,14 +6114,14 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { keywords?: string[]; name?: string; regular_expression?: string; - status?: string; - trigger_action?: number; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; }[]; policy_max_count?: number; }; send_data_to_third_party_archiving_service?: { enable?: boolean; - type?: string; + type?: "global_relay" | "smarsh"; smtp_delivery_address?: string; user_name?: string; passcode?: string; @@ -5539,16 +6134,89 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { translate_messages?: boolean; search_and_send_animated_gif_images?: { enable?: boolean; - giphy_content_rating?: number; + giphy_content_rating?: 1 | 2 | 3 | 4; + }; + external_collab_restrict?: { + enable?: boolean; + external_chat?: "allowed" | "not_allowed"; + group_id?: string; + }; + external_user_control?: { + enable?: boolean; + selected_option?: 1 | 2 | 3; + external_account?: boolean; + }; + external_invite_approve?: { + enable?: boolean; + selected_option?: 1 | 2; + channel_id?: string; + external_account?: boolean; + }; + external_member_join?: { + enable?: boolean; + external_account?: boolean; + }; + external_join_approve?: { + enable?: boolean; + selected_option?: 1 | 2; + channel_id?: string; + external_account?: boolean; + }; + download_file?: boolean; + share_screen_in_chat?: boolean; + code_snippet?: boolean; + personal_channel?: boolean; + store_revise_chat?: boolean; + set_chat_as_default_tab?: boolean; + hyper_link?: boolean; + suppress_removal_notification?: boolean; + suppress_user_group_notification?: boolean; + allow_remove_msg_by_owner_and_admins?: boolean; + allow_huddles_from_channels?: boolean; + shared_spaces?: boolean; + chat_email_address?: { + enable?: boolean; + only_allow_specific_domains?: boolean; + specific_domains?: string[]; + }; + read_receipts?: { + enable?: boolean; + allow_users_opt_out?: boolean; + }; + allow_delete_message?: { + enable?: boolean; + time?: 0 | 5 | 30 | 60 | 1440 | 10080; + }; + allow_edit_message?: { + enable?: boolean; + time?: 0 | 5 | 30 | 60 | 1440 | 10080; + }; + show_status_to_internal_contact?: boolean; + presence_on_meeting?: boolean; + presence_away_when_screen_saver?: boolean; + show_h323_contact_tab?: boolean; + ai_summary?: { + enable?: boolean; + shown_in_team_chat?: boolean; + }; + ai_compose?: { + enable?: boolean; + shown_in_team_chat?: boolean; }; + ai_recommend?: boolean; + ai_quick_reply?: boolean; + ai_sentence_completion?: boolean; + ai_quick_schedule?: boolean; + survey_poll?: boolean; + type?: never; }; meeting_security?: { auto_security?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; whitelisted_domains_for_waiting_room?: string; - users_who_can_admit_participants_from_waiting_room?: number; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; }; meeting_password?: boolean; require_password_for_scheduled_meeting?: boolean; @@ -5563,12 +6231,12 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { have_special_character?: boolean; only_allow_numeric?: boolean; have_upper_and_lower_characters?: boolean; - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; weak_enhance_detection?: boolean; }; embed_password_in_join_link?: boolean; end_to_end_encrypted_meetings?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; block_user_domain?: boolean; block_user_domain_list?: string[]; only_authenticated_can_join_from_webclient?: boolean; @@ -5578,17 +6246,17 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { id?: string; name?: string; description?: string; - trigger_action?: number; + trigger_action?: 1 | 2; keywords?: string[]; regular_expression?: string; - status?: string; + status?: "activated" | "deactivated"; is_locked?: boolean; }[]; }; }; in_session?: { custom_data_center_regions?: boolean; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; p2p_connetion?: boolean; p2p_ports?: boolean; ports_range?: string; @@ -5603,43 +6271,555 @@ type AccountSettingsUpdatedEvent = Event<"account.settings_updated"> & { approved_list?: string[]; denied_list?: string[]; enable?: boolean; - method?: string; + method?: "approve" | "deny"; }; }; }; }; time_stamp?: number; - }; -}; -type AccountsEvents = AccountVanityUrlRejectedEvent | AccountCreatedEvent | InformationBarriersPolicyDeletedEvent | AccountUpdatedEvent | InformationBarriersPolicyCreatedEvent | AccountLockSettingsUpdatedEvent | AccountDisassociatedEvent | InformationBarriersPolicyUpdatedEvent | AccountVanityUrlApprovedEvent | AccountSettingsUpdatedEvent; -declare class AccountsEventProcessor extends EventManager { -} - -/** - * Credentials for access token & refresh token, which are used to access Zoom's APIs. - * - * As access token is short-lived (usually a single hour), its expiration time is checked - * first. If it's possible to use the access token, it's used; however, if it has expired - * or is close to expiring, the refresh token should be used to generate a new access token - * before the API call is made. Refresh tokens are generally valid for 90 days. - * - * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} - * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. - * It's likely that this error will be rare, but it _can_ be thrown. - */ -interface OAuthToken { - accessToken: string; - expirationTimeIso: string; - refreshToken: string; - scopes: string[]; -} -declare class OAuth extends InteractiveAuth { - private assertResponseAccessToken; - private fetchAccessToken; - getToken(): Promise; - initRedirectCode(code: string): Promise; - private mapOAuthToken; - private refreshAccessToken; + old_object?: { + id?: string; + settings?: { + schedule_meeting?: { + host_video?: boolean; + participant_video?: boolean; + audio_type?: string; + join_before_host?: boolean; + enforce_login?: boolean; + enforce_login_with_domains?: boolean; + enforce_login_domains?: string; + not_store_meeting_topic?: boolean; + force_pmi_jbh_password?: boolean; + use_pmi_for_scheduled_meetings?: boolean; + pstn_password_protected?: string; + jbh_time?: 0 | 5 | 10 | 15; + personal_meeting?: boolean; + require_password_for_instant_meetings?: boolean; + mute_upon_entry?: boolean; + require_password_for_pmi_meetings?: "jbh_only" | "all" | "none"; + use_pmi_for_instant_meetings?: boolean; + require_password_for_scheduling_new_meetings?: boolean; + pmi_password?: string; + upcoming_meeting_reminder?: boolean; + always_display_zoom_meeting_as_topic?: { + enable?: boolean; + display_topic_for_scheduled_meetings?: boolean; + }; + hide_meeting_description?: { + enable?: boolean; + hide_description_for_scheduled_meetings?: boolean; + }; + always_display_zoom_webinar_as_topic?: { + enable?: boolean; + display_topic_for_scheduled_webinars?: boolean; + }; + hide_webinar_description?: { + enable?: boolean; + hide_description_for_scheduled_webinars?: boolean; + }; + }; + in_meeting?: { + e2e_encryption?: boolean; + chat?: boolean; + allow_users_to_delete_messages_in_meeting_chat?: boolean; + allow_participants_chat_with?: 1 | 2 | 3 | 4; + allow_users_save_chats?: 1 | 2 | 3; + private_chat?: boolean; + auto_saving_chat?: boolean; + file_transfer?: boolean; + feedback?: boolean; + post_meeting_feedback?: boolean; + co_host?: boolean; + polling?: boolean; + meeting_polling?: { + enable?: boolean; + advanced_polls?: boolean; + require_answers_to_be_anonymous?: boolean; + allow_alternative_host_to_add_edit?: boolean; + manage_saved_polls_and_quizzes?: boolean; + allow_host_to_upload_image?: boolean; + }; + attendee_on_hold?: boolean; + show_meeting_control_toolbar?: boolean; + allow_show_zoom_windows?: boolean; + annotation?: boolean; + whiteboard?: boolean; + webinar_question_answer?: boolean; + meeting_question_answer?: boolean; + anonymous_question_answer?: boolean; + breakout_room?: boolean; + breakout_room_schedule?: boolean; + closed_caption?: boolean; + far_end_camera_control?: boolean; + group_hd?: boolean; + virtual_background?: boolean; + watermark?: boolean; + watermark_by_default?: boolean; + audio_watermark_by_default?: boolean; + attention_mode_focus_mode?: boolean; + allow_host_to_enable_focus_mode?: boolean; + alert_guest_join?: boolean; + auto_answer?: boolean; + p2p_connetion?: boolean; + p2p_ports?: boolean; + ports_range?: string; + sending_default_email_invites?: boolean; + use_html_format_email?: boolean; + dscp_marking?: boolean; + dscp_audio?: number; + dscp_video?: number; + stereo_audio?: boolean; + original_audio?: boolean; + screen_sharing?: boolean; + remote_control?: boolean; + non_verbal_feedback?: boolean; + remote_support?: boolean; + custom_data_center_regions?: boolean; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; + language_interpretation?: { + enable?: boolean; + enable_language_interpretation_by_default?: boolean; + allow_participants_to_speak_in_listening_channel?: boolean; + allow_up_to_25_custom_languages_when_scheduling_meetings?: boolean; + languages?: string[]; + custom_languages?: string[]; + }; + sign_language_interpretation?: { + enable?: boolean; + enable_sign_language_interpretation_by_default?: boolean; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; + custom_languages?: string[]; + }; + meeting_reactions?: boolean; + meeting_reactions_emojis?: "all" | "selected"; + allow_host_panelists_to_use_audible_clap?: boolean; + webinar_reactions?: boolean; + show_a_join_from_your_browser_link?: boolean; + join_from_mobile?: boolean; + join_from_desktop?: boolean; + allow_live_streaming?: boolean; + live_streaming_facebook?: boolean; + workplace_by_facebook?: boolean; + live_streaming_youtube?: boolean; + custom_live_streaming_service?: boolean; + custom_service_instructions?: string; + webinar_live_streaming?: { + enable?: boolean; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; + custom_service_instructions?: string; + live_streaming_reminder?: boolean; + }; + webinar_chat?: { + enable?: boolean; + allow_panelists_chat_with?: 1 | 2; + allow_attendees_chat_with?: 1 | 2 | 3; + default_attendees_chat_with?: 1 | 2; + allow_panelists_send_direct_message?: boolean; + allow_users_save_chats?: 0 | 1 | 2; + allow_auto_save_local_chat_file?: boolean; + }; + closed_captioning?: { + enable?: boolean; + third_party_captioning_service?: boolean; + auto_transcribing?: boolean; + view_full_transcript?: boolean; + save_caption?: boolean; + }; + slide_control?: boolean; + meeting_survey?: boolean; + webinar_polling?: { + enable?: boolean; + advanced_polls?: boolean; + require_answers_to_be_anonymous?: boolean; + allow_alternative_host_to_add_edit?: boolean; + manage_saved_polls_and_quizzes?: boolean; + allow_host_to_upload_image?: boolean; + }; + webinar_survey?: boolean; + disable_screen_sharing_for_host_meetings?: boolean; + disable_screen_sharing_for_in_meeting_guests?: boolean; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; + }; + email_notification?: { + cloud_recording_available_reminder?: boolean; + recording_available_reminder_schedulers?: boolean; + recording_available_reminder_alternative_hosts?: boolean; + jbh_reminder?: boolean; + cancel_meeting_reminder?: boolean; + low_host_count_reminder?: boolean; + alternative_host_reminder?: boolean; + schedule_for_reminder?: boolean; + }; + zoom_rooms?: { + upcoming_meeting_alert?: boolean; + start_airplay_manually?: boolean; + weekly_system_restart?: boolean; + list_meetings_with_calendar?: boolean; + zr_post_meeting_feedback?: boolean; + ultrasonic?: boolean; + force_private_meeting?: boolean; + hide_host_information?: boolean; + cmr_for_instant_meeting?: boolean; + auto_start_stop_scheduled_meetings?: boolean; + }; + security?: { + admin_change_name_pic?: boolean; + import_photos_from_devices?: boolean; + hide_billing_info?: boolean; + password_requirement?: { + minimum_password_length?: number; + have_special_character?: boolean; + consecutive_characters_length?: number; + weak_enhance_detection?: boolean; + }; + signin_with_sso?: { + enable?: boolean; + require_sso_for_domains?: boolean; + domains?: string[]; + sso_bypass_users?: { + id?: string; + email?: string; + }[]; + }; + }; + recording?: { + local_recording?: boolean; + cloud_recording?: boolean; + record_speaker_view?: boolean; + record_gallery_view?: boolean; + record_audio_file?: boolean; + save_chat_text?: boolean; + show_timestamp?: boolean; + recording_audio_transcript?: boolean; + auto_recording?: string; + cloud_recording_download?: boolean; + cloud_recording_download_host?: boolean; + account_user_access_recording?: boolean; + auto_delete_cmr?: boolean; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; + record_files_separately?: { + active_speaker?: boolean; + gallery_view?: boolean; + shared_screen?: boolean; + }; + display_participant_name?: boolean; + recording_thumbnails?: boolean; + optimize_recording_for_3rd_party_video_editor?: boolean; + recording_highlight?: boolean; + smart_recording?: { + create_recording_highlights?: boolean; + create_smart_chapters?: boolean; + create_next_steps?: boolean; + }; + save_panelist_chat?: boolean; + save_poll_results?: boolean; + save_close_caption?: boolean; + record_audio_file_each_participant?: boolean; + host_pause_stop_recording?: boolean; + recording_disclaimer?: boolean; + ask_participants_to_consent_disclaimer?: boolean; + ask_host_to_confirm_disclaimer?: boolean; + recording_password_requirement?: { + length?: number; + have_letter?: boolean; + have_number?: boolean; + have_special_character?: boolean; + only_allow_numeric?: boolean; + }; + ip_address_access_control?: { + enable?: boolean; + ip_addresses_or_ranges?: string; + }; + prevent_host_access_recording?: boolean; + host_delete_cloud_recording?: boolean; + allow_recovery_deleted_cloud_recordings?: boolean; + archive?: { + enable?: boolean; + type?: 1 | 2 | 3; + settings?: { + chat_file?: boolean; + chat_with_sender_email?: boolean; + audio_file?: boolean; + video_file?: boolean; + cc_transcript_file?: boolean; + chat_with_direct_message?: never; + archive_retention?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30; + action_when_archive_failed?: 1 | 2; + notification_when_archiving_starts?: "participants" | "guest"; + play_voice_prompt_when_archiving_starts?: "participants" | "guest" | "none"; + }; + }; + }; + telephony?: { + third_party_audio?: boolean; + audio_conference_info?: string; + telephony_regions?: { + allowed_values?: string[]; + selection_values?: string; + }; + }; + integration?: { + google_calendar?: boolean; + google_drive?: boolean; + dropbox?: boolean; + box?: boolean; + microsoft_one_drive?: boolean; + kubi?: boolean; + }; + feature?: { + meeting_capacity?: number; + }; + other_options?: { + allow_auto_active_users?: boolean; + blur_snapshot?: boolean; + display_meetings_scheduled_for_others?: boolean; + use_cdn?: "none" | "default" | "wangsu"; + allow_users_contact_support_via_chat?: boolean; + show_one_user_meeting_on_dashboard?: boolean; + meeting_qos_and_mos?: 0 | 1 | 2 | 3; + allow_users_enter_and_share_pronouns?: boolean; + webinar_registration_options?: { + allow_host_to_enable_social_share_buttons?: boolean; + }; + }; + audio_conferencing?: { + toll_free_and_fee_based_toll_call?: { + enable?: boolean; + numbers?: { + code?: string; + country_code?: string; + country_name?: string; + number?: string; + display_number?: string; + }[]; + allow_webinar_attendees_dial?: boolean; + }; + }; + chat?: { + allow_bots_chat?: boolean; + share_files?: { + enable?: boolean; + share_option?: "disable" | "anyone" | "account" | "organization"; + view_option?: "anyone" | "account" | "organization"; + restrictions?: { + only_allow_specific_file_types?: boolean; + file_type_restrictions?: (".gz" | ".rar" | ".zip" | ".xls" | ".xlsx" | ".json" | ".png" | ".pptx" | ".ppt" | ".7z" | ".xmind" | ".pdf" | ".pps" | ".txt" | ".docx" | ".doc")[]; + file_type_restrictions_for_external?: (".gz" | ".rar" | ".zip" | ".xls" | ".xlsx" | ".json" | ".png" | ".pptx" | ".ppt" | ".7z" | ".xmind" | ".pdf" | ".pps" | ".txt" | ".docx" | ".doc")[]; + maximum_file_size?: boolean; + file_size_restrictions?: 50 | 100 | 200 | 300 | 400 | 500; + file_size_restrictions_for_external?: 50 | 100 | 200 | 300 | 400 | 500; + file_restrictions_apply_to?: "sharing_and_viewing" | "sharing"; + }; + }; + chat_emojis?: { + enable?: boolean; + emojis_option?: "all" | "selected"; + }; + record_voice_messages?: boolean; + record_video_messages?: boolean; + screen_capture?: boolean; + create_public_channels?: boolean; + create_private_channels?: boolean; + create_group_chat?: boolean; + share_links_in_chat?: boolean; + schedule_meetings_in_chat?: boolean; + set_retention_period_in_cloud?: { + enable?: boolean; + retention_period_of_direct_messages_and_group_conversation?: string; + retention_period_of_channels?: string; + }; + set_retention_period_in_local?: { + enable?: boolean; + retention_period_of_direct_messages_and_group_conversation?: string; + retention_period_of_channels?: string; + }; + allow_users_to_add_contacts?: { + enable?: boolean; + selected_option?: 1 | 2 | 3 | 4; + user_email_addresses?: string; + }; + allow_users_to_chat_with_others?: { + enable?: boolean; + selected_option?: 1 | 2 | 3 | 4; + user_email_addresses?: string; + }; + chat_etiquette_tool?: { + enable?: boolean; + policies?: { + description?: string; + id?: string; + is_locked?: boolean; + keywords?: string[]; + name?: string; + regular_expression?: string; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; + }[]; + policy_max_count?: number; + }; + send_data_to_third_party_archiving_service?: { + enable?: boolean; + type?: "global_relay" | "smarsh"; + smtp_delivery_address?: string; + user_name?: string; + passcode?: string; + authorized_channel_token?: string; + }; + apply_local_storage_to_personal_channel?: { + enable?: boolean; + retention_period?: string; + }; + translate_messages?: boolean; + search_and_send_animated_gif_images?: { + enable?: boolean; + giphy_content_rating?: 1 | 2 | 3 | 4; + }; + external_collab_restrict?: { + enable?: boolean; + external_chat?: "allowed" | "not_allowed"; + group_id?: string; + }; + external_user_control?: { + enable?: boolean; + selected_option?: 1 | 2 | 3; + external_account?: boolean; + }; + external_invite_approve?: { + enable?: boolean; + selected_option?: 1 | 2; + channel_id?: string; + external_account?: boolean; + }; + external_member_join?: { + enable?: boolean; + external_account?: boolean; + }; + external_join_approve?: { + enable?: boolean; + selected_option?: 1 | 2; + channel_id?: string; + external_account?: boolean; + }; + download_file?: boolean; + share_screen_in_chat?: boolean; + code_snippet?: boolean; + personal_channel?: boolean; + store_revise_chat?: boolean; + set_chat_as_default_tab?: boolean; + hyper_link?: boolean; + suppress_removal_notification?: boolean; + suppress_user_group_notification?: boolean; + allow_remove_msg_by_owner_and_admins?: boolean; + allow_huddles_from_channels?: boolean; + shared_spaces?: boolean; + chat_email_address?: { + enable?: boolean; + only_allow_specific_domains?: boolean; + specific_domains?: string[]; + }; + read_receipts?: { + enable?: boolean; + allow_users_opt_out?: boolean; + }; + allow_delete_message?: { + enable?: boolean; + time?: 0 | 5 | 30 | 60 | 1440 | 10080; + }; + allow_edit_message?: { + enable?: boolean; + time?: 0 | 5 | 30 | 60 | 1440 | 10080; + }; + show_status_to_internal_contact?: boolean; + presence_on_meeting?: boolean; + presence_away_when_screen_saver?: boolean; + show_h323_contact_tab?: boolean; + ai_summary?: { + enable?: boolean; + shown_in_team_chat?: boolean; + }; + ai_compose?: { + enable?: boolean; + shown_in_team_chat?: boolean; + }; + ai_recommend?: boolean; + ai_quick_reply?: boolean; + ai_sentence_completion?: boolean; + ai_quick_schedule?: boolean; + survey_poll?: boolean; + type?: never; + }; + meeting_security?: { + auto_security?: boolean; + waiting_room?: boolean; + waiting_room_settings?: { + participants_to_place_in_waiting_room?: 0 | 1 | 2; + whitelisted_domains_for_waiting_room?: string; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; + }; + meeting_password?: boolean; + require_password_for_scheduled_meeting?: boolean; + pmi_password?: boolean; + phone_password?: boolean; + webinar_password?: boolean; + require_password_for_scheduled_webinar?: boolean; + meeting_password_requirement?: { + length?: number; + have_letter?: boolean; + have_number?: boolean; + have_special_character?: boolean; + only_allow_numeric?: boolean; + have_upper_and_lower_characters?: boolean; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; + weak_enhance_detection?: boolean; + }; + embed_password_in_join_link?: boolean; + end_to_end_encrypted_meetings?: boolean; + encryption_type?: "enhanced_encryption" | "e2ee"; + block_user_domain?: boolean; + block_user_domain_list?: string[]; + only_authenticated_can_join_from_webclient?: boolean; + chat_etiquette_tool?: { + enable?: boolean; + policies?: { + id?: string; + name?: string; + description?: string; + trigger_action?: 1 | 2; + keywords?: string[]; + regular_expression?: string; + status?: "activated" | "deactivated"; + is_locked?: boolean; + }[]; + }; + }; + in_session?: { + custom_data_center_regions?: boolean; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; + p2p_connetion?: boolean; + p2p_ports?: boolean; + ports_range?: string; + dscp_audio?: number; + dscp_marking?: boolean; + dscp_video?: number; + dscp_dual?: boolean; + subsession?: boolean; + }; + session_security?: { + approved_or_denied_countries_or_regions?: { + approved_list?: string[]; + denied_list?: string[]; + enable?: boolean; + method?: "approve" | "deny"; + }; + }; + }; + }; + }; +}; +type AccountsEvents = AccountVanityUrlRejectedEvent | AccountCreatedEvent | InformationBarriersPolicyDeletedEvent | AccountUpdatedEvent | InformationBarriersPolicyCreatedEvent | AccountLockSettingsUpdatedEvent | AccountDisassociatedEvent | InformationBarriersPolicyUpdatedEvent | AccountVanityUrlApprovedEvent | AccountSettingsUpdatedEvent; +declare class AccountsEventProcessor extends EventManager { } type AccountsOptions = CommonClientOptions; @@ -5656,4 +6836,5 @@ declare class AccountsS2SAuthClient typeof obj.installerOptions.redirectUri !== "undefined" && typeof obj.installerOptions.stateStore !== "undefined"; @@ -450,7 +453,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -460,14 +466,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -579,9 +591,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -595,6 +604,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? createServer : createServer$1; } @@ -609,8 +631,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -627,69 +661,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -723,18 +772,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -743,87 +798,13 @@ class HttpReceiver { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -852,10 +833,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -1001,6 +995,12 @@ class AccountsEndpoints extends WebEndpoints { urlPathBuilder: ({ roleId, memberId }) => `/roles/${roleId}/members/${memberId}` }) }; + surveyManagement = { + getSurveys: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/surveys` }), + getSurveyInfo: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ surveyId }) => `/surveys/${surveyId}` }), + getSurveyAnswers: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ surveyId }) => `/surveys/${surveyId}/answers` }), + getSurveyInstances: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ surveyId }) => `/surveys/${surveyId}/instances` }) + }; } class AccountsEventProcessor extends EventManager { @@ -1090,7 +1090,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -1111,7 +1113,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. diff --git a/chatbot/chatbot.cjs b/chatbot/chatbot.cjs index d582ad4..142e435 100644 --- a/chatbot/chatbot.cjs +++ b/chatbot/chatbot.cjs @@ -426,6 +426,9 @@ class JwtStateStore { const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; /** * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication @@ -450,7 +453,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -460,14 +466,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -579,9 +591,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -595,6 +604,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? node_https.createServer : node_http.createServer; } @@ -609,8 +631,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -627,69 +661,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, exports.StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, exports.StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -723,18 +772,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(exports.StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -794,7 +849,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -815,7 +872,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. @@ -823,87 +880,13 @@ class ProductClient { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -932,10 +915,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${node_path.basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", diff --git a/chatbot/chatbot.d.ts b/chatbot/chatbot.d.ts index 1a8c162..298b6a4 100644 --- a/chatbot/chatbot.d.ts +++ b/chatbot/chatbot.d.ts @@ -1,88 +1,8 @@ -import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { AxiosResponse } from 'axios'; +import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { Server } from 'node:http'; import { ServerOptions } from 'node:https'; -type AllPropsOptional = Exclude<{ - [P in keyof T]: undefined extends T[P] ? True : False; -}[keyof T], undefined> extends True ? True : False; -type Constructor = new (...args: any[]) => T; -type MaybeArray = T | T[]; -type MaybePromise = T | Promise; -type StringIndexed = Record; - -/** - * {@link StateStore} defines methods for generating and verifying OAuth state. - * - * This interface is implemented internally for the default state store; however, - * it can also be implemented and passed to an OAuth client as well. - */ -interface StateStore { - /** - * Generate a new state string, which is directly appended to the OAuth `state` parameter. - */ - generateState(): MaybePromise; - /** - * Verify that the state received during OAuth callback is valid and not forged. - * - * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. - * - * @param state The state parameter that was received during OAuth callback - */ - verifyState(state: string): MaybePromise; -} -/** - * Guard if an object implements the {@link StateStore} interface — most notably, - * `generateState()` and `verifyState(state: string)`. - */ -declare const isStateStore: (obj: unknown) => obj is StateStore; - -interface TokenStore { - getLatestToken(): MaybePromise; - storeToken(token: Token): MaybePromise; -} - -interface RivetError extends Error { - readonly errorCode: ErrorCode; -} - -declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ - readonly ApiResponseError: "zoom_rivet_api_response_error"; - readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; - readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; - readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; - readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; - readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; - readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; - readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; - readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; - readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; - readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; - readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; - readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; - readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; - readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; - readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; - readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; -}[K]>; -declare const ApiResponseError: Constructor; -declare const AwsReceiverRequestError: Constructor; -declare const ClientCredentialsRawResponseError: Constructor; -declare const S2SRawResponseError: Constructor; -declare const CommonHttpRequestError: Constructor; -declare const ReceiverInconsistentStateError: Constructor; -declare const ReceiverOAuthFlowError: Constructor; -declare const HTTPReceiverConstructionError: Constructor; -declare const HTTPReceiverPortNotNumberError: Constructor; -declare const HTTPReceiverRequestError: Constructor; -declare const OAuthInstallerNotInitializedError: Constructor; -declare const OAuthTokenDoesNotExistError: Constructor; -declare const OAuthTokenFetchFailedError: Constructor; -declare const OAuthTokenRawResponseError: Constructor; -declare const OAuthTokenRefreshFailedError: Constructor; -declare const OAuthStateVerificationFailedError: Constructor; -declare const ProductClientConstructionError: Constructor; - declare enum LogLevel { ERROR = "error", WARN = "warn", @@ -142,6 +62,19 @@ declare class ConsoleLogger implements Logger { private static isMoreOrEqualSevere; } +type AllPropsOptional = Exclude<{ + [P in keyof T]: undefined extends T[P] ? True : False; +}[keyof T], undefined> extends True ? True : False; +type Constructor = new (...args: any[]) => T; +type MaybeArray = T | T[]; +type MaybePromise = T | Promise; +type StringIndexed = Record; + +interface TokenStore { + getLatestToken(): MaybePromise; + storeToken(token: Token): MaybePromise; +} + interface AuthOptions { clientId: string; clientSecret: string; @@ -187,6 +120,23 @@ declare abstract class Auth { }>, "grant_type">): Promise; } +interface ClientCredentialsToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} +declare class ClientCredentialsAuth extends Auth { + private assertRawToken; + private fetchClientCredentials; + getToken(): Promise; + private mapClientCredentials; +} + +interface JwtToken { + token: string; + expirationTimeIso: string; +} + interface S2SAuthToken { accessToken: string; expirationTimeIso: string; @@ -225,12 +175,31 @@ declare class EventManager { protected withContext, Context>(): ContextListener; } +declare enum StatusCode { + OK = 200, + TEMPORARY_REDIRECT = 302, + BAD_REQUEST = 400, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + INTERNAL_SERVER_ERROR = 500 +} +interface ReceiverInitOptions { + eventEmitter?: GenericEventManager | undefined; + interactiveAuth?: InteractiveAuth | undefined; +} +interface Receiver { + canInstall(): true | false; + init(options: ReceiverInitOptions): void; + start(...args: any[]): MaybePromise; + stop(...args: any[]): MaybePromise; +} + interface HttpReceiverOptions extends Partial { endpoints?: MaybeArray | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; port?: number | string | undefined; - webhooksSecretToken: string; - logger?: Logger; - logLevel?: LogLevel; + webhooksSecretToken?: string | undefined; } type SecureServerOptions = { [K in (typeof secureServerOptionKeys)[number]]: ServerOptions[K]; @@ -243,10 +212,15 @@ declare class HttpReceiver implements Receiver { private logger; constructor(options: HttpReceiverOptions); canInstall(): true; + private buildDeletedStateCookieHeader; + private buildStateCookieHeader; + private getRequestCookie; private getServerCreator; private hasEndpoint; private hasSecureOptions; init({ eventEmitter, interactiveAuth }: ReceiverInitOptions): void; + private setResponseCookie; + private areNormalizedUrlsEqual; start(port?: number | string): Promise; stop(): Promise; private writeTemporaryRedirect; @@ -269,6 +243,7 @@ interface WebEndpointOptions { baseUrl?: string | undefined; doubleEncodeUrl?: boolean | undefined; timeout?: number | undefined; + userAgentName?: string | undefined; } type EndpointArguments = (PathSchema extends NoParams ? object : AllPropsOptional extends "t" ? { path?: PathSchema; @@ -290,6 +265,7 @@ declare class WebEndpoints { constructor(options: WebEndpointOptions); protected buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }: BuildEndpointOptions): (_: EndpointArguments) => Promise>; private buildUserAgent; + private getCustomUserAgentName; private getHeaders; private getRequestBody; private isOk; @@ -297,7 +273,7 @@ declare class WebEndpoints { private makeRequest; } -type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & { +type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & Pick & { disableReceiver?: boolean | undefined; logger?: Logger | undefined; logLevel?: LogLevel | undefined; @@ -305,38 +281,68 @@ type CommonClientOptions = GetAuthOptions interface ClientReceiverOptions { receiver: R; } -type ClientConstructorOptions, R extends Receiver> = IsReceiverDisabled extends true ? O : O & (ClientReceiverOptions | HttpReceiverOptions); +type ClientConstructorOptions, R extends Receiver> = (O & { + disableReceiver: true; +}) | (O & (ClientReceiverOptions | HttpReceiverOptions)); type ExtractInstallerOptions = A extends InteractiveAuth ? [ ReturnType ] extends [true] ? WideInstallerOptions : object : object; type ExtractAuthTokenType = A extends Auth ? T : never; -type GenericClientOptions = CommonClientOptions; type GetAuthOptions = AuthOptions> & (A extends S2SAuth ? S2SAuthOptions : object); -type IsReceiverDisabled> = [ - O["disableReceiver"] -] extends [true] ? true : false; type WideInstallerOptions = { installerOptions: InstallerOptions; }; declare abstract class ProductClient, ReceiverType extends Receiver> { private readonly auth; readonly endpoints: EndpointsType; - readonly webEventConsumer: EventProcessorType; + readonly webEventConsumer?: EventProcessorType | undefined; private readonly receiver?; constructor(options: ClientConstructorOptions); protected abstract initAuth(options: OptionsType): AuthType; protected abstract initEndpoints(auth: AuthType, options: OptionsType): EndpointsType; - protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType; + protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType | undefined; private initDefaultReceiver; - start(this: IsReceiverDisabled extends true ? never : this): Promise>; + start(): Promise>; +} + +/** + * {@link StateStore} defines methods for generating and verifying OAuth state. + * + * This interface is implemented internally for the default state store; however, + * it can also be implemented and passed to an OAuth client as well. + */ +interface StateStore { + /** + * Generate a new state string, which is directly appended to the OAuth `state` parameter. + */ + generateState(): MaybePromise; + /** + * Verify that the state received during OAuth callback is valid and not forged. + * + * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. + * + * @param state The state parameter that was received during OAuth callback + */ + verifyState(state: string): MaybePromise; } +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +declare const isStateStore: (obj: unknown) => obj is StateStore; +interface AuthorizationUrlResult { + fullUrl: string; + generatedState: string; +} interface InstallerOptions { directInstall?: boolean | undefined; installPath?: string | undefined; redirectUri: string; redirectUriPath?: string | undefined; stateStore: StateStore | string; + stateCookieName?: string | undefined; + stateCookieMaxAge?: number | undefined; } /** * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication @@ -350,36 +356,79 @@ interface InstallerOptions { */ declare abstract class InteractiveAuth extends Auth { installerOptions?: ReturnType; - getAuthorizationUrl(): Promise; + getAuthorizationUrl(): Promise; getFullRedirectUri(): string; - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }: InstallerOptions): { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }: InstallerOptions): { directInstall: boolean; installPath: string; redirectUri: string; redirectUriPath: string; stateStore: StateStore; + stateCookieName: string; + stateCookieMaxAge: number; }; } -declare enum StatusCode { - OK = 200, - TEMPORARY_REDIRECT = 302, - BAD_REQUEST = 400, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - INTERNAL_SERVER_ERROR = 500 -} -interface ReceiverInitOptions { - eventEmitter: GenericEventManager; - interactiveAuth?: InteractiveAuth | undefined; +/** + * Credentials for access token & refresh token, which are used to access Zoom's APIs. + * + * As access token is short-lived (usually a single hour), its expiration time is checked + * first. If it's possible to use the access token, it's used; however, if it has expired + * or is close to expiring, the refresh token should be used to generate a new access token + * before the API call is made. Refresh tokens are generally valid for 90 days. + * + * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} + * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. + * It's likely that this error will be rare, but it _can_ be thrown. + */ +interface OAuthToken { + accessToken: string; + expirationTimeIso: string; + refreshToken: string; + scopes: string[]; } -interface Receiver { - canInstall(): true | false; - init(options: ReceiverInitOptions): void; - start(...args: any[]): MaybePromise; - stop(...args: any[]): MaybePromise; + +interface RivetError extends Error { + readonly errorCode: ErrorCode; } +declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ + readonly ApiResponseError: "zoom_rivet_api_response_error"; + readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; + readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; + readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; + readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; + readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; + readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; + readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; + readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; + readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; + readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; + readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; + readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; + readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; + readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; + readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; + readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; +}[K]>; +declare const ApiResponseError: Constructor; +declare const AwsReceiverRequestError: Constructor; +declare const ClientCredentialsRawResponseError: Constructor; +declare const S2SRawResponseError: Constructor; +declare const CommonHttpRequestError: Constructor; +declare const ReceiverInconsistentStateError: Constructor; +declare const ReceiverOAuthFlowError: Constructor; +declare const HTTPReceiverConstructionError: Constructor; +declare const HTTPReceiverPortNotNumberError: Constructor; +declare const HTTPReceiverRequestError: Constructor; +declare const OAuthInstallerNotInitializedError: Constructor; +declare const OAuthTokenDoesNotExistError: Constructor; +declare const OAuthTokenFetchFailedError: Constructor; +declare const OAuthTokenRawResponseError: Constructor; +declare const OAuthTokenRefreshFailedError: Constructor; +declare const OAuthStateVerificationFailedError: Constructor; +declare const ProductClientConstructionError: Constructor; + interface AwsLambdaReceiverOptions { webhooksSecretToken: string; } @@ -394,18 +443,6 @@ declare class AwsLambdaReceiver implements Receiver { stop(): Promise; } -interface ClientCredentialsToken { - accessToken: string; - expirationTimeIso: string; - scopes: string[]; -} -declare class ClientCredentialsAuth extends Auth { - private assertRawToken; - private fetchClientCredentials; - getToken(): Promise; - private mapClientCredentials; -} - interface CardContent { settings?: any; head?: Header; @@ -848,4 +885,5 @@ declare class ChatbotClient MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -577,9 +589,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -593,6 +602,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? createServer : createServer$1; } @@ -607,8 +629,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -625,69 +659,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -721,18 +770,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -792,7 +847,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -813,7 +870,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. @@ -821,87 +878,13 @@ class ProductClient { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -930,10 +913,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", diff --git a/commerce/commerce.cjs b/commerce/commerce.cjs new file mode 100644 index 0000000..58a3c91 --- /dev/null +++ b/commerce/commerce.cjs @@ -0,0 +1,1039 @@ +'use strict'; + +var node_crypto = require('node:crypto'); +var node_http = require('node:http'); +var node_https = require('node:https'); +var axios = require('axios'); +var dayjs = require('dayjs'); +var node_buffer = require('node:buffer'); +var jose = require('jose'); +var FormData = require('form-data'); +var os = require('node:os'); +var node_path = require('node:path'); + +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +const isStateStore = (obj) => typeof obj.generateState === "function" && typeof obj.verifyState === "function"; + +const createRivetErrors = (errors) => ({ + createError: (errorCode) => class extends Error { + errorCode = errors[errorCode]; + constructor(message, options) { + const errorMessage = (message ?? + (options?.cause instanceof Error ? options.cause.message : errorCode)); + super(errorMessage, options); + this.name = errorCode; + Object.setPrototypeOf(this, new.target.prototype); + } + }, + isError: (obj, key) => key ? + Object.keys(errors).some((code) => code === key) && + typeof obj.errorCode === "string" && + obj.errorCode === errors[key] + : typeof obj.errorCode === "string" +}); + +const coreErrors = { + ApiResponseError: "zoom_rivet_api_response_error", + AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error", + ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error", + S2SRawResponseError: "zoom_rivet_s2s_raw_response_error", + CommonHttpRequestError: "zoom_rivet_common_http_request_error", + ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error", + ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error", + HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error", + HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error", + HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error", + OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error", + OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error", + OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error", + OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error", + OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error", + OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error", + ProductClientConstructionError: "zoom_rivet_product_client_construction_error" +}; +const { createError: createCoreError, isError: isCoreError } = createRivetErrors(coreErrors); +const ApiResponseError = createCoreError("ApiResponseError"); +const AwsReceiverRequestError = createCoreError("AwsReceiverRequestError"); +const ClientCredentialsRawResponseError = createCoreError("ClientCredentialsRawResponseError"); +const S2SRawResponseError = createCoreError("S2SRawResponseError"); +const CommonHttpRequestError = createCoreError("CommonHttpRequestError"); +const ReceiverInconsistentStateError = createCoreError("ReceiverInconsistentStateError"); +const ReceiverOAuthFlowError = createCoreError("ReceiverOAuthFlowError"); +const HTTPReceiverConstructionError = createCoreError("HTTPReceiverConstructionError"); +const HTTPReceiverPortNotNumberError = createCoreError("HTTPReceiverPortNotNumberError"); +const HTTPReceiverRequestError = createCoreError("HTTPReceiverRequestError"); +const OAuthInstallerNotInitializedError = createCoreError("OAuthInstallerNotInitializedError"); +const OAuthTokenDoesNotExistError = createCoreError("OAuthTokenDoesNotExistError"); +const OAuthTokenFetchFailedError = createCoreError("OAuthTokenFetchFailedError"); +const OAuthTokenRawResponseError = createCoreError("OAuthTokenRawResponseError"); +const OAuthTokenRefreshFailedError = createCoreError("OAuthTokenRefreshFailedError"); +const OAuthStateVerificationFailedError = createCoreError("OAuthStateVerificationFailedError"); +const ProductClientConstructionError = createCoreError("ProductClientConstructionError"); + +exports.LogLevel = void 0; +(function (LogLevel) { + LogLevel["ERROR"] = "error"; + LogLevel["WARN"] = "warn"; + LogLevel["INFO"] = "info"; + LogLevel["DEBUG"] = "debug"; +})(exports.LogLevel || (exports.LogLevel = {})); +class ConsoleLogger { + level; + name; + static labels = (() => { + const entries = Object.entries(exports.LogLevel); + const map = entries.map(([key, value]) => [value, `[${key}] `]); + return new Map(map); + })(); + static severity = { + [exports.LogLevel.ERROR]: 400, + [exports.LogLevel.WARN]: 300, + [exports.LogLevel.INFO]: 200, + [exports.LogLevel.DEBUG]: 100 + }; + constructor() { + this.level = exports.LogLevel.INFO; + this.name = ""; + } + getLevel() { + return this.level; + } + setLevel(level) { + this.level = level; + } + setName(name) { + this.name = name; + } + debug(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(exports.LogLevel.DEBUG, this.level)) { + console.debug(ConsoleLogger.labels.get(exports.LogLevel.DEBUG), this.name, ...msg); + } + } + info(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(exports.LogLevel.INFO, this.level)) { + console.info(ConsoleLogger.labels.get(exports.LogLevel.INFO), this.name, ...msg); + } + } + warn(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(exports.LogLevel.WARN, this.level)) { + console.warn(ConsoleLogger.labels.get(exports.LogLevel.WARN), this.name, ...msg); + } + } + error(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(exports.LogLevel.ERROR, this.level)) { + console.error(ConsoleLogger.labels.get(exports.LogLevel.ERROR), this.name, ...msg); + } + } + static isMoreOrEqualSevere(a, b) { + return ConsoleLogger.severity[a] >= ConsoleLogger.severity[b]; + } +} + +/** @internal */ +const hashUrlValidationEvent = ({ payload: { plainToken } }, webhooksSecretToken) => ({ + encryptedToken: node_crypto.createHmac("sha256", webhooksSecretToken).update(plainToken).digest("hex"), + plainToken +}); +const isHashedUrlValidation = (obj) => typeof obj.encryptedToken === "string" && + typeof obj.plainToken === "string"; +const isRawUrlValidationEvent = (obj) => obj.event === "endpoint.url_validation" && typeof obj.payload.plainToken === "string"; +const isSkeletonEvent = (obj) => typeof obj.event === "string"; +class CommonHttpRequest { + headers; + payload; + webhooksSecretToken; + constructor(headers, payload, webhooksSecretToken) { + this.headers = headers; + this.payload = payload; + this.webhooksSecretToken = webhooksSecretToken; + } + static buildFromAwsEvent({ body, headers, isBase64Encoded }, webhooksSecretToken) { + try { + const rawBody = body ?? ""; + const decodedBody = isBase64Encoded ? Buffer.from(rawBody, "base64").toString("ascii") : rawBody; + const payload = JSON.parse(decodedBody); + return new CommonHttpRequest(headers, payload, webhooksSecretToken); + } + catch (err) { + throw err instanceof SyntaxError ? + new CommonHttpRequestError("Failed to parse payload string to JSON.", err) + : err; + } + } + static async buildFromIncomingMessage(incomingMessage, webhooksSecretToken) { + const bufferAsString = () => { + return new Promise((resolve, reject) => { + const body = []; + incomingMessage.on("data", (chunk) => body.push(chunk)); + incomingMessage.on("error", (err) => { + reject(err); + }); + incomingMessage.on("end", () => { + resolve(Buffer.concat(body).toString()); + }); + }); + }; + try { + const payload = JSON.parse(await bufferAsString()); + return new CommonHttpRequest(incomingMessage.headers, payload, webhooksSecretToken); + } + catch (err) { + if (err instanceof SyntaxError) { + throw new CommonHttpRequestError("Failed to parse payload string to JSON.", err); + } + throw err; + } + } + isEventVerified() { + const { signature, requestTimestamp } = this.parseHeaders(); + const messageToVerify = `v0:${requestTimestamp.toString()}:${JSON.stringify(this.payload)}`; + const hashToVerify = node_crypto.createHmac("sha256", this.webhooksSecretToken).update(messageToVerify).digest("hex"); + const signatureToVerify = `v0=${hashToVerify}`; + return signatureToVerify === signature; + } + parseHeaders() { + const findHeader = (header) => { + const foundHeader = Object.keys(this.headers).find((key) => key.toLowerCase() === header.toLowerCase()); + return foundHeader && this.headers[foundHeader]; + }; + const headerSignature = findHeader("x-zm-signature"); + const headerRequestTimestamp = findHeader("x-zm-request-timestamp"); + if (!headerSignature && !headerRequestTimestamp) { + throw new CommonHttpRequestError("Request payload must have signature and request timestamp from Zoom."); + } + return { + signature: headerSignature, + requestTimestamp: Number(headerRequestTimestamp) + }; + } + processEvent() { + if (!isSkeletonEvent(this.payload)) { + throw new CommonHttpRequestError("Request payload structure does not match expected from Zoom."); + } + if (!this.isEventVerified()) { + throw new CommonHttpRequestError("Failed to verify event originated from Zoom."); + } + if (isRawUrlValidationEvent(this.payload)) { + return hashUrlValidationEvent(this.payload, this.webhooksSecretToken); + } + return this.payload; + } +} + +exports.StatusCode = void 0; +(function (StatusCode) { + StatusCode[StatusCode["OK"] = 200] = "OK"; + StatusCode[StatusCode["TEMPORARY_REDIRECT"] = 302] = "TEMPORARY_REDIRECT"; + StatusCode[StatusCode["BAD_REQUEST"] = 400] = "BAD_REQUEST"; + StatusCode[StatusCode["NOT_FOUND"] = 404] = "NOT_FOUND"; + StatusCode[StatusCode["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED"; + StatusCode[StatusCode["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR"; +})(exports.StatusCode || (exports.StatusCode = {})); + +class AwsLambdaReceiver { + eventEmitter; + webhooksSecretToken; + constructor({ webhooksSecretToken }) { + this.webhooksSecretToken = webhooksSecretToken; + } + buildResponse(statusCode, body) { + return { + body: JSON.stringify(body), + headers: { "Content-Type": "application/json" }, + statusCode + }; + } + canInstall() { + return false; + } + init({ eventEmitter }) { + this.eventEmitter = eventEmitter; + } + start() { + return async (event, context) => { + console.debug("Processing Lambda event ", JSON.stringify(event), " with context ", JSON.stringify(context)); + try { + const request = CommonHttpRequest.buildFromAwsEvent(event, this.webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + return this.buildResponse(exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + return this.buildResponse(exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } + } + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + return this.buildResponse(exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + return this.buildResponse(exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } + } + }; + } + async stop() { + return Promise.resolve(); + } +} + +const prependSlashes = (strs) => { + const rawStrs = Array.isArray(strs) ? strs : [strs]; + const mappedStrs = rawStrs.map((rawStr) => (rawStr.startsWith("/") ? rawStr : `/${rawStr}`)); + return (Array.isArray(strs) ? mappedStrs : mappedStrs[0]); +}; + +class TokenMemoryStore { + currentToken; + getLatestToken() { + return this.currentToken; + } + storeToken(token) { + this.currentToken = token; + } +} + +/** @internal */ +const EXPIRATION_DELTA_SECONDS = 60; +/** @internal */ +const OAUTH_BASE_URL = "https://zoom.us"; +/** @internal */ +const OAUTH_TOKEN_PATH = "/oauth/token"; +/** + * {@link Auth} is the base implementation of authentication for Zoom's APIs. + * + * It only requires a `clientId` and `tokenStore`, as these options are shared across + * all authentication implementations, namely OAuth and server-to-server auth (client + * credentials, JWT, and server-to-server OAuth.) + */ +class Auth { + clientId; + clientSecret; + tokenStore; + logger; + constructor({ clientId, clientSecret, tokenStore, logger }) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.tokenStore = tokenStore ?? new TokenMemoryStore(); + this.logger = logger; + } + getBasicAuthorization() { + const clientCredentials = `${this.clientId}:${this.clientSecret}`; + return node_buffer.Buffer.from(clientCredentials).toString("base64"); + } + isAlmostExpired(isoTime) { + const currentDate = dayjs(); + return dayjs(isoTime).diff(currentDate, "seconds") <= EXPIRATION_DELTA_SECONDS; + } + async makeOAuthTokenRequest(grantType, payload) { + return await axios({ + method: "POST", + url: new URL(OAUTH_TOKEN_PATH, OAUTH_BASE_URL).toString(), + headers: { + Authorization: `Basic ${this.getBasicAuthorization()}`, + "Content-Type": "application/x-www-form-urlencoded" + }, + data: new URLSearchParams({ grant_type: grantType, ...payload }), + validateStatus: (status) => status >= 200 && status <= 299 + }); + } +} + +const DEFAULT_EXPIRATION_SECONDS = 300; // 5 minutes +/** @internal */ +const ISSUER_URN = "urn:zoom:rivet-sdk"; +class JwtStateStore { + encodedSecret; + expirationSeconds; + constructor({ expirationSeconds, stateSecret }) { + this.encodedSecret = new TextEncoder().encode(stateSecret); + this.expirationSeconds = expirationSeconds ?? DEFAULT_EXPIRATION_SECONDS; + } + async generateState() { + const issuedTime = dayjs(); + const expirationTime = issuedTime.add(this.expirationSeconds, "seconds"); + return await new jose.SignJWT({ random: node_crypto.randomBytes(8).toString("hex") }) + .setProtectedHeader({ alg: "HS256", typ: "JWT" }) + .setExpirationTime(expirationTime.toDate()) + .setIssuedAt(issuedTime.toDate()) + .setIssuer(ISSUER_URN) + .sign(this.encodedSecret); + } + async verifyState(state) { + try { + await jose.jwtVerify(state, this.encodedSecret, { + algorithms: ["HS256"], + issuer: ISSUER_URN, + typ: "JWT" + }); + } + catch (err) { + throw new OAuthStateVerificationFailedError(`Failed to verify OAuth state: ${err.name}.`, { + cause: err + }); + } + } +} + +const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; +const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds +const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; +/** + * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication + * is initiated server-side, but requires manual authorization from a user, by redirecting the user to Zoom. + * + * In addition to all required fields from {@link AuthOptions}, this class requires a `redirectUri`, as this + * value is appended to the authorization URL when the user is redirected to Zoom and subsequently redirected + * back to an endpoint on this server. + * + * @see {@link https://developers.zoom.us/docs/integrations/oauth/ | OAuth - Zoom Developers} + */ +class InteractiveAuth extends Auth { + installerOptions; + async getAuthorizationUrl() { + if (!this.installerOptions?.stateStore) { + throw new OAuthInstallerNotInitializedError("Cannot generate authorization URL, state store not initialized."); + } + const authUrl = new URL(OAUTH_AUTHORIZE_PATH, OAUTH_BASE_URL); + const generatedState = await Promise.resolve(this.installerOptions.stateStore.generateState()); + const { searchParams } = authUrl; + searchParams.set("client_id", this.clientId); + searchParams.set("redirect_uri", this.getFullRedirectUri()); + searchParams.set("response_type", "code"); + searchParams.set("state", generatedState); + return { + fullUrl: authUrl.toString(), + generatedState + }; + } + getFullRedirectUri() { + if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { + throw new OAuthInstallerNotInitializedError("Cannot generate full redirect URI, redirect URI or redirect URI path not initialized."); + } + return new URL(this.installerOptions.redirectUriPath, this.installerOptions.redirectUri).toString(); + } + // Don't return a type; we want it to be as narrow as possible (used for ReturnType). + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { + const updatedOptions = { + directInstall: Boolean(directInstall), + installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, + redirectUri, + redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE + }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } + this.installerOptions = updatedOptions; + return updatedOptions; + } +} + +const mergeDefaultOptions = (options, defaultOptions) => ({ ...defaultOptions, ...options }); + +const withDefaultTemplate = (cardContent, buttonContent) => ` + + + Zoom Rivet + + + + + + + + + +`; +/** + * Get the default HTML template that is shown to the developer/user when they visit the + * `installPath` endpoint, if Rivet currently has OAuth enabled. + * + * If `directInstall` is set to `true`, this function is not called; instead, the developer + * is directly redirected to Zoom's OAuth page. + */ +const defaultInstallTemplate = (authUrl) => withDefaultTemplate(`

Click the button below to navigate to Zoom to authorize your application for use with Rivet.

`, { href: authUrl, text: "Authorize with Zoom" }); +/** + * Get the default HTML template that is shown to the developer/user when they successfully + * authorize Rivet with a Zoom application. This is shown once they have already been redirected + * to Zoom, and the authorization attempt with Rivet was successful. + */ +const defaultCallbackSuccessTemplate = () => withDefaultTemplate(`

Your application has been successfully authorized with Rivet!

+

You may now close this page, or click the button below to redirect to Zoom's Marketplace.

`, { href: "https://marketplace.zoom.us", text: "Go to Marketplace" }); +/** + * Get the default HTML template that is shown to the developer when a known error occurs, meaning + * that the error is a core Rivet error. + */ +const defaultCallbackKnownErrorTemplate = (errName, errMessage) => withDefaultTemplate(`

An error occurred authorizing Rivet with Zoom.

+

[${errName}]: ${errMessage}

`); +/** + * Get the default HTML template that is shown to the developer when an unknown error occurs, + * meaning that the error is not known to be a core Rivet error and was thrown and not wrapped elsewhere. + */ +const defaultCallbackUnknownErrorTemplate = () => withDefaultTemplate(`

An unknown error occurred authorizing Rivet with Zoom. Please see stacktrace for details.

+

Please see stacktrace for further details.

`); + +const secureServerOptionKeys = [ + "ALPNProtocols", + "clientCertEngine", + "enableTrace", + "handshakeTimeout", + "rejectUnauthorized", + "requestCert", + "sessionTimeout", + "SNICallback", + "ticketKeys", + "pskCallback", + "pskIdentityHint", + "ca", + "cert", + "sigalgs", + "ciphers", + "clientCertEngine", + "crl", + "dhparam", + "ecdhCurve", + "honorCipherOrder", + "key", + "privateKeyEngine", + "privateKeyIdentifier", + "maxVersion", + "minVersion", + "passphrase", + "pfx", + "secureOptions", + "secureProtocol", + "sessionIdContext" +]; +class HttpReceiver { + /** @internal */ + static DEFAULT_ENDPOINT = "/zoom/events"; + eventEmitter; + interactiveAuth; + /** @internal */ + options; + server; + logger; + constructor(options) { + this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); + this.options.endpoints = prependSlashes(this.options.endpoints); + this.logger = + options.logger ?? + (() => { + const defaultLogger = new ConsoleLogger(); + defaultLogger.setLevel(options.logLevel ?? exports.LogLevel.ERROR); + return defaultLogger; + })(); + } + canInstall() { + return true; + } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } + getServerCreator() { + return this.hasSecureOptions() ? node_https.createServer : node_http.createServer; + } + hasEndpoint(pathname) { + const { endpoints } = this.options; + return Array.isArray(endpoints) ? endpoints.includes(pathname) : endpoints === pathname; + } + hasSecureOptions() { + return Object.keys(this.options).some((option) => secureServerOptionKeys.includes(option)); + } + init({ eventEmitter, interactiveAuth }) { + this.eventEmitter = eventEmitter; + this.interactiveAuth = interactiveAuth; + } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } + start(port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { + const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; + this.logger.error(errorMessage); + throw new HTTPReceiverPortNotNumberError(errorMessage); + } + const listenPort = port ?? this.options.port; + return new Promise((resolve, reject) => { + this.server = this.getServerCreator()(this.options, (req, res) => void (async () => { + // `req.headers.host` should be used with care, as clients can manipulate this value. + // However, for this use case, the value is completely discarded and only `pathname` + // is used, which is why there's no further validation occurring. + const { pathname, searchParams } = new URL(req.url ?? "", `http://${req.headers.host ?? "localhost"}`); + const { interactiveAuth } = this; + this.logger.debug([pathname, searchParams]); + // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath + if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { + const { installerOptions } = interactiveAuth; + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); + await (installerOptions.directInstall ? + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); + return; + } + // The user has navigated to the redirect page; init the code + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); + try { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { + const errorMessage = "OAuth callback did not include code and/or state in request."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); + return; + } + catch (err) { + const htmlTemplate = isCoreError(err) ? + defaultCallbackKnownErrorTemplate(err.name, err.message) + : defaultCallbackUnknownErrorTemplate(); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); + return; + } + } + } + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, exports.StatusCode.NOT_FOUND); + return; + } + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); + return; + } + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } + } + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } + } + } + })()); + this.server.on("close", () => (this.server = undefined)); + this.server.on("error", (err) => { + this.logger.error(err.message); + reject(err); + }); + this.server.listen(listenPort, () => { + if (!this.server) { + throw new ReceiverInconsistentStateError(); + } + const { port: listeningPort } = this.server.address(); + this.logger.info(`Listening on port ${listeningPort.toString()}`); + resolve(this.server); + }); + }); + } + stop() { + if (!this.server) { + throw new ReceiverInconsistentStateError(); + } + return new Promise((resolve, reject) => { + this.server?.close((err) => { + if (err) { + this.logger.error(err.message); + reject(err); + } + }); + this.server = undefined; + resolve(); + }); + } + writeTemporaryRedirect(res, location, setCookie) { + return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } + res.writeHead(exports.StatusCode.TEMPORARY_REDIRECT, { Location: location }); + res.end(() => { + resolve(); + }); + }); + } + writeResponse(res, statusCode, bodyContent, setCookie) { + return new Promise((resolve) => { + const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; + bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } + res.writeHead(statusCode, { "Content-Type": mimeType }); + res.end(bodyContent, () => { + resolve(); + }); + }); + } +} + +const version = "0.4.0"; +var packageJson = { + version: version}; + +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; +class WebEndpoints { + /** @internal */ + static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; + /** @internal */ + static DEFAULT_MIME_TYPE = "application/json"; + /** @internal */ + static DEFAULT_TIMEOUT = 0; + /** @internal */ + static GENERIC_ERROR_MESSAGE = "Request was unsuccessful with no further context"; + /** @internal */ + static TRACKING_ID_HEADER = "x-zm-trackingid"; + /** @internal */ + options; + constructor(options) { + this.options = mergeDefaultOptions(options, { + baseUrl: WebEndpoints.DEFAULT_BASE_URL, + hasCustomBaseUrl: typeof options.baseUrl !== "undefined", + timeout: WebEndpoints.DEFAULT_TIMEOUT + }); + } + buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }) { + // @ts-expect-error: Some arguments may not be present, but we pass them to makeRequest() anyway. + // prettier-ignore + // Next AST node is ignored by Prettier, even though it exceed maximum line length, because TypeScript + // won't allow ts-expect-error directive on multiple lines (https://github.com/Microsoft/TypeScript/issues/19573). + return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); + } + buildUserAgent() { + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + + `${node_path.basename(process.title)}/${process.version.replace("v", "")} ` + + `${os.platform()}/${os.release()}`); + } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } + getHeaders(bearerToken, contentType) { + return { + Accept: "application/json", + Authorization: `Bearer ${bearerToken}`, + "Content-Type": contentType, + "User-Agent": this.buildUserAgent() + }; + } + getRequestBody(args, mimeType) { + if (mimeType === "multipart/form-data") { + const formData = new FormData(); + Object.entries(args).forEach(([key, value]) => { + formData.append(key, value); + }); + return formData; + } + return args; + } + isOk(response) { + return response.status >= 200 && response.status <= 299; + } + isZoomResponseError(obj) { + return (typeof obj.code !== "undefined" && + typeof obj.message !== "undefined"); + } + async makeRequest(method, baseUrlOverride, url, requestContentType, bodyArgs, queryArgs) { + const { auth, baseUrl, doubleEncodeUrl, hasCustomBaseUrl, timeout } = this.options; + const bearerToken = await Promise.resolve(auth.getToken()); + const urlToSend = doubleEncodeUrl ? encodeURIComponent(encodeURIComponent(url)) : url; + const response = await axios({ + url: urlToSend, + method, + baseURL: hasCustomBaseUrl ? baseUrl : (baseUrlOverride ?? baseUrl), + headers: this.getHeaders(bearerToken, requestContentType), + params: queryArgs, + data: bodyArgs && this.getRequestBody(bodyArgs, requestContentType), + timeout: timeout, + beforeRedirect: (options) => { + options.headers = { + ...this.getHeaders(bearerToken, requestContentType), + ...options.headers + }; + }, + validateStatus: () => true // All responses are valid, not just 2xx + }); + if (!this.isOk(response)) { + const { status: statusCode } = response; + if (this.isZoomResponseError(response.data)) { + const { code: errorCode, message: errorMessage } = response.data; + throw new ApiResponseError(`[${statusCode.toString()}/${errorCode.toString()}]: "${errorMessage}"`); + } + throw new ApiResponseError(`[${statusCode.toString()}]: ${WebEndpoints.GENERIC_ERROR_MESSAGE}`); + } + return { + data: response.data, + statusCode: response.status, + trackingId: response.headers[WebEndpoints.TRACKING_ID_HEADER] + }; + } +} + +class CommerceEndpoints extends WebEndpoints { + accountManagement = { + createEndCustomerAccount: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/commerce/account` }), + addContactsToExistingEndCustomerOrYourOwnAccount: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ accountKey }) => `/commerce/account/${accountKey}/contacts` }), + getsListOfAllAccountsAssociatedWithZoomPartnerSubResellerByAccountType: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/accounts` }), + getAccountDetailsForZoomPartnerSubResellerEndCustomer: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ accountKey }) => `/commerce/accounts/${accountKey}` }) + }; + billing = { + getsAllBillingDocumentsForDistributorOrReseller: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/billing_documents` }), + getsPDFDocumentForBillingDocumentID: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ documentNumber }) => `/commerce/billing_documents/${documentNumber}/document` + }), + getDetailedInformationAboutSpecificInvoiceForDistributorOrReseller: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ invoiceNumber }) => `/commerce/invoices/${invoiceNumber}` }) + }; + dealRegistration = { + retrievesAllValidZoomCampaignsWhichDealRegistrationCanBeAssociatedWith: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/campaigns` }), + createsNewDealRegistrationForPartner: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/commerce/deal_registration` }), + getsAllValidDealRegistrationsForPartner: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/deal_registrations` }), + getsDetailsForDealRegistrationByDealRegistrationNumber: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ dealRegKey }) => `/commerce/deal_registrations/${dealRegKey}` }), + updatesExistingDealRegistration: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ dealRegKey }) => `/commerce/deal_registrations/${dealRegKey}` }) + }; + order = { + createsSubscriptionOrderForZoomPartner: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/commerce/order` }), + previewDeltaOrderMetricsAndSubscriptionsInOrder: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/commerce/order/preview` }), + getsAllOrdersForZoomPartner: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/orders` }), + getsOrderDetailsByOrderReferenceID: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ orderReferenceId }) => `/commerce/orders/${orderReferenceId}` }) + }; + productCatalog = { + getsZoomProductCatalogForZoomPartner: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/commerce/catalog` }), + getsDetailsForZoomProductOrOffer: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ offerId }) => `/commerce/catalog/${offerId}` }), + getsPricebookInDownloadableFile: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/pricebooks` }) + }; + subscription = { + getsSubscriptionsForZoomPartner: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/subscriptions` }), + getsSubscriptionDetailsForGivenSubscriptionNumber: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ subscriptionNumber }) => `/commerce/subscriptions/${subscriptionNumber}` }), + getsSubscriptionChangesVersionsForGivenSubscriptionNumber: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ subscriptionNumber }) => `/commerce/subscriptions/${subscriptionNumber}/versions` + }) + }; +} + +class S2SAuth extends Auth { + accountId; + constructor({ accountId, ...restOptions }) { + super(restOptions); + this.accountId = accountId; + } + assertRawToken(obj) { + if (typeof obj.access_token !== "string" || + typeof obj.expires_in !== "number" || + typeof obj.scope !== "string") { + throw new S2SRawResponseError(`Failed to match raw response ${JSON.stringify(obj)} to expected shape.`); + } + } + async fetchAccessToken() { + const response = await this.makeOAuthTokenRequest("account_credentials", { + account_id: this.accountId + }); + this.assertRawToken(response.data); + return this.mapAccessToken(response.data); + } + async getToken() { + const { tokenStore } = this; + const currentToken = await Promise.resolve(tokenStore.getLatestToken()); + if (currentToken && !this.isAlmostExpired(currentToken.expirationTimeIso)) { + return currentToken.accessToken; + } + const token = await this.fetchAccessToken(); + await Promise.resolve(tokenStore.storeToken(token)); + return token.accessToken; + } + mapAccessToken({ access_token, expires_in, scope }) { + return { + accessToken: access_token, + expirationTimeIso: dayjs().add(expires_in, "seconds").toISOString(), + scopes: scope.includes(" ") ? scope.split(" ") : [scope] + }; + } +} + +// Utility functions for determining if client options include custom receiver, or, if not, +// a webhooks secret token, as one of those is required! +const hasExplicitReceiver = (obj) => typeof obj.receiver !== "undefined"; +const hasWebhooksSecretToken = (obj) => typeof obj.webhooksSecretToken !== "undefined"; +const isReceiverDisabled = (options) => typeof options.disableReceiver !== "undefined" && options.disableReceiver; +const DEFAULT_HTTP_RECEIVER_PORT = 8080; +const DEFAULT_LOGLEVEL = exports.LogLevel.ERROR; +class ProductClient { + auth; + endpoints; + webEventConsumer; + receiver; + constructor(options) { + this.auth = this.initAuth(options); + this.endpoints = this.initEndpoints(this.auth, options); + this.webEventConsumer = this.initEventProcessor(this.endpoints, options); + // Only create an instance of `this.receiver` if the developer did not explicitly disable it. + if (!isReceiverDisabled(options)) { + // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); + } + this.receiver = (hasExplicitReceiver(options) ? + options.receiver + : this.initDefaultReceiver(options)); + this.receiver.init({ + eventEmitter: this.webEventConsumer, + interactiveAuth: this.auth instanceof InteractiveAuth ? this.auth : undefined + }); + } + } + initDefaultReceiver({ port, webhooksSecretToken, logLevel }) { + return new HttpReceiver({ + port: port ?? DEFAULT_HTTP_RECEIVER_PORT, + webhooksSecretToken, + logLevel: logLevel ?? DEFAULT_LOGLEVEL + }); + } + async start() { + if (!this.receiver) { + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); + } + // Method call is wrapped in `await` and `Promise.resolve()`, as the call + // may or may not return a promise. This is not required when implementing `Receiver`. + return (await Promise.resolve(this.receiver.start())); + } +} + +class CommerceS2SAuthClient extends ProductClient { + initAuth({ accountId, clientId, clientSecret, tokenStore }) { + return new S2SAuth({ accountId, clientId, clientSecret, tokenStore }); + } + initEndpoints(auth, options) { + return new CommerceEndpoints({ auth, ...options }); + } + initEventProcessor() { + return undefined; + } +} + +exports.ApiResponseError = ApiResponseError; +exports.AwsLambdaReceiver = AwsLambdaReceiver; +exports.AwsReceiverRequestError = AwsReceiverRequestError; +exports.ClientCredentialsRawResponseError = ClientCredentialsRawResponseError; +exports.CommerceEndpoints = CommerceEndpoints; +exports.CommerceS2SAuthClient = CommerceS2SAuthClient; +exports.CommonHttpRequestError = CommonHttpRequestError; +exports.ConsoleLogger = ConsoleLogger; +exports.HTTPReceiverConstructionError = HTTPReceiverConstructionError; +exports.HTTPReceiverPortNotNumberError = HTTPReceiverPortNotNumberError; +exports.HTTPReceiverRequestError = HTTPReceiverRequestError; +exports.HttpReceiver = HttpReceiver; +exports.OAuthInstallerNotInitializedError = OAuthInstallerNotInitializedError; +exports.OAuthStateVerificationFailedError = OAuthStateVerificationFailedError; +exports.OAuthTokenDoesNotExistError = OAuthTokenDoesNotExistError; +exports.OAuthTokenFetchFailedError = OAuthTokenFetchFailedError; +exports.OAuthTokenRawResponseError = OAuthTokenRawResponseError; +exports.OAuthTokenRefreshFailedError = OAuthTokenRefreshFailedError; +exports.ProductClientConstructionError = ProductClientConstructionError; +exports.ReceiverInconsistentStateError = ReceiverInconsistentStateError; +exports.ReceiverOAuthFlowError = ReceiverOAuthFlowError; +exports.S2SRawResponseError = S2SRawResponseError; +exports.isCoreError = isCoreError; +exports.isStateStore = isStateStore; diff --git a/commerce/commerce.d.ts b/commerce/commerce.d.ts new file mode 100644 index 0000000..f4deb91 --- /dev/null +++ b/commerce/commerce.d.ts @@ -0,0 +1,2444 @@ +import { AxiosResponse } from 'axios'; +import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; +import { Server } from 'node:http'; +import { ServerOptions } from 'node:https'; + +declare enum LogLevel { + ERROR = "error", + WARN = "warn", + INFO = "info", + DEBUG = "debug" +} +interface Logger { + /** + * Output debug message + * @param msg any data to be logged + */ + debug(...msg: unknown[]): void; + /** + * Output info message + * @param msg any data to be logged + */ + info(...msg: unknown[]): void; + /** + * Output warn message + * @param msg any data to be logged + */ + warn(...msg: unknown[]): void; + /** + * Output error message + * @param msg any data to be logged + */ + error(...msg: unknown[]): void; + /** + * Disables all logging below the given level + * @param level as a string, 'error' | 'warn' | 'info' | 'debug' + */ + setLevel(level: LogLevel): void; + /** + * Return the current LogLevel. + */ + getLevel(): LogLevel; + /** + * Name the instance so that it can be filtered when many loggers are sending output + * to the same destination. + * @param name as a string + */ + setName(name: string): void; +} +declare class ConsoleLogger implements Logger { + private level; + private name; + private static labels; + private static severity; + constructor(); + getLevel(): LogLevel; + setLevel(level: LogLevel): void; + setName(name: string): void; + debug(...msg: unknown[]): void; + info(...msg: unknown[]): void; + warn(...msg: unknown[]): void; + error(...msg: unknown[]): void; + private static isMoreOrEqualSevere; +} + +type AllPropsOptional = Exclude<{ + [P in keyof T]: undefined extends T[P] ? True : False; +}[keyof T], undefined> extends True ? True : False; +type Constructor = new (...args: any[]) => T; +type MaybeArray = T | T[]; +type MaybePromise = T | Promise; +type StringIndexed = Record; + +interface TokenStore { + getLatestToken(): MaybePromise; + storeToken(token: Token): MaybePromise; +} + +interface AuthOptions { + clientId: string; + clientSecret: string; + tokenStore?: TokenStore | undefined; + logger?: Logger; +} +type OAuthGrantType = "authorization_code" | "client_credentials" | "refresh_token" | "account_credentials"; +interface BaseOAuthRequest { + grant_type: OAuthGrantType; +} +interface OAuthAuthorizationCodeRequest extends BaseOAuthRequest { + code: string; + grant_type: "authorization_code"; + redirect_uri?: string; +} +interface OAuthRefreshTokenRequest extends BaseOAuthRequest { + grant_type: "refresh_token"; + refresh_token: string; +} +interface S2SAuthTokenRequest extends BaseOAuthRequest { + grant_type: "account_credentials"; + account_id: string; +} +type OAuthRequest = OAuthAuthorizationCodeRequest | OAuthRefreshTokenRequest | S2SAuthTokenRequest; +/** + * {@link Auth} is the base implementation of authentication for Zoom's APIs. + * + * It only requires a `clientId` and `tokenStore`, as these options are shared across + * all authentication implementations, namely OAuth and server-to-server auth (client + * credentials, JWT, and server-to-server OAuth.) + */ +declare abstract class Auth { + protected readonly clientId: string; + protected readonly clientSecret: string; + protected readonly tokenStore: TokenStore; + protected readonly logger: Logger | undefined; + constructor({ clientId, clientSecret, tokenStore, logger }: AuthOptions); + protected getBasicAuthorization(): string; + abstract getToken(): MaybePromise; + protected isAlmostExpired(isoTime: string): boolean; + protected makeOAuthTokenRequest(grantType: T, payload?: Omit, "grant_type">): Promise; +} + +interface ClientCredentialsToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} + +interface JwtToken { + token: string; + expirationTimeIso: string; +} + +interface S2SAuthToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} +interface S2SAuthOptions { + accountId: string; +} +declare class S2SAuth extends Auth { + private accountId; + constructor({ accountId, ...restOptions }: AuthOptions & S2SAuthOptions); + private assertRawToken; + private fetchAccessToken; + getToken(): Promise; + private mapAccessToken; +} + +interface Event { + event: Type; +} +type EventKeys = T extends Event ? U : never; +type EventPayload = Extract; +type EventListenerFn, ReturnType = MaybePromise> = (payload: EventPayload) => ReturnType; +type EventListenerPredicateFn> = EventListenerFn>; +type ContextListener, Context> = (_: EventPayload & Context) => MaybePromise; +type GenericEventManager = EventManager; +declare class EventManager { + protected endpoints: Endpoints; + constructor(endpoints: Endpoints); + private appendListener; + filteredEvent>(eventName: EventName, predicate: EventListenerPredicateFn, listener: EventListenerFn): void; + emit>(eventName: EventName, payload: EventPayload): Promise; + event>(eventName: EventName, listener: EventListenerFn): void; + protected withContext, Context>(): ContextListener; +} + +declare enum StatusCode { + OK = 200, + TEMPORARY_REDIRECT = 302, + BAD_REQUEST = 400, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + INTERNAL_SERVER_ERROR = 500 +} +interface ReceiverInitOptions { + eventEmitter?: GenericEventManager | undefined; + interactiveAuth?: InteractiveAuth | undefined; +} +interface Receiver { + canInstall(): true | false; + init(options: ReceiverInitOptions): void; + start(...args: any[]): MaybePromise; + stop(...args: any[]): MaybePromise; +} + +interface HttpReceiverOptions extends Partial { + endpoints?: MaybeArray | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; + port?: number | string | undefined; + webhooksSecretToken?: string | undefined; +} +type SecureServerOptions = { + [K in (typeof secureServerOptionKeys)[number]]: ServerOptions[K]; +}; +declare const secureServerOptionKeys: (keyof ServerOptions)[]; +declare class HttpReceiver implements Receiver { + private eventEmitter?; + private interactiveAuth?; + private server?; + private logger; + constructor(options: HttpReceiverOptions); + canInstall(): true; + private buildDeletedStateCookieHeader; + private buildStateCookieHeader; + private getRequestCookie; + private getServerCreator; + private hasEndpoint; + private hasSecureOptions; + init({ eventEmitter, interactiveAuth }: ReceiverInitOptions): void; + private setResponseCookie; + private areNormalizedUrlsEqual; + start(port?: number | string): Promise; + stop(): Promise; + private writeTemporaryRedirect; + private writeResponse; +} + +interface BaseResponse { + data?: Data | undefined; + statusCode: number; + trackingId?: string | undefined; +} +interface BuildEndpointOptions { + method: HttpMethod; + baseUrlOverride?: string | undefined; + urlPathBuilder: (params: PathSchema) => string; + requestMimeType?: RequestMimeType; +} +interface WebEndpointOptions { + auth: Auth; + baseUrl?: string | undefined; + doubleEncodeUrl?: boolean | undefined; + timeout?: number | undefined; + userAgentName?: string | undefined; +} +type EndpointArguments = (PathSchema extends NoParams ? object : AllPropsOptional extends "t" ? { + path?: PathSchema; +} : { + path: PathSchema; +}) & (BodySchema extends NoParams ? object : AllPropsOptional extends "t" ? { + body?: BodySchema; +} : { + body: BodySchema; +}) & (QuerySchema extends NoParams ? object : AllPropsOptional extends "t" ? { + query?: QuerySchema; +} : { + query: QuerySchema; +}); +type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; +type NoParams = "_NO_PARAMS_"; +type RequestMimeType = "application/json" | "multipart/form-data"; +declare class WebEndpoints { + constructor(options: WebEndpointOptions); + protected buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }: BuildEndpointOptions): (_: EndpointArguments) => Promise>; + private buildUserAgent; + private getCustomUserAgentName; + private getHeaders; + private getRequestBody; + private isOk; + private isZoomResponseError; + private makeRequest; +} + +type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & Pick & { + disableReceiver?: boolean | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; +}; +interface ClientReceiverOptions { + receiver: R; +} +type ClientConstructorOptions, R extends Receiver> = (O & { + disableReceiver: true; +}) | (O & (ClientReceiverOptions | HttpReceiverOptions)); +type ExtractInstallerOptions = A extends InteractiveAuth ? [ + ReturnType +] extends [true] ? WideInstallerOptions : object : object; +type ExtractAuthTokenType = A extends Auth ? T : never; +type GetAuthOptions = AuthOptions> & (A extends S2SAuth ? S2SAuthOptions : object); +type WideInstallerOptions = { + installerOptions: InstallerOptions; +}; +declare abstract class ProductClient, ReceiverType extends Receiver> { + private readonly auth; + readonly endpoints: EndpointsType; + readonly webEventConsumer?: EventProcessorType | undefined; + private readonly receiver?; + constructor(options: ClientConstructorOptions); + protected abstract initAuth(options: OptionsType): AuthType; + protected abstract initEndpoints(auth: AuthType, options: OptionsType): EndpointsType; + protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType | undefined; + private initDefaultReceiver; + start(): Promise>; +} + +/** + * {@link StateStore} defines methods for generating and verifying OAuth state. + * + * This interface is implemented internally for the default state store; however, + * it can also be implemented and passed to an OAuth client as well. + */ +interface StateStore { + /** + * Generate a new state string, which is directly appended to the OAuth `state` parameter. + */ + generateState(): MaybePromise; + /** + * Verify that the state received during OAuth callback is valid and not forged. + * + * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. + * + * @param state The state parameter that was received during OAuth callback + */ + verifyState(state: string): MaybePromise; +} +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +declare const isStateStore: (obj: unknown) => obj is StateStore; + +interface AuthorizationUrlResult { + fullUrl: string; + generatedState: string; +} +interface InstallerOptions { + directInstall?: boolean | undefined; + installPath?: string | undefined; + redirectUri: string; + redirectUriPath?: string | undefined; + stateStore: StateStore | string; + stateCookieName?: string | undefined; + stateCookieMaxAge?: number | undefined; +} +/** + * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication + * is initiated server-side, but requires manual authorization from a user, by redirecting the user to Zoom. + * + * In addition to all required fields from {@link AuthOptions}, this class requires a `redirectUri`, as this + * value is appended to the authorization URL when the user is redirected to Zoom and subsequently redirected + * back to an endpoint on this server. + * + * @see {@link https://developers.zoom.us/docs/integrations/oauth/ | OAuth - Zoom Developers} + */ +declare abstract class InteractiveAuth extends Auth { + installerOptions?: ReturnType; + getAuthorizationUrl(): Promise; + getFullRedirectUri(): string; + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }: InstallerOptions): { + directInstall: boolean; + installPath: string; + redirectUri: string; + redirectUriPath: string; + stateStore: StateStore; + stateCookieName: string; + stateCookieMaxAge: number; + }; +} + +/** + * Credentials for access token & refresh token, which are used to access Zoom's APIs. + * + * As access token is short-lived (usually a single hour), its expiration time is checked + * first. If it's possible to use the access token, it's used; however, if it has expired + * or is close to expiring, the refresh token should be used to generate a new access token + * before the API call is made. Refresh tokens are generally valid for 90 days. + * + * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} + * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. + * It's likely that this error will be rare, but it _can_ be thrown. + */ +interface OAuthToken { + accessToken: string; + expirationTimeIso: string; + refreshToken: string; + scopes: string[]; +} + +interface RivetError extends Error { + readonly errorCode: ErrorCode; +} + +declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ + readonly ApiResponseError: "zoom_rivet_api_response_error"; + readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; + readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; + readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; + readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; + readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; + readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; + readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; + readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; + readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; + readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; + readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; + readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; + readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; + readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; + readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; + readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; +}[K]>; +declare const ApiResponseError: Constructor; +declare const AwsReceiverRequestError: Constructor; +declare const ClientCredentialsRawResponseError: Constructor; +declare const S2SRawResponseError: Constructor; +declare const CommonHttpRequestError: Constructor; +declare const ReceiverInconsistentStateError: Constructor; +declare const ReceiverOAuthFlowError: Constructor; +declare const HTTPReceiverConstructionError: Constructor; +declare const HTTPReceiverPortNotNumberError: Constructor; +declare const HTTPReceiverRequestError: Constructor; +declare const OAuthInstallerNotInitializedError: Constructor; +declare const OAuthTokenDoesNotExistError: Constructor; +declare const OAuthTokenFetchFailedError: Constructor; +declare const OAuthTokenRawResponseError: Constructor; +declare const OAuthTokenRefreshFailedError: Constructor; +declare const OAuthStateVerificationFailedError: Constructor; +declare const ProductClientConstructionError: Constructor; + +interface AwsLambdaReceiverOptions { + webhooksSecretToken: string; +} +declare class AwsLambdaReceiver implements Receiver { + private eventEmitter?; + private readonly webhooksSecretToken; + constructor({ webhooksSecretToken }: AwsLambdaReceiverOptions); + buildResponse(statusCode: StatusCode, body: object): LambdaFunctionURLResult; + canInstall(): false; + init({ eventEmitter }: ReceiverInitOptions): void; + start(): LambdaFunctionURLHandler; + stop(): Promise; +} + +type AccountManagementCreateEndCustomerAccountRequestBody = { + account_name: string; + employee_count: string; + website: string; + sub_reseller_crm_account_number?: string; + contacts: { + first_name: string; + last_name: string; + job_title: string; + company_email: string; + business_phone: string; + primary_role?: string; + }[]; + currency: string; + billing_address: { + line_1: string; + city: string; + postal_code: string; + state: string; + country: string; + }; +}; +type AccountManagementCreateEndCustomerAccountResponse = { + create_reference_id?: string; + crm_account_number?: string; + status?: string; + status_detail?: string; +}; +type AccountManagementAddContactsToExistingEndCustomerOrYourOwnAccountPathParams = { + accountKey: string; +}; +type AccountManagementAddContactsToExistingEndCustomerOrYourOwnAccountRequestBody = { + contacts?: { + first_name: string; + last_name: string; + job_title: string; + company_email: string; + business_phone: string; + primary_role?: string; + }[]; +}; +type AccountManagementAddContactsToExistingEndCustomerOrYourOwnAccountResponse = { + create_reference_id?: string; + crm_account_number?: string; + status?: string; + status_detail?: string; +}; +type AccountManagementGetsListOfAllAccountsAssociatedWithZoomPartnerSubResellerByAccountTypeQueryParams = { + relationship_type?: string; + account_name?: string; + crm_account_number?: string; + zoom_account_number?: string; + create_reference_id?: string; + sub_reseller_crm_account_number?: string; + sibling_crm_account_number?: string; + page_size?: number; + page_number?: number; +}; +type AccountManagementGetsListOfAllAccountsAssociatedWithZoomPartnerSubResellerByAccountTypeResponse = { + page_count?: number; + account_list?: { + create_reference_id?: string; + crm_account_number?: string; + account_name?: string; + zoom_account_number?: string; + account_type?: string; + country?: string; + currency?: string; + website?: string; + }[]; +}; +type AccountManagementGetAccountDetailsForZoomPartnerSubResellerEndCustomerPathParams = { + accountKey: string; +}; +type AccountManagementGetAccountDetailsForZoomPartnerSubResellerEndCustomerResponse = { + crm_account_number?: string; + create_reference_id?: string; + status?: string; + zoom_account_number?: string; + account_name?: string; + account_type?: string; + created_on_date?: string; + employee_count?: string; + website?: string; + currency?: string; + contacts?: { + contact_crm_number?: string; + first_name: string; + last_name: string; + job_title: string; + company_email: string; + business_phone: string; + primary_role?: string; + }[]; + billing_address?: { + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; +}; +type BillingGetsAllBillingDocumentsForDistributorOrResellerQueryParams = { + document_type?: string; + payment_status?: string; + document_date_start?: string; + document_date_end?: string; + due_date?: string; + document_number?: string; + currency?: string; + end_customer_name?: string; + invoice_owner_crm_account_number?: string; + page_size?: number; + next_page_token?: string; + sort?: string; +}; +type BillingGetsAllBillingDocumentsForDistributorOrResellerResponse = { + document_count?: number; + billing_documents?: { + document_number?: string; + document_date?: string; + document_type?: string; + customer_name?: string[]; + sub_reseller_name?: string; + invoice_owner_name?: string; + due_date?: string; + payment_status?: string; + balance?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + po_number?: string; + billing_description?: string; + posted_date?: string; + reason_detail?: string; + reference_billing_document_id?: string; + }[]; + next_page_token?: string; +}; +type BillingGetsPDFDocumentForBillingDocumentIDPathParams = { + documentNumber: string; +}; +type BillingGetDetailedInformationAboutSpecificInvoiceForDistributorOrResellerPathParams = { + invoiceNumber: string; +}; +type BillingGetDetailedInformationAboutSpecificInvoiceForDistributorOrResellerQueryParams = { + zoom_account_number?: string; + crm_account_number?: string; +}; +type BillingGetDetailedInformationAboutSpecificInvoiceForDistributorOrResellerResponse = { + invoice_number?: string; + invoice_date?: string; + customer_name?: string[]; + invoice_owner_account?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + sub_reseller?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + sold_to_contact?: { + crm_contact_number?: string; + first_name?: string; + last_name?: string; + email?: string; + }; + bill_to_contact?: { + crm_contact_number?: string; + first_name?: string; + last_name?: string; + email?: string; + }; + due_date?: string; + payment_status?: string; + balance?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + po_numbers?: string; + billing_description?: string; + posted_date?: string; + reason_detail?: string; + reference_billing_document_id?: string; + total_tax_amount?: { + amount?: number; + currency?: string; + }; + target_date?: object; + invoice_items?: { + end_customer_account?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + charge_name?: string; + charge_type?: string; + description?: string; + offer_name?: string; + offer_price_list_name?: string; + start_date?: string; + end_date?: string; + subscription_number?: string; + partner_sku_code?: string; + po_number?: string; + quantity?: number; + tax_amount?: { + amount?: number; + currency?: string; + }; + total_amount?: { + amount?: number; + currency?: string; + }; + }[]; +}; +type DealRegistrationRetrievesAllValidZoomCampaignsWhichDealRegistrationCanBeAssociatedWithQueryParams = { + end_customer_crm_account_number: string; + sub_reseller_crm_account_number?: string; + campaign_member_crm_contact_number: string; + product_groups: string; +}; +type DealRegistrationRetrievesAllValidZoomCampaignsWhichDealRegistrationCanBeAssociatedWithResponse = { + campaigns?: { + campaign_number: number; + campaign_name?: string; + campaign_description?: string; + campaign_start_date?: string; + campaign_end_date?: string; + }[]; +}; +type DealRegistrationCreatesNewDealRegistrationForPartnerRequestBody = { + opportunity_type: string; + sub_reseller_crm_account_number?: string; + sales_rep_contact_crm_number: string; + partner_contacts?: { + contact_crm_number?: string; + }[]; + end_customer_crm_account_number: string; + end_customer_contact_crm_number: string; + end_customer_contacts?: { + contact_crm_number?: string; + }[]; + end_customer_department?: string[]; + met_decision_maker: boolean; + decision_maker_crm_number?: string; + budget_identified?: boolean; + is_public_sector?: boolean; + buy_gov_skus?: boolean; + end_customer_industry: string; + opportunity_name: string; + opportunity_desc?: string; + estimated_close_date: string; + estimated_mrr: number; + currency: string; + requires_professional_services: boolean; + professional_services_description?: string; + phone_carrier?: string; + product_groups: { + name: string; + quantity: number; + }[]; + campaign_number?: string; + sales_activities: { + type: string; + date: string; + }[]; + rfp_details?: { + is_rfp: boolean; + rfp_link?: string; + rfp_issue_date?: string; + rfp_due_date?: string; + }; + submitter_contact_crm_number?: string; + migration_type?: string; + additional_comments?: string; +}; +type DealRegistrationCreatesNewDealRegistrationForPartnerResponse = { + create_reference_id?: string; + deal_reg_number?: string; + status?: string; + errors?: { + error_code: string; + error_description: string; + }[]; +}; +type DealRegistrationGetsAllValidDealRegistrationsForPartnerQueryParams = { + deal_reg_number?: string; + create_reference_id?: string; + end_customer_name?: string; + end_customer_crm_account_number?: string; + end_customer_zoom_account_number?: string; + sub_reseller_name?: string; + sub_reseller_crm_account_number?: string; + invoice_owner_crm_account_number?: string; + status: string; + page_size?: number; + page_number?: string; +}; +type DealRegistrationGetsAllValidDealRegistrationsForPartnerResponse = { + page_count?: number; + deal_registrations?: { + deal_reg_number?: string; + create_reference_id?: string; + deal_name?: string; + invoice_owner_crm_account_number?: string; + opportunity_stage?: string; + submitted_date?: string; + expected_closed_date?: string; + estimated_mrr?: number; + currency?: string; + program_name?: string; + original_expiry_date?: string; + extended_expiry_date?: string; + partner_sales_rep?: string; + zoom_account_executive?: string; + zoom_cam?: string; + status?: string; + end_customer?: { + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + department?: string; + industry?: string; + account_local_name?: string; + employee_count?: string; + website?: string; + }; + opportunity?: { + opportunity_id?: string; + opportunity_name?: string; + opportunity_type?: string; + expected_close_date?: string; + partner_role?: string; + channel_sales_motion?: string; + }; + }[]; +}; +type DealRegistrationGetsDetailsForDealRegistrationByDealRegistrationNumberPathParams = { + dealRegKey: string; +}; +type DealRegistrationGetsDetailsForDealRegistrationByDealRegistrationNumberResponse = { + deal_reg_number?: string; + create_reference_id?: string; + deal_name?: string; + deal_description?: string; + currency?: string; + program_name?: string; + opportunity_stage?: string; + submitted_date?: string; + approved_date?: string; + denied_date?: string; + expected_close_date?: string; + partner_role?: string; + original_expiry_date?: string; + extended_expiry_date?: string; + sales_representative?: { + self_sales_representative?: boolean; + sales_rep_contact?: { + contact_crm_number: string; + first_name?: string; + last_name?: string; + email?: string; + title?: string; + phone?: string; + }; + }; + submitter?: { + contact_crm_number: string; + first_name?: string; + last_name?: string; + email?: string; + title?: string; + phone?: string; + }; + related_partner?: string; + partner_contacts?: { + contact_crm_number: string; + first_name?: string; + last_name?: string; + email?: string; + title?: string; + phone?: string; + }[]; + is_existing_customer?: boolean; + sub_reseller?: { + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + invoice_owner?: { + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + end_customer?: { + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + department?: string; + industry?: string; + account_local_name?: string; + employee_count?: string; + website?: string; + }; + end_customer_primary_contact?: { + contact_crm_number: string; + first_name?: string; + last_name?: string; + email?: string; + title?: string; + phone?: string; + end_customer_contact_domain_reason?: string; + }; + end_customer_other_contacts?: { + contact_crm_number: string; + first_name?: string; + last_name?: string; + email?: string; + title?: string; + phone?: string; + end_customer_contact_domain_reason?: string; + }[]; + end_customer_website_details?: { + empty_website?: boolean; + empty_website_reason?: string; + public_website_reason?: string; + }; + is_public_sector?: boolean; + budget_identified?: boolean; + buy_gov_skus?: boolean; + met_decision_maker?: boolean; + decision_maker_email?: string; + decision_maker_name?: string; + estimated_mrr?: number; + requires_professional_services?: boolean; + professional_services_description?: string; + phone_carrier?: string; + product_groups?: { + name: string; + quantity: number; + }[]; + campaign?: string; + sales_activities?: { + sales_activity_number?: string; + type: string; + date: string; + }[]; + rfp_details?: { + is_rfp?: boolean; + rfp_link?: string; + issue_date?: string; + due_date?: string; + }; + zoom_cams?: { + zoom_territory_cam?: string; + zoom_named_cam?: string; + zoom_distribution_cam?: string; + }; + additional_comments?: string; + opportunity?: { + opportunity_number?: string; + opportunity_name?: string; + opportunity_type?: string; + partner_role?: string; + channel_sales_motion?: string; + expected_close_date?: string; + }; + status?: string; + status_detail?: { + return_reason?: string; + revoke_reason?: string; + denied_reason?: string; + other_comments?: string; + }; +}; +type DealRegistrationUpdatesExistingDealRegistrationPathParams = { + dealRegKey: string; +}; +type DealRegistrationUpdatesExistingDealRegistrationRequestBody = { + sales_rep_contact_crm_number?: string; + add_partner_contacts?: { + contact_crm_number?: string; + }[]; + add_end_customer_contacts?: { + contact_crm_number?: string; + }[]; + end_customer_department?: string[]; + met_decision_maker?: boolean; + decision_maker_crm_number?: string; + budget_identified?: boolean; + is_public_sector?: boolean; + buy_gov_skus?: boolean; + end_customer_industry?: string; + opportunity_name?: string; + opportunity_desc?: string; + estimated_close_date?: string; + estimated_mrr?: number; + currency?: string; + requires_professional_services?: boolean; + professional_services_description?: string; + phone_carrier?: string; + add_product_groups?: { + name: string; + quantity: number; + }[]; + remove_product_groups?: { + name: string; + }[]; + campaign_number?: string; + sales_activities?: { + sales_activity_number?: string; + type: string; + date: string; + }[]; + rfp_details?: { + is_rfp: boolean; + rfp_link?: string; + issue_date?: string; + due_date?: string; + }; + submitter_contact_crm_number?: string; + additional_comments?: string; +}; +type OrderCreatesSubscriptionOrderForZoomPartnerRequestBody = { + header: { + order_type?: string; + order_description?: string; + deal_reg_number?: string; + order_date: string; + po_number?: string; + additional_attributes?: { + name?: string; + value_type?: string; + value?: string; + }[]; + }; + create_subscriptions?: { + end_customer_account_number?: string; + end_customer_crm_account_number: string; + sold_to_crm_contact_number: string; + end_customer_language?: string; + initial_term: { + term_type: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + renewal_term?: { + term_type: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + sub_reseller?: { + crm_account_number?: string; + account_name?: string; + }; + service_start_date?: string; + paid_period_start_date?: string; + free_months_reason_code?: string; + currency: string; + auto_renew?: boolean; + add_offers?: { + offer_price_list_id: string; + partner_sku_code?: string; + quantity?: number; + start_date?: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + add_add_ons?: { + offer_price_list_id: string; + partner_sku_code?: string; + quantity?: number; + start_date?: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + }[]; + amend_subscriptions?: { + subscription_number?: string; + zoom_account_number?: string; + add_offers?: { + offer_price_list_id: string; + partner_sku_code?: string; + quantity?: number; + start_date?: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + add_add_ons?: { + offer_price_list_id: string; + partner_sku_code?: string; + quantity?: number; + start_date?: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + upgrade_offers?: { + new_offer_price_list_id?: string; + new_partner_sku_code?: string; + old_offer_price_list_id?: string; + old_partner_sku_code?: string; + quantity?: number; + start_date: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + remove_offers?: { + offer_price_list_id: string; + partner_sku_code?: string; + end_date: string; + remove_reason?: string; + }[]; + update_offers?: { + offer_price_list_id: string; + partner_sku_code?: string; + quantity?: number; + start_date: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + cancel_subscription?: { + cancel_by?: string; + cancel_on?: string; + cancel_reason: string; + }; + renew_subscription?: { + renewal_term?: { + term_type: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + }; + update_subscription?: { + auto_renew?: boolean; + sold_to_crm_contact_number?: string; + end_customer_language?: string; + }; + }[]; +}; +type OrderCreatesSubscriptionOrderForZoomPartnerResponse = { + status?: string; + order_reference_id?: string; + order_number?: string; + order_date?: string; + subscriptions?: { + subscription_number?: string; + zoom_account_number?: string; + subscription_status?: string; + subscription_owner_id?: string; + invoice_owner_id?: string; + invoice_owner_crm_account_number?: string; + }[]; + errors?: { + error_code: string; + error_description: string; + }[]; +}; +type OrderPreviewDeltaOrderMetricsAndSubscriptionsInOrderRequestBody = { + header: { + order_type?: string; + order_description?: string; + deal_reg_number?: string; + order_date: string; + po_number?: string; + additional_attributes?: { + name?: string; + value?: string; + }[]; + }; + create_subscriptions?: { + end_customer_account_number?: string; + end_customer_crm_account_number: string; + sold_to_crm_contact_number: string; + end_customer_language?: string; + initial_term: { + term_type: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + renewal_term?: { + term_type: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + sub_reseller?: { + crm_account_number?: string; + account_name?: string; + }; + service_start_date?: string; + paid_period_start_date?: string; + free_months_reason_code?: string; + currency: string; + auto_renew?: boolean; + add_offers?: { + offer_price_list_id: string; + partner_sku_code?: string; + quantity?: number; + start_date?: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + add_add_ons?: { + offer_price_list_id: string; + partner_sku_code?: string; + quantity?: number; + start_date?: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + }[]; + amend_subscriptions?: { + subscription_number?: string; + zoom_account_number?: string; + add_offers?: { + offer_price_list_id: string; + partner_sku_code?: string; + quantity?: number; + start_date?: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + add_add_ons?: { + offer_price_list_id: string; + partner_sku_code?: string; + quantity?: number; + start_date?: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + upgrade_offers?: { + new_offer_price_list_id?: string; + new_partner_sku_code?: string; + old_offer_price_list_id?: string; + old_partner_sku_code?: string; + quantity?: number; + start_date: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + remove_offers?: { + offer_price_list_id: string; + partner_sku_code?: string; + end_date: string; + remove_reason?: string; + }[]; + update_offers?: { + offer_price_list_id: string; + partner_sku_code?: string; + quantity?: number; + start_date: string; + offer_attributes?: { + name?: string; + value?: string; + }[]; + }[]; + cancel_subscription?: { + cancel_by?: string; + cancel_on?: string; + cancel_reason: string; + }; + renew_subscription?: { + renewal_term?: { + term_type: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + }; + update_subscription?: { + auto_renew?: boolean; + sold_to_crm_contact_number?: string; + end_customer_language?: string; + }; + }[]; +}; +type OrderPreviewDeltaOrderMetricsAndSubscriptionsInOrderResponse = { + status?: string; + order_reference_id?: string; + order_date?: string; + order_metrics?: { + tcv?: number; + tcb?: number; + mrr?: number; + total_discount_pct?: number; + }; + subscription_preview?: { + subscription_number?: object; + tcv?: number; + tcb?: number; + mrr?: number; + total_discount_pct?: number; + }[]; + subscription_item_metrics?: { + offer_id?: string; + offer_name?: string; + sku?: string; + offer_price_list_id?: string; + offer_price_list_name?: string; + charges?: { + charge_model?: string; + charge_type?: string; + sale_price?: { + amount?: number; + currency?: string; + }; + net_price?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + discounts?: { + discount_type: string; + percent_value?: number; + amount_value?: number; + apply_to: string; + discount_level: string; + }[]; + }[]; + mrr?: { + amount?: number; + currency?: string; + }; + }[]; + errors?: string[]; +}; +type OrderGetsAllOrdersForZoomPartnerQueryParams = { + invoice_owner_crm_account_number?: string; + page_size?: number; + page_num?: string; + date_filter_option?: string; + from?: string; + to?: string; + order_type?: string; + order_reference_id?: string; + order_number?: string; + po_number?: string; + deal_reg_number?: string; + end_customer_name?: string; + end_customer_crm_account_number?: string; + end_customer_zoom_account_number?: string; + sub_reseller_name?: string; + sub_reseller_crm_account_number?: string; + status?: string; + subscription_number?: string; + sort?: string; +}; +type OrderGetsAllOrdersForZoomPartnerResponse = { + order_list?: { + order_reference_id?: string; + order_number?: string; + status?: string; + order_type?: string; + invoice_owner_crm_account_number?: string; + end_customer_account_name?: string; + end_customer_account_number?: string; + end_customer_crm_account_number?: string; + sub_reseller_name?: string; + sub_reseller_crm_account_number?: string; + creation_date?: string; + effective_date?: string; + net_amount?: { + amount?: number; + currency?: string; + }; + updated_date?: string; + trade_screening?: boolean; + deal_reg_number?: string; + po_number?: string; + }[]; +}; +type OrderGetsOrderDetailsByOrderReferenceIDPathParams = { + orderReferenceId: string; +}; +type OrderGetsOrderDetailsByOrderReferenceIDResponse = { + header?: { + order_reference_id?: string; + order_type?: string; + order_description?: string; + status?: string; + order_number?: string; + deal_reg_number?: string; + order_date: string; + po_number?: string; + trade_screening?: boolean; + order_metrics?: { + tcv?: number; + tcb?: number; + mrr?: number; + total_discount_pct?: number; + }; + additional_attributes?: { + name?: string; + value?: string; + }[]; + }; + create_subscription?: { + subscription_number?: string; + end_customer_account?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + invoice_owner_account?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + sub_reseller?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + sold_to_contact?: { + crm_contact_number?: string; + first_name?: string; + last_name?: string; + email?: string; + }; + bill_to_contact?: { + crm_contact_number?: string; + first_name?: string; + last_name?: string; + email?: string; + }; + initial_term?: { + term_type: string; + period_type?: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + renewal_term?: { + term_type: string; + period_type?: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + agreement_dates?: { + contract_effective_date?: string; + service_activation_date?: string; + customer_acceptance_date?: string; + }; + sold_to_crm_contact_number?: string; + end_customer_language?: string; + payment_term?: string; + service_start_date?: string; + paid_period_start_date?: string; + free_months_included?: boolean; + free_months_reason_code?: string; + auto_renew?: boolean; + currency?: string; + deal_reg_number?: string; + po_number?: string; + subscription_metrics?: { + subscription_number?: string; + tcv?: number; + tcb?: number; + mrr?: number; + total_discount_pct?: number; + }; + offers?: { + offer_id?: string; + offer_name?: string; + sku?: string; + offer_price_list_id?: string; + partner_sku_code?: string; + offer_price_list_name?: string; + quantity?: number; + start_date?: string; + end_date?: string; + charges?: { + charge_model?: string; + charge_type?: string; + sale_price?: { + amount?: number; + currency?: string; + }; + net_price?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + discounts?: { + discount_type: string; + percent_value?: number; + amount_value?: number; + apply_to: string; + discount_level: string; + }[]; + }[]; + usage_based_charge?: boolean; + }[]; + add_ons?: { + offer_id?: string; + offer_name?: string; + sku?: string; + offer_price_list_id?: string; + partner_sku_code?: string; + offer_price_list_name?: string; + quantity?: number; + start_date?: string; + end_date?: string; + charges?: { + charge_model?: string; + charge_type?: string; + sale_price?: { + amount?: number; + currency?: string; + }; + net_price?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + discounts?: { + discount_type: string; + percent_value?: number; + amount_value?: number; + apply_to: string; + discount_level: string; + }[]; + }[]; + usage_based_charge?: boolean; + }[]; + }[]; + amend_subscriptions?: { + subscription_number?: string; + end_customer_account?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + invoice_owner_account?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + sub_reseller?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + sold_to_contact?: { + crm_account_number?: string; + first_name?: string; + last_name?: string; + email?: string; + }; + bill_to_contact?: { + crm_account_number?: string; + first_name?: string; + last_name?: string; + email?: string; + }; + currency?: string; + deal_reg_number?: string; + po_number?: string; + subscription_metrics?: { + subscription_number?: string; + tcv?: number; + tcb?: number; + mrr?: number; + total_discount_pct?: number; + }; + add_offers?: { + offer_id?: string; + offer_name?: string; + sku?: string; + offer_price_list_id?: string; + partner_sku_code?: string; + offer_price_list_name?: string; + quantity?: number; + start_date?: string; + end_date?: string; + charges?: { + charge_model?: string; + charge_type?: string; + sale_price?: { + amount?: number; + currency?: string; + }; + net_price?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + discounts?: { + discount_type: string; + percent_value?: number; + amount_value?: number; + apply_to: string; + discount_level: string; + }[]; + }[]; + usage_based_charge?: boolean; + }[]; + add_add_ons?: { + offer_id?: string; + offer_name?: string; + sku?: string; + offer_price_list_id?: string; + partner_sku_code?: string; + offer_price_list_name?: string; + quantity?: number; + start_date?: string; + end_date?: string; + charges?: { + charge_model?: string; + charge_type?: string; + sale_price?: { + amount?: number; + currency?: string; + }; + net_price?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + discounts?: { + discount_type: string; + percent_value?: number; + amount_value?: number; + apply_to: string; + discount_level: string; + }[]; + }[]; + usage_based_charge?: boolean; + }[]; + upgrade_offers?: { + offer_id?: string; + offer_name?: string; + sku?: string; + offer_price_list_id?: string; + partner_sku_code?: string; + offer_price_list_name?: string; + old_offer_price_list_id?: string; + old_partner_sku_code?: string; + quantity?: number; + start_date?: string; + end_date?: string; + charges?: { + charge_model?: string; + charge_type?: string; + sale_price?: { + amount?: number; + currency?: string; + }; + net_price?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + discounts?: { + discount_type: string; + percent_value?: number; + amount_value?: number; + apply_to: string; + discount_level: string; + }[]; + }[]; + usage_based_charge?: boolean; + }[]; + remove_offers?: { + offer_id?: string; + offer_name?: string; + sku?: string; + offer_price_list_id?: string; + partner_sku_code?: string; + offer_price_list_name?: string; + quantity?: number; + start_date?: string; + end_date?: string; + charges?: { + charge_model?: string; + charge_type?: string; + sale_price?: { + amount?: number; + currency?: string; + }; + net_price?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + discounts?: { + discount_type: string; + percent_value?: number; + amount_value?: number; + apply_to: string; + discount_level: string; + }[]; + }[]; + usage_based_charge?: boolean; + }[]; + update_offers?: { + offer_id?: string; + offer_name?: string; + sku?: string; + offer_price_list_id?: string; + partner_sku_code?: string; + offer_price_list_name?: string; + quantity?: number; + start_date?: string; + end_date?: string; + charges?: { + charge_model?: string; + charge_type?: string; + sale_price?: { + amount?: number; + currency?: string; + }; + net_price?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + discounts?: { + discount_type: string; + percent_value?: number; + amount_value?: number; + apply_to: string; + discount_level: string; + }[]; + }[]; + usage_based_charge?: boolean; + }[]; + cancel_subscription?: { + cancel_by?: string; + cancel_on?: string; + cancel_reason?: string; + }; + renew_subscription?: { + renewal_term?: { + term_type: string; + period_type?: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + }; + update_subscription?: { + auto_renew?: boolean; + sold_to_crm_contact_number?: string; + end_customer_language?: string; + }; + }[]; + errors?: { + error_code?: string; + error_description?: string; + }[]; +}; +type ProductCatalogGetsZoomProductCatalogForZoomPartnerRequestBody = { + filter_options: { + filter_by: string; + filter_value: string; + operand?: string; + }[]; + eligibility_criteria?: { + only_trial_eligible?: boolean; + only_base_plans?: boolean; + upgrade_offer_id?: string; + }; +}; +type ProductCatalogGetsZoomProductCatalogForZoomPartnerResponse = { + offers: { + offer_id: string; + offer_name: string; + offer_desc?: string; + offer_type?: string; + z_product_category?: string; + sku?: string; + status?: string; + start_date?: string; + end_date?: string; + offer_products?: { + product_name?: string; + product_id?: string; + product_family_id?: string; + product_family_name?: string; + product_group_id?: string; + product_group_name?: string; + product_type?: string; + sku?: string; + product_features?: { + feature_id?: string; + name?: string; + value_type?: string; + value?: string; + uom?: string; + }[]; + price_list?: { + price_list_id?: string; + price_list_name?: string; + prices?: { + price_list_charge_id?: string; + partner_sku_code?: string; + charge_type?: string; + charge_model?: string; + name?: string; + uom?: string; + amount?: number; + currency?: string; + region?: string; + country?: string; + min_unit_quantity?: number; + status?: string; + start_date?: string; + end_date?: string; + price_tiers?: { + partner_sku_code?: string; + lower?: number; + upper?: number; + price?: number; + apply_rule?: string; + }[]; + }[]; + start_date?: string; + end_date?: string; + billing_period?: string; + status?: string; + eccn_value?: string; + }[]; + }[]; + offer_attributes?: { + name?: string; + uom?: string; + value_type?: string; + value?: string; + }[]; + pricebook?: { + price_list_id?: string; + price_list_name?: string; + prices?: { + price_list_charge_id?: string; + partner_sku_code?: string; + charge_type?: string; + charge_model?: string; + name?: string; + uom?: string; + amount?: number; + currency?: string; + region?: string; + country?: string; + min_unit_quantity?: number; + status?: string; + start_date?: string; + end_date?: string; + price_tiers?: { + partner_sku_code?: string; + lower?: number; + upper?: number; + price?: number; + apply_rule?: string; + }[]; + }[]; + start_date?: string; + end_date?: string; + billing_period?: string; + status?: string; + eccn_value?: string; + pricebook_attributes?: { + name?: string; + value_type?: string; + value?: string; + }[]; + }[]; + }[]; +}; +type ProductCatalogGetsDetailsForZoomProductOrOfferPathParams = { + offerId: number; +}; +type ProductCatalogGetsDetailsForZoomProductOrOfferResponse = { + offer_id: string; + offer_name: string; + offer_desc?: string; + offer_type?: string; + z_product_category?: string; + sku?: string; + status?: string; + start_date?: string; + end_date?: string; + offer_products?: { + product_name?: string; + product_id?: string; + product_family_id?: string; + product_family_name?: string; + product_group_id?: string; + product_group_name?: string; + product_type?: string; + sku?: string; + product_features?: { + feature_id?: string; + name?: string; + value_type?: string; + value?: string; + uom?: string; + }[]; + price_list?: { + price_list_id?: string; + price_list_name?: string; + prices?: { + price_list_charge_id?: string; + partner_sku_code?: string; + charge_type?: string; + charge_model?: string; + name?: string; + uom?: string; + amount?: number; + currency?: string; + region?: string; + country?: string; + min_unit_quantity?: number; + status?: string; + start_date?: string; + end_date?: string; + price_tiers?: { + partner_sku_code?: string; + lower?: number; + upper?: number; + price?: number; + apply_rule?: string; + }[]; + }[]; + start_date?: string; + end_date?: string; + billing_period?: string; + status?: string; + eccn_value?: string; + }[]; + }[]; + offer_attributes?: { + name?: string; + uom?: string; + value_type?: string; + value?: string; + }[]; + pricebook?: { + price_list_id?: string; + price_list_name?: string; + prices?: { + price_list_charge_id?: string; + partner_sku_code?: string; + charge_type?: string; + charge_model?: string; + name?: string; + uom?: string; + amount?: number; + currency?: string; + country?: string; + min_unit_quantity?: number; + status?: string; + start_date?: string; + end_date?: string; + price_tiers?: { + partner_sku_code?: string; + lower?: number; + upper?: number; + price?: number; + apply_rule?: string; + }[]; + }[]; + start_date?: string; + end_date?: string; + billing_period?: string; + status?: string; + eccn_value?: string; + pricebook_attributes?: { + name?: string; + value_type?: string; + value?: string; + }[]; + }[]; +}; +type ProductCatalogGetsPricebookInDownloadableFileQueryParams = { + currency?: string; + file_type?: string; +}; +type SubscriptionGetsSubscriptionsForZoomPartnerQueryParams = { + page_size?: number; + sort?: string; + status?: string; + start?: string; + end?: string; + duration?: string; + end_customer_name?: string; + end_customer_crm_account_number?: string; + end_customer_zoom_account_number?: string; + sub_reseller_name?: string; + sub_reseller_crm_account_number?: string; + subscription_number?: string; + invoice_owner_crm_account_number?: string; + next_page_token?: string; +}; +type SubscriptionGetsSubscriptionsForZoomPartnerResponse = { + next_page_token?: string; + subscription_list?: { + subscription_number?: string; + subscription_status?: string; + subscription_owner?: { + crm_account_number?: string; + account_name?: string; + zoom_account_number?: string; + }; + invoice_owner?: { + crm_account_number?: string; + account_name?: string; + zoom_account_number?: string; + }; + start_date?: string; + end_date?: string; + order_number?: string; + sub_reseller_name?: string; + sold_to_email?: string; + mrr?: { + gross_amount?: number; + net_amount?: number; + currency?: string; + }; + auto_renew?: boolean; + trade_screening?: boolean; + }[]; +}; +type SubscriptionGetsSubscriptionDetailsForGivenSubscriptionNumberPathParams = { + subscriptionNumber: string; +}; +type SubscriptionGetsSubscriptionDetailsForGivenSubscriptionNumberResponse = { + subscription_number?: string; + status?: string; + payment_term?: string; + service_start_date?: string; + paid_period_start_date?: string; + free_months_included?: boolean; + free_months_reason_code?: string; + deal_reg_number?: string; + po_number?: string; + currency?: string; + end_customer_account?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + invoice_owner_account?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + sub_reseller?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + address_type: string; + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + agreement_dates?: { + contract_effective_date?: string; + service_activation_date?: string; + customer_acceptance_date?: string; + }; + initial_term?: { + term_type: string; + period_type?: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + renewal_term?: { + term_type: string; + period_type?: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + current_term?: { + term_type: string; + period_type?: string; + term_period?: number; + start_date?: string; + end_date?: string; + }; + auto_renew?: boolean; + start_date?: string; + end_date?: string; + invoice_separately?: boolean; + contracted_mrr?: number; + mrr?: { + amount?: number; + currency?: string; + }; + bill_to?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + sold_to?: { + zoom_account_number?: string; + crm_account_number?: string; + account_name?: string; + address?: { + line_1: string; + line_2?: string; + line_3?: string; + city: string; + postal_code?: string; + state: string; + country: string; + }; + }; + sold_to_contact?: { + crm_contact_number?: string; + first_name?: string; + last_name?: string; + email?: string; + }; + bill_to_contact?: { + crm_contact_number?: string; + first_name?: string; + last_name?: string; + email?: string; + }; + subscription_lines?: { + offer_id?: string; + offer_name?: string; + sku?: string; + offer_price_list_id?: string; + offer_price_list_name?: string; + quantity?: number; + start_date?: string; + end_date?: string; + status?: string; + charges?: { + charge_model?: string; + charge_type?: string; + sale_price?: { + amount?: number; + currency?: string; + }; + net_price?: { + amount?: number; + currency?: string; + }; + net_amount?: { + amount?: number; + currency?: string; + }; + discounts?: { + discount_type: string; + percent_value?: number; + amount_value?: number; + apply_to: string; + discount_level: string; + }[]; + }[]; + offer_attributes?: { + name?: string; + uom?: string; + value_type?: string; + value?: string; + }[]; + }[]; + created_date?: string; + updated_date?: string; +}; +type SubscriptionGetsSubscriptionChangesVersionsForGivenSubscriptionNumberPathParams = { + subscriptionNumber: string; +}; +type SubscriptionGetsSubscriptionChangesVersionsForGivenSubscriptionNumberResponse = { + subscription_number?: string; + status?: string; + start_date?: string; + end_date?: string; + subscription_versions?: { + sequence?: number; + version?: number; + latest_version?: boolean; + action?: string[]; + start_date?: string; + end_date?: string; + mrr?: number; + currency?: string; + }[]; +}; +declare class CommerceEndpoints extends WebEndpoints { + readonly accountManagement: { + createEndCustomerAccount: (_: object & { + body: AccountManagementCreateEndCustomerAccountRequestBody; + }) => Promise>; + addContactsToExistingEndCustomerOrYourOwnAccount: (_: { + path: AccountManagementAddContactsToExistingEndCustomerOrYourOwnAccountPathParams; + } & { + body?: AccountManagementAddContactsToExistingEndCustomerOrYourOwnAccountRequestBody; + } & object) => Promise>; + getsListOfAllAccountsAssociatedWithZoomPartnerSubResellerByAccountType: (_: object & { + query?: AccountManagementGetsListOfAllAccountsAssociatedWithZoomPartnerSubResellerByAccountTypeQueryParams; + }) => Promise>; + getAccountDetailsForZoomPartnerSubResellerEndCustomer: (_: { + path: AccountManagementGetAccountDetailsForZoomPartnerSubResellerEndCustomerPathParams; + } & object) => Promise>; + }; + readonly billing: { + getsAllBillingDocumentsForDistributorOrReseller: (_: object & { + query?: BillingGetsAllBillingDocumentsForDistributorOrResellerQueryParams; + }) => Promise>; + getsPDFDocumentForBillingDocumentID: (_: { + path: BillingGetsPDFDocumentForBillingDocumentIDPathParams; + } & object) => Promise>; + getDetailedInformationAboutSpecificInvoiceForDistributorOrReseller: (_: { + path: BillingGetDetailedInformationAboutSpecificInvoiceForDistributorOrResellerPathParams; + } & object & { + query?: BillingGetDetailedInformationAboutSpecificInvoiceForDistributorOrResellerQueryParams; + }) => Promise>; + }; + readonly dealRegistration: { + retrievesAllValidZoomCampaignsWhichDealRegistrationCanBeAssociatedWith: (_: object & { + query: DealRegistrationRetrievesAllValidZoomCampaignsWhichDealRegistrationCanBeAssociatedWithQueryParams; + }) => Promise>; + createsNewDealRegistrationForPartner: (_: object & { + body: DealRegistrationCreatesNewDealRegistrationForPartnerRequestBody; + }) => Promise>; + getsAllValidDealRegistrationsForPartner: (_: object & { + query: DealRegistrationGetsAllValidDealRegistrationsForPartnerQueryParams; + }) => Promise>; + getsDetailsForDealRegistrationByDealRegistrationNumber: (_: { + path: DealRegistrationGetsDetailsForDealRegistrationByDealRegistrationNumberPathParams; + } & object) => Promise>; + updatesExistingDealRegistration: (_: { + path: DealRegistrationUpdatesExistingDealRegistrationPathParams; + } & { + body?: DealRegistrationUpdatesExistingDealRegistrationRequestBody; + } & object) => Promise>; + }; + readonly order: { + createsSubscriptionOrderForZoomPartner: (_: object & { + body: OrderCreatesSubscriptionOrderForZoomPartnerRequestBody; + }) => Promise>; + previewDeltaOrderMetricsAndSubscriptionsInOrder: (_: object & { + body: OrderPreviewDeltaOrderMetricsAndSubscriptionsInOrderRequestBody; + }) => Promise>; + getsAllOrdersForZoomPartner: (_: object & { + query?: OrderGetsAllOrdersForZoomPartnerQueryParams; + }) => Promise>; + getsOrderDetailsByOrderReferenceID: (_: { + path: OrderGetsOrderDetailsByOrderReferenceIDPathParams; + } & object) => Promise>; + }; + readonly productCatalog: { + getsZoomProductCatalogForZoomPartner: (_: object & { + body: ProductCatalogGetsZoomProductCatalogForZoomPartnerRequestBody; + }) => Promise>; + getsDetailsForZoomProductOrOffer: (_: { + path: ProductCatalogGetsDetailsForZoomProductOrOfferPathParams; + } & object) => Promise>; + getsPricebookInDownloadableFile: (_: object & { + query?: ProductCatalogGetsPricebookInDownloadableFileQueryParams; + }) => Promise>; + }; + readonly subscription: { + getsSubscriptionsForZoomPartner: (_: object & { + query?: SubscriptionGetsSubscriptionsForZoomPartnerQueryParams; + }) => Promise>; + getsSubscriptionDetailsForGivenSubscriptionNumber: (_: { + path: SubscriptionGetsSubscriptionDetailsForGivenSubscriptionNumberPathParams; + } & object) => Promise>; + getsSubscriptionChangesVersionsForGivenSubscriptionNumber: (_: { + path: SubscriptionGetsSubscriptionChangesVersionsForGivenSubscriptionNumberPathParams; + } & object) => Promise>; + }; +} + +type CommerceS2SAuthOptions = CommonClientOptions; +declare class CommerceS2SAuthClient = CommerceS2SAuthOptions> extends ProductClient { + protected initAuth({ accountId, clientId, clientSecret, tokenStore }: OptionsType): S2SAuth; + protected initEndpoints(auth: S2SAuth, options: OptionsType): CommerceEndpoints; + protected initEventProcessor(): never; +} + +export { ApiResponseError, AwsLambdaReceiver, AwsReceiverRequestError, ClientCredentialsRawResponseError, CommerceEndpoints, CommerceS2SAuthClient, CommonHttpRequestError, ConsoleLogger, HTTPReceiverConstructionError, HTTPReceiverPortNotNumberError, HTTPReceiverRequestError, HttpReceiver, LogLevel, OAuthInstallerNotInitializedError, OAuthStateVerificationFailedError, OAuthTokenDoesNotExistError, OAuthTokenFetchFailedError, OAuthTokenRawResponseError, OAuthTokenRefreshFailedError, ProductClientConstructionError, ReceiverInconsistentStateError, ReceiverOAuthFlowError, S2SRawResponseError, StatusCode, isCoreError, isStateStore }; +export type { AccountManagementAddContactsToExistingEndCustomerOrYourOwnAccountPathParams, AccountManagementAddContactsToExistingEndCustomerOrYourOwnAccountRequestBody, AccountManagementAddContactsToExistingEndCustomerOrYourOwnAccountResponse, AccountManagementCreateEndCustomerAccountRequestBody, AccountManagementCreateEndCustomerAccountResponse, AccountManagementGetAccountDetailsForZoomPartnerSubResellerEndCustomerPathParams, AccountManagementGetAccountDetailsForZoomPartnerSubResellerEndCustomerResponse, AccountManagementGetsListOfAllAccountsAssociatedWithZoomPartnerSubResellerByAccountTypeQueryParams, AccountManagementGetsListOfAllAccountsAssociatedWithZoomPartnerSubResellerByAccountTypeResponse, BillingGetDetailedInformationAboutSpecificInvoiceForDistributorOrResellerPathParams, BillingGetDetailedInformationAboutSpecificInvoiceForDistributorOrResellerQueryParams, BillingGetDetailedInformationAboutSpecificInvoiceForDistributorOrResellerResponse, BillingGetsAllBillingDocumentsForDistributorOrResellerQueryParams, BillingGetsAllBillingDocumentsForDistributorOrResellerResponse, BillingGetsPDFDocumentForBillingDocumentIDPathParams, ClientCredentialsToken, CommerceS2SAuthOptions, DealRegistrationCreatesNewDealRegistrationForPartnerRequestBody, DealRegistrationCreatesNewDealRegistrationForPartnerResponse, DealRegistrationGetsAllValidDealRegistrationsForPartnerQueryParams, DealRegistrationGetsAllValidDealRegistrationsForPartnerResponse, DealRegistrationGetsDetailsForDealRegistrationByDealRegistrationNumberPathParams, DealRegistrationGetsDetailsForDealRegistrationByDealRegistrationNumberResponse, DealRegistrationRetrievesAllValidZoomCampaignsWhichDealRegistrationCanBeAssociatedWithQueryParams, DealRegistrationRetrievesAllValidZoomCampaignsWhichDealRegistrationCanBeAssociatedWithResponse, DealRegistrationUpdatesExistingDealRegistrationPathParams, DealRegistrationUpdatesExistingDealRegistrationRequestBody, HttpReceiverOptions, JwtToken, Logger, OAuthToken, OrderCreatesSubscriptionOrderForZoomPartnerRequestBody, OrderCreatesSubscriptionOrderForZoomPartnerResponse, OrderGetsAllOrdersForZoomPartnerQueryParams, OrderGetsAllOrdersForZoomPartnerResponse, OrderGetsOrderDetailsByOrderReferenceIDPathParams, OrderGetsOrderDetailsByOrderReferenceIDResponse, OrderPreviewDeltaOrderMetricsAndSubscriptionsInOrderRequestBody, OrderPreviewDeltaOrderMetricsAndSubscriptionsInOrderResponse, ProductCatalogGetsDetailsForZoomProductOrOfferPathParams, ProductCatalogGetsDetailsForZoomProductOrOfferResponse, ProductCatalogGetsPricebookInDownloadableFileQueryParams, ProductCatalogGetsZoomProductCatalogForZoomPartnerRequestBody, ProductCatalogGetsZoomProductCatalogForZoomPartnerResponse, Receiver, ReceiverInitOptions, S2SAuthToken, StateStore, SubscriptionGetsSubscriptionChangesVersionsForGivenSubscriptionNumberPathParams, SubscriptionGetsSubscriptionChangesVersionsForGivenSubscriptionNumberResponse, SubscriptionGetsSubscriptionDetailsForGivenSubscriptionNumberPathParams, SubscriptionGetsSubscriptionDetailsForGivenSubscriptionNumberResponse, SubscriptionGetsSubscriptionsForZoomPartnerQueryParams, SubscriptionGetsSubscriptionsForZoomPartnerResponse, TokenStore }; diff --git a/commerce/commerce.mjs b/commerce/commerce.mjs new file mode 100644 index 0000000..ef8010e --- /dev/null +++ b/commerce/commerce.mjs @@ -0,0 +1,1014 @@ +import { createHmac, randomBytes } from 'node:crypto'; +import { createServer as createServer$1 } from 'node:http'; +import { createServer } from 'node:https'; +import axios from 'axios'; +import dayjs from 'dayjs'; +import { Buffer as Buffer$1 } from 'node:buffer'; +import { SignJWT, jwtVerify } from 'jose'; +import FormData from 'form-data'; +import os from 'node:os'; +import { basename } from 'node:path'; + +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +const isStateStore = (obj) => typeof obj.generateState === "function" && typeof obj.verifyState === "function"; + +const createRivetErrors = (errors) => ({ + createError: (errorCode) => class extends Error { + errorCode = errors[errorCode]; + constructor(message, options) { + const errorMessage = (message ?? + (options?.cause instanceof Error ? options.cause.message : errorCode)); + super(errorMessage, options); + this.name = errorCode; + Object.setPrototypeOf(this, new.target.prototype); + } + }, + isError: (obj, key) => key ? + Object.keys(errors).some((code) => code === key) && + typeof obj.errorCode === "string" && + obj.errorCode === errors[key] + : typeof obj.errorCode === "string" +}); + +const coreErrors = { + ApiResponseError: "zoom_rivet_api_response_error", + AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error", + ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error", + S2SRawResponseError: "zoom_rivet_s2s_raw_response_error", + CommonHttpRequestError: "zoom_rivet_common_http_request_error", + ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error", + ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error", + HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error", + HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error", + HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error", + OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error", + OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error", + OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error", + OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error", + OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error", + OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error", + ProductClientConstructionError: "zoom_rivet_product_client_construction_error" +}; +const { createError: createCoreError, isError: isCoreError } = createRivetErrors(coreErrors); +const ApiResponseError = createCoreError("ApiResponseError"); +const AwsReceiverRequestError = createCoreError("AwsReceiverRequestError"); +const ClientCredentialsRawResponseError = createCoreError("ClientCredentialsRawResponseError"); +const S2SRawResponseError = createCoreError("S2SRawResponseError"); +const CommonHttpRequestError = createCoreError("CommonHttpRequestError"); +const ReceiverInconsistentStateError = createCoreError("ReceiverInconsistentStateError"); +const ReceiverOAuthFlowError = createCoreError("ReceiverOAuthFlowError"); +const HTTPReceiverConstructionError = createCoreError("HTTPReceiverConstructionError"); +const HTTPReceiverPortNotNumberError = createCoreError("HTTPReceiverPortNotNumberError"); +const HTTPReceiverRequestError = createCoreError("HTTPReceiverRequestError"); +const OAuthInstallerNotInitializedError = createCoreError("OAuthInstallerNotInitializedError"); +const OAuthTokenDoesNotExistError = createCoreError("OAuthTokenDoesNotExistError"); +const OAuthTokenFetchFailedError = createCoreError("OAuthTokenFetchFailedError"); +const OAuthTokenRawResponseError = createCoreError("OAuthTokenRawResponseError"); +const OAuthTokenRefreshFailedError = createCoreError("OAuthTokenRefreshFailedError"); +const OAuthStateVerificationFailedError = createCoreError("OAuthStateVerificationFailedError"); +const ProductClientConstructionError = createCoreError("ProductClientConstructionError"); + +var LogLevel; +(function (LogLevel) { + LogLevel["ERROR"] = "error"; + LogLevel["WARN"] = "warn"; + LogLevel["INFO"] = "info"; + LogLevel["DEBUG"] = "debug"; +})(LogLevel || (LogLevel = {})); +class ConsoleLogger { + level; + name; + static labels = (() => { + const entries = Object.entries(LogLevel); + const map = entries.map(([key, value]) => [value, `[${key}] `]); + return new Map(map); + })(); + static severity = { + [LogLevel.ERROR]: 400, + [LogLevel.WARN]: 300, + [LogLevel.INFO]: 200, + [LogLevel.DEBUG]: 100 + }; + constructor() { + this.level = LogLevel.INFO; + this.name = ""; + } + getLevel() { + return this.level; + } + setLevel(level) { + this.level = level; + } + setName(name) { + this.name = name; + } + debug(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(LogLevel.DEBUG, this.level)) { + console.debug(ConsoleLogger.labels.get(LogLevel.DEBUG), this.name, ...msg); + } + } + info(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(LogLevel.INFO, this.level)) { + console.info(ConsoleLogger.labels.get(LogLevel.INFO), this.name, ...msg); + } + } + warn(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(LogLevel.WARN, this.level)) { + console.warn(ConsoleLogger.labels.get(LogLevel.WARN), this.name, ...msg); + } + } + error(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(LogLevel.ERROR, this.level)) { + console.error(ConsoleLogger.labels.get(LogLevel.ERROR), this.name, ...msg); + } + } + static isMoreOrEqualSevere(a, b) { + return ConsoleLogger.severity[a] >= ConsoleLogger.severity[b]; + } +} + +/** @internal */ +const hashUrlValidationEvent = ({ payload: { plainToken } }, webhooksSecretToken) => ({ + encryptedToken: createHmac("sha256", webhooksSecretToken).update(plainToken).digest("hex"), + plainToken +}); +const isHashedUrlValidation = (obj) => typeof obj.encryptedToken === "string" && + typeof obj.plainToken === "string"; +const isRawUrlValidationEvent = (obj) => obj.event === "endpoint.url_validation" && typeof obj.payload.plainToken === "string"; +const isSkeletonEvent = (obj) => typeof obj.event === "string"; +class CommonHttpRequest { + headers; + payload; + webhooksSecretToken; + constructor(headers, payload, webhooksSecretToken) { + this.headers = headers; + this.payload = payload; + this.webhooksSecretToken = webhooksSecretToken; + } + static buildFromAwsEvent({ body, headers, isBase64Encoded }, webhooksSecretToken) { + try { + const rawBody = body ?? ""; + const decodedBody = isBase64Encoded ? Buffer.from(rawBody, "base64").toString("ascii") : rawBody; + const payload = JSON.parse(decodedBody); + return new CommonHttpRequest(headers, payload, webhooksSecretToken); + } + catch (err) { + throw err instanceof SyntaxError ? + new CommonHttpRequestError("Failed to parse payload string to JSON.", err) + : err; + } + } + static async buildFromIncomingMessage(incomingMessage, webhooksSecretToken) { + const bufferAsString = () => { + return new Promise((resolve, reject) => { + const body = []; + incomingMessage.on("data", (chunk) => body.push(chunk)); + incomingMessage.on("error", (err) => { + reject(err); + }); + incomingMessage.on("end", () => { + resolve(Buffer.concat(body).toString()); + }); + }); + }; + try { + const payload = JSON.parse(await bufferAsString()); + return new CommonHttpRequest(incomingMessage.headers, payload, webhooksSecretToken); + } + catch (err) { + if (err instanceof SyntaxError) { + throw new CommonHttpRequestError("Failed to parse payload string to JSON.", err); + } + throw err; + } + } + isEventVerified() { + const { signature, requestTimestamp } = this.parseHeaders(); + const messageToVerify = `v0:${requestTimestamp.toString()}:${JSON.stringify(this.payload)}`; + const hashToVerify = createHmac("sha256", this.webhooksSecretToken).update(messageToVerify).digest("hex"); + const signatureToVerify = `v0=${hashToVerify}`; + return signatureToVerify === signature; + } + parseHeaders() { + const findHeader = (header) => { + const foundHeader = Object.keys(this.headers).find((key) => key.toLowerCase() === header.toLowerCase()); + return foundHeader && this.headers[foundHeader]; + }; + const headerSignature = findHeader("x-zm-signature"); + const headerRequestTimestamp = findHeader("x-zm-request-timestamp"); + if (!headerSignature && !headerRequestTimestamp) { + throw new CommonHttpRequestError("Request payload must have signature and request timestamp from Zoom."); + } + return { + signature: headerSignature, + requestTimestamp: Number(headerRequestTimestamp) + }; + } + processEvent() { + if (!isSkeletonEvent(this.payload)) { + throw new CommonHttpRequestError("Request payload structure does not match expected from Zoom."); + } + if (!this.isEventVerified()) { + throw new CommonHttpRequestError("Failed to verify event originated from Zoom."); + } + if (isRawUrlValidationEvent(this.payload)) { + return hashUrlValidationEvent(this.payload, this.webhooksSecretToken); + } + return this.payload; + } +} + +var StatusCode; +(function (StatusCode) { + StatusCode[StatusCode["OK"] = 200] = "OK"; + StatusCode[StatusCode["TEMPORARY_REDIRECT"] = 302] = "TEMPORARY_REDIRECT"; + StatusCode[StatusCode["BAD_REQUEST"] = 400] = "BAD_REQUEST"; + StatusCode[StatusCode["NOT_FOUND"] = 404] = "NOT_FOUND"; + StatusCode[StatusCode["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED"; + StatusCode[StatusCode["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR"; +})(StatusCode || (StatusCode = {})); + +class AwsLambdaReceiver { + eventEmitter; + webhooksSecretToken; + constructor({ webhooksSecretToken }) { + this.webhooksSecretToken = webhooksSecretToken; + } + buildResponse(statusCode, body) { + return { + body: JSON.stringify(body), + headers: { "Content-Type": "application/json" }, + statusCode + }; + } + canInstall() { + return false; + } + init({ eventEmitter }) { + this.eventEmitter = eventEmitter; + } + start() { + return async (event, context) => { + console.debug("Processing Lambda event ", JSON.stringify(event), " with context ", JSON.stringify(context)); + try { + const request = CommonHttpRequest.buildFromAwsEvent(event, this.webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + return this.buildResponse(StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + return this.buildResponse(StatusCode.OK, { message: "Zoom event processed successfully." }); + } + } + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + return this.buildResponse(StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + return this.buildResponse(StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } + } + }; + } + async stop() { + return Promise.resolve(); + } +} + +const prependSlashes = (strs) => { + const rawStrs = Array.isArray(strs) ? strs : [strs]; + const mappedStrs = rawStrs.map((rawStr) => (rawStr.startsWith("/") ? rawStr : `/${rawStr}`)); + return (Array.isArray(strs) ? mappedStrs : mappedStrs[0]); +}; + +class TokenMemoryStore { + currentToken; + getLatestToken() { + return this.currentToken; + } + storeToken(token) { + this.currentToken = token; + } +} + +/** @internal */ +const EXPIRATION_DELTA_SECONDS = 60; +/** @internal */ +const OAUTH_BASE_URL = "https://zoom.us"; +/** @internal */ +const OAUTH_TOKEN_PATH = "/oauth/token"; +/** + * {@link Auth} is the base implementation of authentication for Zoom's APIs. + * + * It only requires a `clientId` and `tokenStore`, as these options are shared across + * all authentication implementations, namely OAuth and server-to-server auth (client + * credentials, JWT, and server-to-server OAuth.) + */ +class Auth { + clientId; + clientSecret; + tokenStore; + logger; + constructor({ clientId, clientSecret, tokenStore, logger }) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.tokenStore = tokenStore ?? new TokenMemoryStore(); + this.logger = logger; + } + getBasicAuthorization() { + const clientCredentials = `${this.clientId}:${this.clientSecret}`; + return Buffer$1.from(clientCredentials).toString("base64"); + } + isAlmostExpired(isoTime) { + const currentDate = dayjs(); + return dayjs(isoTime).diff(currentDate, "seconds") <= EXPIRATION_DELTA_SECONDS; + } + async makeOAuthTokenRequest(grantType, payload) { + return await axios({ + method: "POST", + url: new URL(OAUTH_TOKEN_PATH, OAUTH_BASE_URL).toString(), + headers: { + Authorization: `Basic ${this.getBasicAuthorization()}`, + "Content-Type": "application/x-www-form-urlencoded" + }, + data: new URLSearchParams({ grant_type: grantType, ...payload }), + validateStatus: (status) => status >= 200 && status <= 299 + }); + } +} + +const DEFAULT_EXPIRATION_SECONDS = 300; // 5 minutes +/** @internal */ +const ISSUER_URN = "urn:zoom:rivet-sdk"; +class JwtStateStore { + encodedSecret; + expirationSeconds; + constructor({ expirationSeconds, stateSecret }) { + this.encodedSecret = new TextEncoder().encode(stateSecret); + this.expirationSeconds = expirationSeconds ?? DEFAULT_EXPIRATION_SECONDS; + } + async generateState() { + const issuedTime = dayjs(); + const expirationTime = issuedTime.add(this.expirationSeconds, "seconds"); + return await new SignJWT({ random: randomBytes(8).toString("hex") }) + .setProtectedHeader({ alg: "HS256", typ: "JWT" }) + .setExpirationTime(expirationTime.toDate()) + .setIssuedAt(issuedTime.toDate()) + .setIssuer(ISSUER_URN) + .sign(this.encodedSecret); + } + async verifyState(state) { + try { + await jwtVerify(state, this.encodedSecret, { + algorithms: ["HS256"], + issuer: ISSUER_URN, + typ: "JWT" + }); + } + catch (err) { + throw new OAuthStateVerificationFailedError(`Failed to verify OAuth state: ${err.name}.`, { + cause: err + }); + } + } +} + +const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; +const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds +const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; +/** + * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication + * is initiated server-side, but requires manual authorization from a user, by redirecting the user to Zoom. + * + * In addition to all required fields from {@link AuthOptions}, this class requires a `redirectUri`, as this + * value is appended to the authorization URL when the user is redirected to Zoom and subsequently redirected + * back to an endpoint on this server. + * + * @see {@link https://developers.zoom.us/docs/integrations/oauth/ | OAuth - Zoom Developers} + */ +class InteractiveAuth extends Auth { + installerOptions; + async getAuthorizationUrl() { + if (!this.installerOptions?.stateStore) { + throw new OAuthInstallerNotInitializedError("Cannot generate authorization URL, state store not initialized."); + } + const authUrl = new URL(OAUTH_AUTHORIZE_PATH, OAUTH_BASE_URL); + const generatedState = await Promise.resolve(this.installerOptions.stateStore.generateState()); + const { searchParams } = authUrl; + searchParams.set("client_id", this.clientId); + searchParams.set("redirect_uri", this.getFullRedirectUri()); + searchParams.set("response_type", "code"); + searchParams.set("state", generatedState); + return { + fullUrl: authUrl.toString(), + generatedState + }; + } + getFullRedirectUri() { + if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { + throw new OAuthInstallerNotInitializedError("Cannot generate full redirect URI, redirect URI or redirect URI path not initialized."); + } + return new URL(this.installerOptions.redirectUriPath, this.installerOptions.redirectUri).toString(); + } + // Don't return a type; we want it to be as narrow as possible (used for ReturnType). + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { + const updatedOptions = { + directInstall: Boolean(directInstall), + installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, + redirectUri, + redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE + }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } + this.installerOptions = updatedOptions; + return updatedOptions; + } +} + +const mergeDefaultOptions = (options, defaultOptions) => ({ ...defaultOptions, ...options }); + +const withDefaultTemplate = (cardContent, buttonContent) => ` + + + Zoom Rivet + + + + + + + + + +`; +/** + * Get the default HTML template that is shown to the developer/user when they visit the + * `installPath` endpoint, if Rivet currently has OAuth enabled. + * + * If `directInstall` is set to `true`, this function is not called; instead, the developer + * is directly redirected to Zoom's OAuth page. + */ +const defaultInstallTemplate = (authUrl) => withDefaultTemplate(`

Click the button below to navigate to Zoom to authorize your application for use with Rivet.

`, { href: authUrl, text: "Authorize with Zoom" }); +/** + * Get the default HTML template that is shown to the developer/user when they successfully + * authorize Rivet with a Zoom application. This is shown once they have already been redirected + * to Zoom, and the authorization attempt with Rivet was successful. + */ +const defaultCallbackSuccessTemplate = () => withDefaultTemplate(`

Your application has been successfully authorized with Rivet!

+

You may now close this page, or click the button below to redirect to Zoom's Marketplace.

`, { href: "https://marketplace.zoom.us", text: "Go to Marketplace" }); +/** + * Get the default HTML template that is shown to the developer when a known error occurs, meaning + * that the error is a core Rivet error. + */ +const defaultCallbackKnownErrorTemplate = (errName, errMessage) => withDefaultTemplate(`

An error occurred authorizing Rivet with Zoom.

+

[${errName}]: ${errMessage}

`); +/** + * Get the default HTML template that is shown to the developer when an unknown error occurs, + * meaning that the error is not known to be a core Rivet error and was thrown and not wrapped elsewhere. + */ +const defaultCallbackUnknownErrorTemplate = () => withDefaultTemplate(`

An unknown error occurred authorizing Rivet with Zoom. Please see stacktrace for details.

+

Please see stacktrace for further details.

`); + +const secureServerOptionKeys = [ + "ALPNProtocols", + "clientCertEngine", + "enableTrace", + "handshakeTimeout", + "rejectUnauthorized", + "requestCert", + "sessionTimeout", + "SNICallback", + "ticketKeys", + "pskCallback", + "pskIdentityHint", + "ca", + "cert", + "sigalgs", + "ciphers", + "clientCertEngine", + "crl", + "dhparam", + "ecdhCurve", + "honorCipherOrder", + "key", + "privateKeyEngine", + "privateKeyIdentifier", + "maxVersion", + "minVersion", + "passphrase", + "pfx", + "secureOptions", + "secureProtocol", + "sessionIdContext" +]; +class HttpReceiver { + /** @internal */ + static DEFAULT_ENDPOINT = "/zoom/events"; + eventEmitter; + interactiveAuth; + /** @internal */ + options; + server; + logger; + constructor(options) { + this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); + this.options.endpoints = prependSlashes(this.options.endpoints); + this.logger = + options.logger ?? + (() => { + const defaultLogger = new ConsoleLogger(); + defaultLogger.setLevel(options.logLevel ?? LogLevel.ERROR); + return defaultLogger; + })(); + } + canInstall() { + return true; + } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } + getServerCreator() { + return this.hasSecureOptions() ? createServer : createServer$1; + } + hasEndpoint(pathname) { + const { endpoints } = this.options; + return Array.isArray(endpoints) ? endpoints.includes(pathname) : endpoints === pathname; + } + hasSecureOptions() { + return Object.keys(this.options).some((option) => secureServerOptionKeys.includes(option)); + } + init({ eventEmitter, interactiveAuth }) { + this.eventEmitter = eventEmitter; + this.interactiveAuth = interactiveAuth; + } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } + start(port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { + const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; + this.logger.error(errorMessage); + throw new HTTPReceiverPortNotNumberError(errorMessage); + } + const listenPort = port ?? this.options.port; + return new Promise((resolve, reject) => { + this.server = this.getServerCreator()(this.options, (req, res) => void (async () => { + // `req.headers.host` should be used with care, as clients can manipulate this value. + // However, for this use case, the value is completely discarded and only `pathname` + // is used, which is why there's no further validation occurring. + const { pathname, searchParams } = new URL(req.url ?? "", `http://${req.headers.host ?? "localhost"}`); + const { interactiveAuth } = this; + this.logger.debug([pathname, searchParams]); + // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath + if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { + const { installerOptions } = interactiveAuth; + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); + await (installerOptions.directInstall ? + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); + return; + } + // The user has navigated to the redirect page; init the code + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); + try { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { + const errorMessage = "OAuth callback did not include code and/or state in request."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); + return; + } + catch (err) { + const htmlTemplate = isCoreError(err) ? + defaultCallbackKnownErrorTemplate(err.name, err.message) + : defaultCallbackUnknownErrorTemplate(); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); + return; + } + } + } + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, StatusCode.NOT_FOUND); + return; + } + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); + return; + } + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + } + } + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } + } + } + })()); + this.server.on("close", () => (this.server = undefined)); + this.server.on("error", (err) => { + this.logger.error(err.message); + reject(err); + }); + this.server.listen(listenPort, () => { + if (!this.server) { + throw new ReceiverInconsistentStateError(); + } + const { port: listeningPort } = this.server.address(); + this.logger.info(`Listening on port ${listeningPort.toString()}`); + resolve(this.server); + }); + }); + } + stop() { + if (!this.server) { + throw new ReceiverInconsistentStateError(); + } + return new Promise((resolve, reject) => { + this.server?.close((err) => { + if (err) { + this.logger.error(err.message); + reject(err); + } + }); + this.server = undefined; + resolve(); + }); + } + writeTemporaryRedirect(res, location, setCookie) { + return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } + res.writeHead(StatusCode.TEMPORARY_REDIRECT, { Location: location }); + res.end(() => { + resolve(); + }); + }); + } + writeResponse(res, statusCode, bodyContent, setCookie) { + return new Promise((resolve) => { + const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; + bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } + res.writeHead(statusCode, { "Content-Type": mimeType }); + res.end(bodyContent, () => { + resolve(); + }); + }); + } +} + +const version = "0.4.0"; +var packageJson = { + version: version}; + +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; +class WebEndpoints { + /** @internal */ + static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; + /** @internal */ + static DEFAULT_MIME_TYPE = "application/json"; + /** @internal */ + static DEFAULT_TIMEOUT = 0; + /** @internal */ + static GENERIC_ERROR_MESSAGE = "Request was unsuccessful with no further context"; + /** @internal */ + static TRACKING_ID_HEADER = "x-zm-trackingid"; + /** @internal */ + options; + constructor(options) { + this.options = mergeDefaultOptions(options, { + baseUrl: WebEndpoints.DEFAULT_BASE_URL, + hasCustomBaseUrl: typeof options.baseUrl !== "undefined", + timeout: WebEndpoints.DEFAULT_TIMEOUT + }); + } + buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }) { + // @ts-expect-error: Some arguments may not be present, but we pass them to makeRequest() anyway. + // prettier-ignore + // Next AST node is ignored by Prettier, even though it exceed maximum line length, because TypeScript + // won't allow ts-expect-error directive on multiple lines (https://github.com/Microsoft/TypeScript/issues/19573). + return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); + } + buildUserAgent() { + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + + `${basename(process.title)}/${process.version.replace("v", "")} ` + + `${os.platform()}/${os.release()}`); + } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } + getHeaders(bearerToken, contentType) { + return { + Accept: "application/json", + Authorization: `Bearer ${bearerToken}`, + "Content-Type": contentType, + "User-Agent": this.buildUserAgent() + }; + } + getRequestBody(args, mimeType) { + if (mimeType === "multipart/form-data") { + const formData = new FormData(); + Object.entries(args).forEach(([key, value]) => { + formData.append(key, value); + }); + return formData; + } + return args; + } + isOk(response) { + return response.status >= 200 && response.status <= 299; + } + isZoomResponseError(obj) { + return (typeof obj.code !== "undefined" && + typeof obj.message !== "undefined"); + } + async makeRequest(method, baseUrlOverride, url, requestContentType, bodyArgs, queryArgs) { + const { auth, baseUrl, doubleEncodeUrl, hasCustomBaseUrl, timeout } = this.options; + const bearerToken = await Promise.resolve(auth.getToken()); + const urlToSend = doubleEncodeUrl ? encodeURIComponent(encodeURIComponent(url)) : url; + const response = await axios({ + url: urlToSend, + method, + baseURL: hasCustomBaseUrl ? baseUrl : (baseUrlOverride ?? baseUrl), + headers: this.getHeaders(bearerToken, requestContentType), + params: queryArgs, + data: bodyArgs && this.getRequestBody(bodyArgs, requestContentType), + timeout: timeout, + beforeRedirect: (options) => { + options.headers = { + ...this.getHeaders(bearerToken, requestContentType), + ...options.headers + }; + }, + validateStatus: () => true // All responses are valid, not just 2xx + }); + if (!this.isOk(response)) { + const { status: statusCode } = response; + if (this.isZoomResponseError(response.data)) { + const { code: errorCode, message: errorMessage } = response.data; + throw new ApiResponseError(`[${statusCode.toString()}/${errorCode.toString()}]: "${errorMessage}"`); + } + throw new ApiResponseError(`[${statusCode.toString()}]: ${WebEndpoints.GENERIC_ERROR_MESSAGE}`); + } + return { + data: response.data, + statusCode: response.status, + trackingId: response.headers[WebEndpoints.TRACKING_ID_HEADER] + }; + } +} + +class CommerceEndpoints extends WebEndpoints { + accountManagement = { + createEndCustomerAccount: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/commerce/account` }), + addContactsToExistingEndCustomerOrYourOwnAccount: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ accountKey }) => `/commerce/account/${accountKey}/contacts` }), + getsListOfAllAccountsAssociatedWithZoomPartnerSubResellerByAccountType: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/accounts` }), + getAccountDetailsForZoomPartnerSubResellerEndCustomer: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ accountKey }) => `/commerce/accounts/${accountKey}` }) + }; + billing = { + getsAllBillingDocumentsForDistributorOrReseller: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/billing_documents` }), + getsPDFDocumentForBillingDocumentID: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ documentNumber }) => `/commerce/billing_documents/${documentNumber}/document` + }), + getDetailedInformationAboutSpecificInvoiceForDistributorOrReseller: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ invoiceNumber }) => `/commerce/invoices/${invoiceNumber}` }) + }; + dealRegistration = { + retrievesAllValidZoomCampaignsWhichDealRegistrationCanBeAssociatedWith: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/campaigns` }), + createsNewDealRegistrationForPartner: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/commerce/deal_registration` }), + getsAllValidDealRegistrationsForPartner: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/deal_registrations` }), + getsDetailsForDealRegistrationByDealRegistrationNumber: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ dealRegKey }) => `/commerce/deal_registrations/${dealRegKey}` }), + updatesExistingDealRegistration: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ dealRegKey }) => `/commerce/deal_registrations/${dealRegKey}` }) + }; + order = { + createsSubscriptionOrderForZoomPartner: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/commerce/order` }), + previewDeltaOrderMetricsAndSubscriptionsInOrder: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/commerce/order/preview` }), + getsAllOrdersForZoomPartner: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/orders` }), + getsOrderDetailsByOrderReferenceID: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ orderReferenceId }) => `/commerce/orders/${orderReferenceId}` }) + }; + productCatalog = { + getsZoomProductCatalogForZoomPartner: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/commerce/catalog` }), + getsDetailsForZoomProductOrOffer: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ offerId }) => `/commerce/catalog/${offerId}` }), + getsPricebookInDownloadableFile: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/pricebooks` }) + }; + subscription = { + getsSubscriptionsForZoomPartner: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/commerce/subscriptions` }), + getsSubscriptionDetailsForGivenSubscriptionNumber: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ subscriptionNumber }) => `/commerce/subscriptions/${subscriptionNumber}` }), + getsSubscriptionChangesVersionsForGivenSubscriptionNumber: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ subscriptionNumber }) => `/commerce/subscriptions/${subscriptionNumber}/versions` + }) + }; +} + +class S2SAuth extends Auth { + accountId; + constructor({ accountId, ...restOptions }) { + super(restOptions); + this.accountId = accountId; + } + assertRawToken(obj) { + if (typeof obj.access_token !== "string" || + typeof obj.expires_in !== "number" || + typeof obj.scope !== "string") { + throw new S2SRawResponseError(`Failed to match raw response ${JSON.stringify(obj)} to expected shape.`); + } + } + async fetchAccessToken() { + const response = await this.makeOAuthTokenRequest("account_credentials", { + account_id: this.accountId + }); + this.assertRawToken(response.data); + return this.mapAccessToken(response.data); + } + async getToken() { + const { tokenStore } = this; + const currentToken = await Promise.resolve(tokenStore.getLatestToken()); + if (currentToken && !this.isAlmostExpired(currentToken.expirationTimeIso)) { + return currentToken.accessToken; + } + const token = await this.fetchAccessToken(); + await Promise.resolve(tokenStore.storeToken(token)); + return token.accessToken; + } + mapAccessToken({ access_token, expires_in, scope }) { + return { + accessToken: access_token, + expirationTimeIso: dayjs().add(expires_in, "seconds").toISOString(), + scopes: scope.includes(" ") ? scope.split(" ") : [scope] + }; + } +} + +// Utility functions for determining if client options include custom receiver, or, if not, +// a webhooks secret token, as one of those is required! +const hasExplicitReceiver = (obj) => typeof obj.receiver !== "undefined"; +const hasWebhooksSecretToken = (obj) => typeof obj.webhooksSecretToken !== "undefined"; +const isReceiverDisabled = (options) => typeof options.disableReceiver !== "undefined" && options.disableReceiver; +const DEFAULT_HTTP_RECEIVER_PORT = 8080; +const DEFAULT_LOGLEVEL = LogLevel.ERROR; +class ProductClient { + auth; + endpoints; + webEventConsumer; + receiver; + constructor(options) { + this.auth = this.initAuth(options); + this.endpoints = this.initEndpoints(this.auth, options); + this.webEventConsumer = this.initEventProcessor(this.endpoints, options); + // Only create an instance of `this.receiver` if the developer did not explicitly disable it. + if (!isReceiverDisabled(options)) { + // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); + } + this.receiver = (hasExplicitReceiver(options) ? + options.receiver + : this.initDefaultReceiver(options)); + this.receiver.init({ + eventEmitter: this.webEventConsumer, + interactiveAuth: this.auth instanceof InteractiveAuth ? this.auth : undefined + }); + } + } + initDefaultReceiver({ port, webhooksSecretToken, logLevel }) { + return new HttpReceiver({ + port: port ?? DEFAULT_HTTP_RECEIVER_PORT, + webhooksSecretToken, + logLevel: logLevel ?? DEFAULT_LOGLEVEL + }); + } + async start() { + if (!this.receiver) { + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); + } + // Method call is wrapped in `await` and `Promise.resolve()`, as the call + // may or may not return a promise. This is not required when implementing `Receiver`. + return (await Promise.resolve(this.receiver.start())); + } +} + +class CommerceS2SAuthClient extends ProductClient { + initAuth({ accountId, clientId, clientSecret, tokenStore }) { + return new S2SAuth({ accountId, clientId, clientSecret, tokenStore }); + } + initEndpoints(auth, options) { + return new CommerceEndpoints({ auth, ...options }); + } + initEventProcessor() { + return undefined; + } +} + +export { ApiResponseError, AwsLambdaReceiver, AwsReceiverRequestError, ClientCredentialsRawResponseError, CommerceEndpoints, CommerceS2SAuthClient, CommonHttpRequestError, ConsoleLogger, HTTPReceiverConstructionError, HTTPReceiverPortNotNumberError, HTTPReceiverRequestError, HttpReceiver, LogLevel, OAuthInstallerNotInitializedError, OAuthStateVerificationFailedError, OAuthTokenDoesNotExistError, OAuthTokenFetchFailedError, OAuthTokenRawResponseError, OAuthTokenRefreshFailedError, ProductClientConstructionError, ReceiverInconsistentStateError, ReceiverOAuthFlowError, S2SRawResponseError, StatusCode, isCoreError, isStateStore }; diff --git a/index.cjs b/index.cjs index e017a85..6700b2a 100644 --- a/index.cjs +++ b/index.cjs @@ -1,8 +1,10 @@ const Chatbot = require("./chatbot/chatbot.cjs"); +const Commerce = require("./commerce/commerce.cjs"); const TeamChat = require("./teamchat/teamchat.cjs"); const Users = require("./users/users.cjs"); +const Marketplace = require("./marketplace/marketplace.cjs"); const Phone = require("./phone/phone.cjs"); const Accounts = require("./accounts/accounts.cjs"); const Meetings = require("./meetings/meetings.cjs"); const VideoSdk = require("./videosdk/videosdk.cjs"); -module.exports = { Chatbot, TeamChat, Users, Phone, Accounts, Meetings, VideoSdk }; \ No newline at end of file +module.exports = { Chatbot, Commerce, TeamChat, Users, Marketplace, Phone, Accounts, Meetings, VideoSdk }; \ No newline at end of file diff --git a/index.d.ts b/index.d.ts index ee512b9..30bbbd8 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,13 +1,17 @@ import Chatbot from "./chatbot/chatbot.d.ts"; +import Commerce from "./commerce/commerce.d.ts"; import TeamChat from "./teamchat/teamchat.d.ts"; import Users from "./users/users.d.ts"; +import Marketplace from "./marketplace/marketplace.d.ts"; import Phone from "./phone/phone.d.ts"; import Accounts from "./accounts/accounts.d.ts"; import Meetings from "./meetings/meetings.d.ts"; import VideoSdk from "./videosdk/videosdk.d.ts"; export import Chatbot = Chatbot; +export import Commerce = Commerce; export import TeamChat = TeamChat; export import Users = Users; +export import Marketplace = Marketplace; export import Phone = Phone; export import Accounts = Accounts; export import Meetings = Meetings; diff --git a/index.mjs b/index.mjs index 0031bae..463a937 100644 --- a/index.mjs +++ b/index.mjs @@ -1,8 +1,10 @@ import Chatbot from "./chatbot/chatbot.mjs"; +import Commerce from "./commerce/commerce.mjs"; import TeamChat from "./teamchat/teamchat.mjs"; import Users from "./users/users.mjs"; +import Marketplace from "./marketplace/marketplace.mjs"; import Phone from "./phone/phone.mjs"; import Accounts from "./accounts/accounts.mjs"; import Meetings from "./meetings/meetings.mjs"; import VideoSdk from "./videosdk/videosdk.mjs"; -export { Chatbot, TeamChat, Users, Phone, Accounts, Meetings, VideoSdk }; \ No newline at end of file +export { Chatbot, Commerce, TeamChat, Users, Marketplace, Phone, Accounts, Meetings, VideoSdk }; \ No newline at end of file diff --git a/marketplace/marketplace.cjs b/marketplace/marketplace.cjs new file mode 100644 index 0000000..61fa4f5 --- /dev/null +++ b/marketplace/marketplace.cjs @@ -0,0 +1,1182 @@ +'use strict'; + +var node_crypto = require('node:crypto'); +var node_http = require('node:http'); +var node_https = require('node:https'); +var axios = require('axios'); +var dayjs = require('dayjs'); +var node_buffer = require('node:buffer'); +var jose = require('jose'); +var FormData = require('form-data'); +var os = require('node:os'); +var node_path = require('node:path'); + +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +const isStateStore = (obj) => typeof obj.generateState === "function" && typeof obj.verifyState === "function"; + +const createRivetErrors = (errors) => ({ + createError: (errorCode) => class extends Error { + errorCode = errors[errorCode]; + constructor(message, options) { + const errorMessage = (message ?? + (options?.cause instanceof Error ? options.cause.message : errorCode)); + super(errorMessage, options); + this.name = errorCode; + Object.setPrototypeOf(this, new.target.prototype); + } + }, + isError: (obj, key) => key ? + Object.keys(errors).some((code) => code === key) && + typeof obj.errorCode === "string" && + obj.errorCode === errors[key] + : typeof obj.errorCode === "string" +}); + +const coreErrors = { + ApiResponseError: "zoom_rivet_api_response_error", + AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error", + ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error", + S2SRawResponseError: "zoom_rivet_s2s_raw_response_error", + CommonHttpRequestError: "zoom_rivet_common_http_request_error", + ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error", + ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error", + HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error", + HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error", + HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error", + OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error", + OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error", + OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error", + OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error", + OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error", + OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error", + ProductClientConstructionError: "zoom_rivet_product_client_construction_error" +}; +const { createError: createCoreError, isError: isCoreError } = createRivetErrors(coreErrors); +const ApiResponseError = createCoreError("ApiResponseError"); +const AwsReceiverRequestError = createCoreError("AwsReceiverRequestError"); +const ClientCredentialsRawResponseError = createCoreError("ClientCredentialsRawResponseError"); +const S2SRawResponseError = createCoreError("S2SRawResponseError"); +const CommonHttpRequestError = createCoreError("CommonHttpRequestError"); +const ReceiverInconsistentStateError = createCoreError("ReceiverInconsistentStateError"); +const ReceiverOAuthFlowError = createCoreError("ReceiverOAuthFlowError"); +const HTTPReceiverConstructionError = createCoreError("HTTPReceiverConstructionError"); +const HTTPReceiverPortNotNumberError = createCoreError("HTTPReceiverPortNotNumberError"); +const HTTPReceiverRequestError = createCoreError("HTTPReceiverRequestError"); +const OAuthInstallerNotInitializedError = createCoreError("OAuthInstallerNotInitializedError"); +const OAuthTokenDoesNotExistError = createCoreError("OAuthTokenDoesNotExistError"); +const OAuthTokenFetchFailedError = createCoreError("OAuthTokenFetchFailedError"); +const OAuthTokenRawResponseError = createCoreError("OAuthTokenRawResponseError"); +const OAuthTokenRefreshFailedError = createCoreError("OAuthTokenRefreshFailedError"); +const OAuthStateVerificationFailedError = createCoreError("OAuthStateVerificationFailedError"); +const ProductClientConstructionError = createCoreError("ProductClientConstructionError"); + +exports.LogLevel = void 0; +(function (LogLevel) { + LogLevel["ERROR"] = "error"; + LogLevel["WARN"] = "warn"; + LogLevel["INFO"] = "info"; + LogLevel["DEBUG"] = "debug"; +})(exports.LogLevel || (exports.LogLevel = {})); +class ConsoleLogger { + level; + name; + static labels = (() => { + const entries = Object.entries(exports.LogLevel); + const map = entries.map(([key, value]) => [value, `[${key}] `]); + return new Map(map); + })(); + static severity = { + [exports.LogLevel.ERROR]: 400, + [exports.LogLevel.WARN]: 300, + [exports.LogLevel.INFO]: 200, + [exports.LogLevel.DEBUG]: 100 + }; + constructor() { + this.level = exports.LogLevel.INFO; + this.name = ""; + } + getLevel() { + return this.level; + } + setLevel(level) { + this.level = level; + } + setName(name) { + this.name = name; + } + debug(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(exports.LogLevel.DEBUG, this.level)) { + console.debug(ConsoleLogger.labels.get(exports.LogLevel.DEBUG), this.name, ...msg); + } + } + info(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(exports.LogLevel.INFO, this.level)) { + console.info(ConsoleLogger.labels.get(exports.LogLevel.INFO), this.name, ...msg); + } + } + warn(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(exports.LogLevel.WARN, this.level)) { + console.warn(ConsoleLogger.labels.get(exports.LogLevel.WARN), this.name, ...msg); + } + } + error(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(exports.LogLevel.ERROR, this.level)) { + console.error(ConsoleLogger.labels.get(exports.LogLevel.ERROR), this.name, ...msg); + } + } + static isMoreOrEqualSevere(a, b) { + return ConsoleLogger.severity[a] >= ConsoleLogger.severity[b]; + } +} + +class EventManager { + endpoints; + /** @internal */ + listeners; + constructor(endpoints) { + this.endpoints = endpoints; + this.listeners = {}; + } + appendListener(eventName, predicate, listener) { + if (this.listeners[eventName]) { + this.listeners[eventName].push({ predicate, listener }); + } + else { + this.listeners[eventName] = [{ predicate, listener }]; + } + } + filteredEvent(eventName, predicate, listener) { + if (typeof predicate !== "function" || typeof listener !== "function") { + throw new Error("Event predicate and listener must be of type function."); + } + this.appendListener(eventName, predicate, listener); + } + async emit(eventName, payload) { + if (!this.listeners[eventName]) + return; + await Promise.all(this.listeners[eventName].map(async ({ predicate, listener }) => { + if (typeof predicate !== "undefined" && !predicate(payload)) + return; + await Promise.resolve(listener(payload)); + })); + } + event(eventName, listener) { + if (typeof listener !== "function") { + throw new Error("Event listener must be of type function."); + } + this.appendListener(eventName, undefined, listener); + } + withContext() { + throw new Error("Method not implemented. Only to be used for type."); + } +} + +/** @internal */ +const hashUrlValidationEvent = ({ payload: { plainToken } }, webhooksSecretToken) => ({ + encryptedToken: node_crypto.createHmac("sha256", webhooksSecretToken).update(plainToken).digest("hex"), + plainToken +}); +const isHashedUrlValidation = (obj) => typeof obj.encryptedToken === "string" && + typeof obj.plainToken === "string"; +const isRawUrlValidationEvent = (obj) => obj.event === "endpoint.url_validation" && typeof obj.payload.plainToken === "string"; +const isSkeletonEvent = (obj) => typeof obj.event === "string"; +class CommonHttpRequest { + headers; + payload; + webhooksSecretToken; + constructor(headers, payload, webhooksSecretToken) { + this.headers = headers; + this.payload = payload; + this.webhooksSecretToken = webhooksSecretToken; + } + static buildFromAwsEvent({ body, headers, isBase64Encoded }, webhooksSecretToken) { + try { + const rawBody = body ?? ""; + const decodedBody = isBase64Encoded ? Buffer.from(rawBody, "base64").toString("ascii") : rawBody; + const payload = JSON.parse(decodedBody); + return new CommonHttpRequest(headers, payload, webhooksSecretToken); + } + catch (err) { + throw err instanceof SyntaxError ? + new CommonHttpRequestError("Failed to parse payload string to JSON.", err) + : err; + } + } + static async buildFromIncomingMessage(incomingMessage, webhooksSecretToken) { + const bufferAsString = () => { + return new Promise((resolve, reject) => { + const body = []; + incomingMessage.on("data", (chunk) => body.push(chunk)); + incomingMessage.on("error", (err) => { + reject(err); + }); + incomingMessage.on("end", () => { + resolve(Buffer.concat(body).toString()); + }); + }); + }; + try { + const payload = JSON.parse(await bufferAsString()); + return new CommonHttpRequest(incomingMessage.headers, payload, webhooksSecretToken); + } + catch (err) { + if (err instanceof SyntaxError) { + throw new CommonHttpRequestError("Failed to parse payload string to JSON.", err); + } + throw err; + } + } + isEventVerified() { + const { signature, requestTimestamp } = this.parseHeaders(); + const messageToVerify = `v0:${requestTimestamp.toString()}:${JSON.stringify(this.payload)}`; + const hashToVerify = node_crypto.createHmac("sha256", this.webhooksSecretToken).update(messageToVerify).digest("hex"); + const signatureToVerify = `v0=${hashToVerify}`; + return signatureToVerify === signature; + } + parseHeaders() { + const findHeader = (header) => { + const foundHeader = Object.keys(this.headers).find((key) => key.toLowerCase() === header.toLowerCase()); + return foundHeader && this.headers[foundHeader]; + }; + const headerSignature = findHeader("x-zm-signature"); + const headerRequestTimestamp = findHeader("x-zm-request-timestamp"); + if (!headerSignature && !headerRequestTimestamp) { + throw new CommonHttpRequestError("Request payload must have signature and request timestamp from Zoom."); + } + return { + signature: headerSignature, + requestTimestamp: Number(headerRequestTimestamp) + }; + } + processEvent() { + if (!isSkeletonEvent(this.payload)) { + throw new CommonHttpRequestError("Request payload structure does not match expected from Zoom."); + } + if (!this.isEventVerified()) { + throw new CommonHttpRequestError("Failed to verify event originated from Zoom."); + } + if (isRawUrlValidationEvent(this.payload)) { + return hashUrlValidationEvent(this.payload, this.webhooksSecretToken); + } + return this.payload; + } +} + +exports.StatusCode = void 0; +(function (StatusCode) { + StatusCode[StatusCode["OK"] = 200] = "OK"; + StatusCode[StatusCode["TEMPORARY_REDIRECT"] = 302] = "TEMPORARY_REDIRECT"; + StatusCode[StatusCode["BAD_REQUEST"] = 400] = "BAD_REQUEST"; + StatusCode[StatusCode["NOT_FOUND"] = 404] = "NOT_FOUND"; + StatusCode[StatusCode["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED"; + StatusCode[StatusCode["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR"; +})(exports.StatusCode || (exports.StatusCode = {})); + +class AwsLambdaReceiver { + eventEmitter; + webhooksSecretToken; + constructor({ webhooksSecretToken }) { + this.webhooksSecretToken = webhooksSecretToken; + } + buildResponse(statusCode, body) { + return { + body: JSON.stringify(body), + headers: { "Content-Type": "application/json" }, + statusCode + }; + } + canInstall() { + return false; + } + init({ eventEmitter }) { + this.eventEmitter = eventEmitter; + } + start() { + return async (event, context) => { + console.debug("Processing Lambda event ", JSON.stringify(event), " with context ", JSON.stringify(context)); + try { + const request = CommonHttpRequest.buildFromAwsEvent(event, this.webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + return this.buildResponse(exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + return this.buildResponse(exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } + } + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + return this.buildResponse(exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + return this.buildResponse(exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } + } + }; + } + async stop() { + return Promise.resolve(); + } +} + +const prependSlashes = (strs) => { + const rawStrs = Array.isArray(strs) ? strs : [strs]; + const mappedStrs = rawStrs.map((rawStr) => (rawStr.startsWith("/") ? rawStr : `/${rawStr}`)); + return (Array.isArray(strs) ? mappedStrs : mappedStrs[0]); +}; + +class TokenMemoryStore { + currentToken; + getLatestToken() { + return this.currentToken; + } + storeToken(token) { + this.currentToken = token; + } +} + +/** @internal */ +const EXPIRATION_DELTA_SECONDS = 60; +/** @internal */ +const OAUTH_BASE_URL = "https://zoom.us"; +/** @internal */ +const OAUTH_TOKEN_PATH = "/oauth/token"; +/** + * {@link Auth} is the base implementation of authentication for Zoom's APIs. + * + * It only requires a `clientId` and `tokenStore`, as these options are shared across + * all authentication implementations, namely OAuth and server-to-server auth (client + * credentials, JWT, and server-to-server OAuth.) + */ +class Auth { + clientId; + clientSecret; + tokenStore; + logger; + constructor({ clientId, clientSecret, tokenStore, logger }) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.tokenStore = tokenStore ?? new TokenMemoryStore(); + this.logger = logger; + } + getBasicAuthorization() { + const clientCredentials = `${this.clientId}:${this.clientSecret}`; + return node_buffer.Buffer.from(clientCredentials).toString("base64"); + } + isAlmostExpired(isoTime) { + const currentDate = dayjs(); + return dayjs(isoTime).diff(currentDate, "seconds") <= EXPIRATION_DELTA_SECONDS; + } + async makeOAuthTokenRequest(grantType, payload) { + return await axios({ + method: "POST", + url: new URL(OAUTH_TOKEN_PATH, OAUTH_BASE_URL).toString(), + headers: { + Authorization: `Basic ${this.getBasicAuthorization()}`, + "Content-Type": "application/x-www-form-urlencoded" + }, + data: new URLSearchParams({ grant_type: grantType, ...payload }), + validateStatus: (status) => status >= 200 && status <= 299 + }); + } +} + +const DEFAULT_EXPIRATION_SECONDS = 300; // 5 minutes +/** @internal */ +const ISSUER_URN = "urn:zoom:rivet-sdk"; +class JwtStateStore { + encodedSecret; + expirationSeconds; + constructor({ expirationSeconds, stateSecret }) { + this.encodedSecret = new TextEncoder().encode(stateSecret); + this.expirationSeconds = expirationSeconds ?? DEFAULT_EXPIRATION_SECONDS; + } + async generateState() { + const issuedTime = dayjs(); + const expirationTime = issuedTime.add(this.expirationSeconds, "seconds"); + return await new jose.SignJWT({ random: node_crypto.randomBytes(8).toString("hex") }) + .setProtectedHeader({ alg: "HS256", typ: "JWT" }) + .setExpirationTime(expirationTime.toDate()) + .setIssuedAt(issuedTime.toDate()) + .setIssuer(ISSUER_URN) + .sign(this.encodedSecret); + } + async verifyState(state) { + try { + await jose.jwtVerify(state, this.encodedSecret, { + algorithms: ["HS256"], + issuer: ISSUER_URN, + typ: "JWT" + }); + } + catch (err) { + throw new OAuthStateVerificationFailedError(`Failed to verify OAuth state: ${err.name}.`, { + cause: err + }); + } + } +} + +const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; +const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds +const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; +const hasInstallerOptions = (obj) => typeof obj.installerOptions.redirectUri !== "undefined" && + typeof obj.installerOptions.stateStore !== "undefined"; +/** + * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication + * is initiated server-side, but requires manual authorization from a user, by redirecting the user to Zoom. + * + * In addition to all required fields from {@link AuthOptions}, this class requires a `redirectUri`, as this + * value is appended to the authorization URL when the user is redirected to Zoom and subsequently redirected + * back to an endpoint on this server. + * + * @see {@link https://developers.zoom.us/docs/integrations/oauth/ | OAuth - Zoom Developers} + */ +class InteractiveAuth extends Auth { + installerOptions; + async getAuthorizationUrl() { + if (!this.installerOptions?.stateStore) { + throw new OAuthInstallerNotInitializedError("Cannot generate authorization URL, state store not initialized."); + } + const authUrl = new URL(OAUTH_AUTHORIZE_PATH, OAUTH_BASE_URL); + const generatedState = await Promise.resolve(this.installerOptions.stateStore.generateState()); + const { searchParams } = authUrl; + searchParams.set("client_id", this.clientId); + searchParams.set("redirect_uri", this.getFullRedirectUri()); + searchParams.set("response_type", "code"); + searchParams.set("state", generatedState); + return { + fullUrl: authUrl.toString(), + generatedState + }; + } + getFullRedirectUri() { + if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { + throw new OAuthInstallerNotInitializedError("Cannot generate full redirect URI, redirect URI or redirect URI path not initialized."); + } + return new URL(this.installerOptions.redirectUriPath, this.installerOptions.redirectUri).toString(); + } + // Don't return a type; we want it to be as narrow as possible (used for ReturnType). + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { + const updatedOptions = { + directInstall: Boolean(directInstall), + installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, + redirectUri, + redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE + }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } + this.installerOptions = updatedOptions; + return updatedOptions; + } +} + +const mergeDefaultOptions = (options, defaultOptions) => ({ ...defaultOptions, ...options }); + +const withDefaultTemplate = (cardContent, buttonContent) => ` + + + Zoom Rivet + + + + + + +
+
+
+

+ Zoom Rivet +

+
+
+ ${cardContent} +
+ ${buttonContent ? + `` + : ""} +
+
+ + +`; +/** + * Get the default HTML template that is shown to the developer/user when they visit the + * `installPath` endpoint, if Rivet currently has OAuth enabled. + * + * If `directInstall` is set to `true`, this function is not called; instead, the developer + * is directly redirected to Zoom's OAuth page. + */ +const defaultInstallTemplate = (authUrl) => withDefaultTemplate(`

Click the button below to navigate to Zoom to authorize your application for use with Rivet.

`, { href: authUrl, text: "Authorize with Zoom" }); +/** + * Get the default HTML template that is shown to the developer/user when they successfully + * authorize Rivet with a Zoom application. This is shown once they have already been redirected + * to Zoom, and the authorization attempt with Rivet was successful. + */ +const defaultCallbackSuccessTemplate = () => withDefaultTemplate(`

Your application has been successfully authorized with Rivet!

+

You may now close this page, or click the button below to redirect to Zoom's Marketplace.

`, { href: "https://marketplace.zoom.us", text: "Go to Marketplace" }); +/** + * Get the default HTML template that is shown to the developer when a known error occurs, meaning + * that the error is a core Rivet error. + */ +const defaultCallbackKnownErrorTemplate = (errName, errMessage) => withDefaultTemplate(`

An error occurred authorizing Rivet with Zoom.

+

[${errName}]: ${errMessage}

`); +/** + * Get the default HTML template that is shown to the developer when an unknown error occurs, + * meaning that the error is not known to be a core Rivet error and was thrown and not wrapped elsewhere. + */ +const defaultCallbackUnknownErrorTemplate = () => withDefaultTemplate(`

An unknown error occurred authorizing Rivet with Zoom. Please see stacktrace for details.

+

Please see stacktrace for further details.

`); + +const secureServerOptionKeys = [ + "ALPNProtocols", + "clientCertEngine", + "enableTrace", + "handshakeTimeout", + "rejectUnauthorized", + "requestCert", + "sessionTimeout", + "SNICallback", + "ticketKeys", + "pskCallback", + "pskIdentityHint", + "ca", + "cert", + "sigalgs", + "ciphers", + "clientCertEngine", + "crl", + "dhparam", + "ecdhCurve", + "honorCipherOrder", + "key", + "privateKeyEngine", + "privateKeyIdentifier", + "maxVersion", + "minVersion", + "passphrase", + "pfx", + "secureOptions", + "secureProtocol", + "sessionIdContext" +]; +class HttpReceiver { + /** @internal */ + static DEFAULT_ENDPOINT = "/zoom/events"; + eventEmitter; + interactiveAuth; + /** @internal */ + options; + server; + logger; + constructor(options) { + this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); + this.options.endpoints = prependSlashes(this.options.endpoints); + this.logger = + options.logger ?? + (() => { + const defaultLogger = new ConsoleLogger(); + defaultLogger.setLevel(options.logLevel ?? exports.LogLevel.ERROR); + return defaultLogger; + })(); + } + canInstall() { + return true; + } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } + getServerCreator() { + return this.hasSecureOptions() ? node_https.createServer : node_http.createServer; + } + hasEndpoint(pathname) { + const { endpoints } = this.options; + return Array.isArray(endpoints) ? endpoints.includes(pathname) : endpoints === pathname; + } + hasSecureOptions() { + return Object.keys(this.options).some((option) => secureServerOptionKeys.includes(option)); + } + init({ eventEmitter, interactiveAuth }) { + this.eventEmitter = eventEmitter; + this.interactiveAuth = interactiveAuth; + } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } + start(port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { + const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; + this.logger.error(errorMessage); + throw new HTTPReceiverPortNotNumberError(errorMessage); + } + const listenPort = port ?? this.options.port; + return new Promise((resolve, reject) => { + this.server = this.getServerCreator()(this.options, (req, res) => void (async () => { + // `req.headers.host` should be used with care, as clients can manipulate this value. + // However, for this use case, the value is completely discarded and only `pathname` + // is used, which is why there's no further validation occurring. + const { pathname, searchParams } = new URL(req.url ?? "", `http://${req.headers.host ?? "localhost"}`); + const { interactiveAuth } = this; + this.logger.debug([pathname, searchParams]); + // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath + if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { + const { installerOptions } = interactiveAuth; + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); + await (installerOptions.directInstall ? + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); + return; + } + // The user has navigated to the redirect page; init the code + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); + try { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { + const errorMessage = "OAuth callback did not include code and/or state in request."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); + return; + } + catch (err) { + const htmlTemplate = isCoreError(err) ? + defaultCallbackKnownErrorTemplate(err.name, err.message) + : defaultCallbackUnknownErrorTemplate(); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); + return; + } + } + } + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, exports.StatusCode.NOT_FOUND); + return; + } + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); + return; + } + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } + } + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } + } + } + })()); + this.server.on("close", () => (this.server = undefined)); + this.server.on("error", (err) => { + this.logger.error(err.message); + reject(err); + }); + this.server.listen(listenPort, () => { + if (!this.server) { + throw new ReceiverInconsistentStateError(); + } + const { port: listeningPort } = this.server.address(); + this.logger.info(`Listening on port ${listeningPort.toString()}`); + resolve(this.server); + }); + }); + } + stop() { + if (!this.server) { + throw new ReceiverInconsistentStateError(); + } + return new Promise((resolve, reject) => { + this.server?.close((err) => { + if (err) { + this.logger.error(err.message); + reject(err); + } + }); + this.server = undefined; + resolve(); + }); + } + writeTemporaryRedirect(res, location, setCookie) { + return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } + res.writeHead(exports.StatusCode.TEMPORARY_REDIRECT, { Location: location }); + res.end(() => { + resolve(); + }); + }); + } + writeResponse(res, statusCode, bodyContent, setCookie) { + return new Promise((resolve) => { + const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; + bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } + res.writeHead(statusCode, { "Content-Type": mimeType }); + res.end(bodyContent, () => { + resolve(); + }); + }); + } +} + +const version = "0.4.0"; +var packageJson = { + version: version}; + +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; +class WebEndpoints { + /** @internal */ + static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; + /** @internal */ + static DEFAULT_MIME_TYPE = "application/json"; + /** @internal */ + static DEFAULT_TIMEOUT = 0; + /** @internal */ + static GENERIC_ERROR_MESSAGE = "Request was unsuccessful with no further context"; + /** @internal */ + static TRACKING_ID_HEADER = "x-zm-trackingid"; + /** @internal */ + options; + constructor(options) { + this.options = mergeDefaultOptions(options, { + baseUrl: WebEndpoints.DEFAULT_BASE_URL, + hasCustomBaseUrl: typeof options.baseUrl !== "undefined", + timeout: WebEndpoints.DEFAULT_TIMEOUT + }); + } + buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }) { + // @ts-expect-error: Some arguments may not be present, but we pass them to makeRequest() anyway. + // prettier-ignore + // Next AST node is ignored by Prettier, even though it exceed maximum line length, because TypeScript + // won't allow ts-expect-error directive on multiple lines (https://github.com/Microsoft/TypeScript/issues/19573). + return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); + } + buildUserAgent() { + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + + `${node_path.basename(process.title)}/${process.version.replace("v", "")} ` + + `${os.platform()}/${os.release()}`); + } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } + getHeaders(bearerToken, contentType) { + return { + Accept: "application/json", + Authorization: `Bearer ${bearerToken}`, + "Content-Type": contentType, + "User-Agent": this.buildUserAgent() + }; + } + getRequestBody(args, mimeType) { + if (mimeType === "multipart/form-data") { + const formData = new FormData(); + Object.entries(args).forEach(([key, value]) => { + formData.append(key, value); + }); + return formData; + } + return args; + } + isOk(response) { + return response.status >= 200 && response.status <= 299; + } + isZoomResponseError(obj) { + return (typeof obj.code !== "undefined" && + typeof obj.message !== "undefined"); + } + async makeRequest(method, baseUrlOverride, url, requestContentType, bodyArgs, queryArgs) { + const { auth, baseUrl, doubleEncodeUrl, hasCustomBaseUrl, timeout } = this.options; + const bearerToken = await Promise.resolve(auth.getToken()); + const urlToSend = doubleEncodeUrl ? encodeURIComponent(encodeURIComponent(url)) : url; + const response = await axios({ + url: urlToSend, + method, + baseURL: hasCustomBaseUrl ? baseUrl : (baseUrlOverride ?? baseUrl), + headers: this.getHeaders(bearerToken, requestContentType), + params: queryArgs, + data: bodyArgs && this.getRequestBody(bodyArgs, requestContentType), + timeout: timeout, + beforeRedirect: (options) => { + options.headers = { + ...this.getHeaders(bearerToken, requestContentType), + ...options.headers + }; + }, + validateStatus: () => true // All responses are valid, not just 2xx + }); + if (!this.isOk(response)) { + const { status: statusCode } = response; + if (this.isZoomResponseError(response.data)) { + const { code: errorCode, message: errorMessage } = response.data; + throw new ApiResponseError(`[${statusCode.toString()}/${errorCode.toString()}]: "${errorMessage}"`); + } + throw new ApiResponseError(`[${statusCode.toString()}]: ${WebEndpoints.GENERIC_ERROR_MESSAGE}`); + } + return { + data: response.data, + statusCode: response.status, + trackingId: response.headers[WebEndpoints.TRACKING_ID_HEADER] + }; + } +} + +class MarketplaceEndpoints extends WebEndpoints { + app = { + sendAppNotifications: this.buildEndpoint({ + method: "POST", + urlPathBuilder: () => `/app/notifications` + }), + getUserOrAccountEventSubscription: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/marketplace/app/event_subscription` }), + createEventSubscription: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/marketplace/app/event_subscription` }), + unsubscribeAppEventSubscription: this.buildEndpoint({ method: "DELETE", urlPathBuilder: () => `/marketplace/app/event_subscription` }), + deleteEventSubscription: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ eventSubscriptionId }) => `/marketplace/app/event_subscription/${eventSubscriptionId}` + }), + subscribeEventSubscription: this.buildEndpoint({ + method: "PATCH", + urlPathBuilder: ({ eventSubscriptionId }) => `/marketplace/app/event_subscription/${eventSubscriptionId}` + }), + listApps: this.buildEndpoint({ + method: "GET", + urlPathBuilder: () => `/marketplace/apps` + }), + createApps: this.buildEndpoint({ + method: "POST", + urlPathBuilder: () => `/marketplace/apps` + }), + getInformationAboutApp: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}` }), + deletesApp: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}` + }), + getAPICallLogs: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/api_call_logs` }), + generateZoomAppDeeplink: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/deeplink` }), + updateAppPreApprovalSetting: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/preApprove` }), + getAppsUserRequests: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/requests` }), + addAppAllowRequestsForUsers: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/requests` }), + updateAppsRequestStatus: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/requests` }), + rotateClientSecret: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/rotate_client_secret` }), + getWebhookLogs: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/webhook_logs` }), + getAppUserEntitlements: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/marketplace/monetization/entitlements` }), + getUsersAppRequests: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/marketplace/users/${userId}/apps` }), + enableOrDisableUserAppSubscription: this.buildEndpoint({ + method: "PATCH", + urlPathBuilder: ({ appId, userId }) => `/marketplace/users/${userId}/apps/${appId}/subscription` + }), + getUsersEntitlements: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/marketplace/users/${userId}/entitlements` }) + }; + apps = { + generateAppDeeplink: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/zoomapp/deeplink` }) + }; + manifest = { + validateAppManifest: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/marketplace/apps/manifest/validate` }), + exportAppManifestFromExistingApp: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/manifest` }), + updateAppByManifest: this.buildEndpoint({ method: "PUT", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/manifest` }) + }; +} + +class MarketplaceEventProcessor extends EventManager { +} + +class OAuth extends InteractiveAuth { + assertResponseAccessToken(data) { + if (typeof data.access_token !== "string" || + typeof data.refresh_token !== "string" || + typeof data.expires_in !== "number" || + typeof data.scope !== "string") { + throw new OAuthTokenRawResponseError(`Failed to match raw response (${JSON.stringify(data)}) to expected shape.`); + } + } + async fetchAccessToken(code) { + try { + const response = await this.makeOAuthTokenRequest("authorization_code", { + code, + redirect_uri: this.getFullRedirectUri() + }); + this.assertResponseAccessToken(response.data); + return this.mapOAuthToken(response.data); + } + catch (err) { + throw new OAuthTokenFetchFailedError("Failed to fetch OAuth token.", { cause: err }); + } + } + async getToken() { + const { tokenStore } = this; + const currentToken = await Promise.resolve(tokenStore.getLatestToken()); + // If we have no OAuth token, app most likely has not been previously authorized. + if (!currentToken) { + throw new OAuthTokenDoesNotExistError("Failed to find OAuth token. Authorize this app first."); + } + // If the OAuth token hasn't already expired (and isn't within the delta), return it. + if (!this.isAlmostExpired(currentToken.expirationTimeIso)) { + return currentToken.accessToken; + } + // Since the token has expired, refresh, store, and return it. + const refreshedToken = await this.refreshAccessToken(currentToken.refreshToken); + await Promise.resolve(tokenStore.storeToken(refreshedToken)); + return refreshedToken.accessToken; + } + async initRedirectCode(code) { + const { tokenStore } = this; + const accessToken = await this.fetchAccessToken(code); + await Promise.resolve(tokenStore.storeToken(accessToken)); + } + mapOAuthToken({ access_token, expires_in, refresh_token, scope }) { + return { + accessToken: access_token, + expirationTimeIso: dayjs().add(expires_in, "seconds").toISOString(), + refreshToken: refresh_token, + scopes: scope.includes(" ") ? scope.split(" ") : [scope] + }; + } + async refreshAccessToken(refreshToken) { + try { + const response = await this.makeOAuthTokenRequest("refresh_token", { + refresh_token: refreshToken + }); + this.assertResponseAccessToken(response.data); + return this.mapOAuthToken(response.data); + } + catch (err) { + throw new OAuthTokenRefreshFailedError("Failed to refresh OAuth token.", { cause: err }); + } + } +} + +// Utility functions for determining if client options include custom receiver, or, if not, +// a webhooks secret token, as one of those is required! +const hasExplicitReceiver = (obj) => typeof obj.receiver !== "undefined"; +const hasWebhooksSecretToken = (obj) => typeof obj.webhooksSecretToken !== "undefined"; +const isReceiverDisabled = (options) => typeof options.disableReceiver !== "undefined" && options.disableReceiver; +const DEFAULT_HTTP_RECEIVER_PORT = 8080; +const DEFAULT_LOGLEVEL = exports.LogLevel.ERROR; +class ProductClient { + auth; + endpoints; + webEventConsumer; + receiver; + constructor(options) { + this.auth = this.initAuth(options); + this.endpoints = this.initEndpoints(this.auth, options); + this.webEventConsumer = this.initEventProcessor(this.endpoints, options); + // Only create an instance of `this.receiver` if the developer did not explicitly disable it. + if (!isReceiverDisabled(options)) { + // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); + } + this.receiver = (hasExplicitReceiver(options) ? + options.receiver + : this.initDefaultReceiver(options)); + this.receiver.init({ + eventEmitter: this.webEventConsumer, + interactiveAuth: this.auth instanceof InteractiveAuth ? this.auth : undefined + }); + } + } + initDefaultReceiver({ port, webhooksSecretToken, logLevel }) { + return new HttpReceiver({ + port: port ?? DEFAULT_HTTP_RECEIVER_PORT, + webhooksSecretToken, + logLevel: logLevel ?? DEFAULT_LOGLEVEL + }); + } + async start() { + if (!this.receiver) { + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); + } + // Method call is wrapped in `await` and `Promise.resolve()`, as the call + // may or may not return a promise. This is not required when implementing `Receiver`. + return (await Promise.resolve(this.receiver.start())); + } +} + +class MarketplaceOAuthClient extends ProductClient { + initAuth({ clientId, clientSecret, tokenStore, ...restOptions }) { + const oAuth = new OAuth({ clientId, clientSecret, tokenStore }); + if (hasInstallerOptions(restOptions)) { + oAuth.setInstallerOptions(restOptions.installerOptions); + } + return oAuth; + } + initEndpoints(auth, options) { + return new MarketplaceEndpoints({ auth, ...options }); + } + initEventProcessor(endpoints) { + return new MarketplaceEventProcessor(endpoints); + } +} + +class S2SAuth extends Auth { + accountId; + constructor({ accountId, ...restOptions }) { + super(restOptions); + this.accountId = accountId; + } + assertRawToken(obj) { + if (typeof obj.access_token !== "string" || + typeof obj.expires_in !== "number" || + typeof obj.scope !== "string") { + throw new S2SRawResponseError(`Failed to match raw response ${JSON.stringify(obj)} to expected shape.`); + } + } + async fetchAccessToken() { + const response = await this.makeOAuthTokenRequest("account_credentials", { + account_id: this.accountId + }); + this.assertRawToken(response.data); + return this.mapAccessToken(response.data); + } + async getToken() { + const { tokenStore } = this; + const currentToken = await Promise.resolve(tokenStore.getLatestToken()); + if (currentToken && !this.isAlmostExpired(currentToken.expirationTimeIso)) { + return currentToken.accessToken; + } + const token = await this.fetchAccessToken(); + await Promise.resolve(tokenStore.storeToken(token)); + return token.accessToken; + } + mapAccessToken({ access_token, expires_in, scope }) { + return { + accessToken: access_token, + expirationTimeIso: dayjs().add(expires_in, "seconds").toISOString(), + scopes: scope.includes(" ") ? scope.split(" ") : [scope] + }; + } +} + +class MarketplaceS2SAuthClient extends ProductClient { + initAuth({ clientId, clientSecret, tokenStore, accountId }) { + return new S2SAuth({ clientId, clientSecret, tokenStore, accountId }); + } + initEndpoints(auth, options) { + return new MarketplaceEndpoints({ auth, ...options }); + } + initEventProcessor(endpoints) { + return new MarketplaceEventProcessor(endpoints); + } +} + +exports.ApiResponseError = ApiResponseError; +exports.AwsLambdaReceiver = AwsLambdaReceiver; +exports.AwsReceiverRequestError = AwsReceiverRequestError; +exports.ClientCredentialsRawResponseError = ClientCredentialsRawResponseError; +exports.CommonHttpRequestError = CommonHttpRequestError; +exports.ConsoleLogger = ConsoleLogger; +exports.HTTPReceiverConstructionError = HTTPReceiverConstructionError; +exports.HTTPReceiverPortNotNumberError = HTTPReceiverPortNotNumberError; +exports.HTTPReceiverRequestError = HTTPReceiverRequestError; +exports.HttpReceiver = HttpReceiver; +exports.MarketplaceEndpoints = MarketplaceEndpoints; +exports.MarketplaceEventProcessor = MarketplaceEventProcessor; +exports.MarketplaceOAuthClient = MarketplaceOAuthClient; +exports.MarketplaceS2SAuthClient = MarketplaceS2SAuthClient; +exports.OAuthInstallerNotInitializedError = OAuthInstallerNotInitializedError; +exports.OAuthStateVerificationFailedError = OAuthStateVerificationFailedError; +exports.OAuthTokenDoesNotExistError = OAuthTokenDoesNotExistError; +exports.OAuthTokenFetchFailedError = OAuthTokenFetchFailedError; +exports.OAuthTokenRawResponseError = OAuthTokenRawResponseError; +exports.OAuthTokenRefreshFailedError = OAuthTokenRefreshFailedError; +exports.ProductClientConstructionError = ProductClientConstructionError; +exports.ReceiverInconsistentStateError = ReceiverInconsistentStateError; +exports.ReceiverOAuthFlowError = ReceiverOAuthFlowError; +exports.S2SRawResponseError = S2SRawResponseError; +exports.isCoreError = isCoreError; +exports.isStateStore = isStateStore; diff --git a/marketplace/marketplace.d.ts b/marketplace/marketplace.d.ts new file mode 100644 index 0000000..a4639ec --- /dev/null +++ b/marketplace/marketplace.d.ts @@ -0,0 +1,980 @@ +import { AxiosResponse } from 'axios'; +import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; +import { Server } from 'node:http'; +import { ServerOptions } from 'node:https'; + +declare enum LogLevel { + ERROR = "error", + WARN = "warn", + INFO = "info", + DEBUG = "debug" +} +interface Logger { + /** + * Output debug message + * @param msg any data to be logged + */ + debug(...msg: unknown[]): void; + /** + * Output info message + * @param msg any data to be logged + */ + info(...msg: unknown[]): void; + /** + * Output warn message + * @param msg any data to be logged + */ + warn(...msg: unknown[]): void; + /** + * Output error message + * @param msg any data to be logged + */ + error(...msg: unknown[]): void; + /** + * Disables all logging below the given level + * @param level as a string, 'error' | 'warn' | 'info' | 'debug' + */ + setLevel(level: LogLevel): void; + /** + * Return the current LogLevel. + */ + getLevel(): LogLevel; + /** + * Name the instance so that it can be filtered when many loggers are sending output + * to the same destination. + * @param name as a string + */ + setName(name: string): void; +} +declare class ConsoleLogger implements Logger { + private level; + private name; + private static labels; + private static severity; + constructor(); + getLevel(): LogLevel; + setLevel(level: LogLevel): void; + setName(name: string): void; + debug(...msg: unknown[]): void; + info(...msg: unknown[]): void; + warn(...msg: unknown[]): void; + error(...msg: unknown[]): void; + private static isMoreOrEqualSevere; +} + +type AllPropsOptional = Exclude<{ + [P in keyof T]: undefined extends T[P] ? True : False; +}[keyof T], undefined> extends True ? True : False; +type Constructor = new (...args: any[]) => T; +type MaybeArray = T | T[]; +type MaybePromise = T | Promise; +type StringIndexed = Record; + +interface TokenStore { + getLatestToken(): MaybePromise; + storeToken(token: Token): MaybePromise; +} + +interface AuthOptions { + clientId: string; + clientSecret: string; + tokenStore?: TokenStore | undefined; + logger?: Logger; +} +type OAuthGrantType = "authorization_code" | "client_credentials" | "refresh_token" | "account_credentials"; +interface BaseOAuthRequest { + grant_type: OAuthGrantType; +} +interface OAuthAuthorizationCodeRequest extends BaseOAuthRequest { + code: string; + grant_type: "authorization_code"; + redirect_uri?: string; +} +interface OAuthRefreshTokenRequest extends BaseOAuthRequest { + grant_type: "refresh_token"; + refresh_token: string; +} +interface S2SAuthTokenRequest extends BaseOAuthRequest { + grant_type: "account_credentials"; + account_id: string; +} +type OAuthRequest = OAuthAuthorizationCodeRequest | OAuthRefreshTokenRequest | S2SAuthTokenRequest; +/** + * {@link Auth} is the base implementation of authentication for Zoom's APIs. + * + * It only requires a `clientId` and `tokenStore`, as these options are shared across + * all authentication implementations, namely OAuth and server-to-server auth (client + * credentials, JWT, and server-to-server OAuth.) + */ +declare abstract class Auth { + protected readonly clientId: string; + protected readonly clientSecret: string; + protected readonly tokenStore: TokenStore; + protected readonly logger: Logger | undefined; + constructor({ clientId, clientSecret, tokenStore, logger }: AuthOptions); + protected getBasicAuthorization(): string; + abstract getToken(): MaybePromise; + protected isAlmostExpired(isoTime: string): boolean; + protected makeOAuthTokenRequest(grantType: T, payload?: Omit, "grant_type">): Promise; +} + +interface ClientCredentialsToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} + +interface JwtToken { + token: string; + expirationTimeIso: string; +} + +interface S2SAuthToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} +interface S2SAuthOptions { + accountId: string; +} +declare class S2SAuth extends Auth { + private accountId; + constructor({ accountId, ...restOptions }: AuthOptions & S2SAuthOptions); + private assertRawToken; + private fetchAccessToken; + getToken(): Promise; + private mapAccessToken; +} + +interface Event { + event: Type; +} +type EventKeys = T extends Event ? U : never; +type EventPayload = Extract; +type EventListenerFn, ReturnType = MaybePromise> = (payload: EventPayload) => ReturnType; +type EventListenerPredicateFn> = EventListenerFn>; +type ContextListener, Context> = (_: EventPayload & Context) => MaybePromise; +type GenericEventManager = EventManager; +declare class EventManager { + protected endpoints: Endpoints; + constructor(endpoints: Endpoints); + private appendListener; + filteredEvent>(eventName: EventName, predicate: EventListenerPredicateFn, listener: EventListenerFn): void; + emit>(eventName: EventName, payload: EventPayload): Promise; + event>(eventName: EventName, listener: EventListenerFn): void; + protected withContext, Context>(): ContextListener; +} + +declare enum StatusCode { + OK = 200, + TEMPORARY_REDIRECT = 302, + BAD_REQUEST = 400, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + INTERNAL_SERVER_ERROR = 500 +} +interface ReceiverInitOptions { + eventEmitter?: GenericEventManager | undefined; + interactiveAuth?: InteractiveAuth | undefined; +} +interface Receiver { + canInstall(): true | false; + init(options: ReceiverInitOptions): void; + start(...args: any[]): MaybePromise; + stop(...args: any[]): MaybePromise; +} + +interface HttpReceiverOptions extends Partial { + endpoints?: MaybeArray | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; + port?: number | string | undefined; + webhooksSecretToken?: string | undefined; +} +type SecureServerOptions = { + [K in (typeof secureServerOptionKeys)[number]]: ServerOptions[K]; +}; +declare const secureServerOptionKeys: (keyof ServerOptions)[]; +declare class HttpReceiver implements Receiver { + private eventEmitter?; + private interactiveAuth?; + private server?; + private logger; + constructor(options: HttpReceiverOptions); + canInstall(): true; + private buildDeletedStateCookieHeader; + private buildStateCookieHeader; + private getRequestCookie; + private getServerCreator; + private hasEndpoint; + private hasSecureOptions; + init({ eventEmitter, interactiveAuth }: ReceiverInitOptions): void; + private setResponseCookie; + private areNormalizedUrlsEqual; + start(port?: number | string): Promise; + stop(): Promise; + private writeTemporaryRedirect; + private writeResponse; +} + +interface BaseResponse { + data?: Data | undefined; + statusCode: number; + trackingId?: string | undefined; +} +interface BuildEndpointOptions { + method: HttpMethod; + baseUrlOverride?: string | undefined; + urlPathBuilder: (params: PathSchema) => string; + requestMimeType?: RequestMimeType; +} +interface WebEndpointOptions { + auth: Auth; + baseUrl?: string | undefined; + doubleEncodeUrl?: boolean | undefined; + timeout?: number | undefined; + userAgentName?: string | undefined; +} +type EndpointArguments = (PathSchema extends NoParams ? object : AllPropsOptional extends "t" ? { + path?: PathSchema; +} : { + path: PathSchema; +}) & (BodySchema extends NoParams ? object : AllPropsOptional extends "t" ? { + body?: BodySchema; +} : { + body: BodySchema; +}) & (QuerySchema extends NoParams ? object : AllPropsOptional extends "t" ? { + query?: QuerySchema; +} : { + query: QuerySchema; +}); +type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; +type NoParams = "_NO_PARAMS_"; +type RequestMimeType = "application/json" | "multipart/form-data"; +declare class WebEndpoints { + constructor(options: WebEndpointOptions); + protected buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }: BuildEndpointOptions): (_: EndpointArguments) => Promise>; + private buildUserAgent; + private getCustomUserAgentName; + private getHeaders; + private getRequestBody; + private isOk; + private isZoomResponseError; + private makeRequest; +} + +type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & Pick & { + disableReceiver?: boolean | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; +}; +interface ClientReceiverOptions { + receiver: R; +} +type ClientConstructorOptions, R extends Receiver> = (O & { + disableReceiver: true; +}) | (O & (ClientReceiverOptions | HttpReceiverOptions)); +type ExtractInstallerOptions = A extends InteractiveAuth ? [ + ReturnType +] extends [true] ? WideInstallerOptions : object : object; +type ExtractAuthTokenType = A extends Auth ? T : never; +type GetAuthOptions = AuthOptions> & (A extends S2SAuth ? S2SAuthOptions : object); +type WideInstallerOptions = { + installerOptions: InstallerOptions; +}; +declare abstract class ProductClient, ReceiverType extends Receiver> { + private readonly auth; + readonly endpoints: EndpointsType; + readonly webEventConsumer?: EventProcessorType | undefined; + private readonly receiver?; + constructor(options: ClientConstructorOptions); + protected abstract initAuth(options: OptionsType): AuthType; + protected abstract initEndpoints(auth: AuthType, options: OptionsType): EndpointsType; + protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType | undefined; + private initDefaultReceiver; + start(): Promise>; +} + +/** + * {@link StateStore} defines methods for generating and verifying OAuth state. + * + * This interface is implemented internally for the default state store; however, + * it can also be implemented and passed to an OAuth client as well. + */ +interface StateStore { + /** + * Generate a new state string, which is directly appended to the OAuth `state` parameter. + */ + generateState(): MaybePromise; + /** + * Verify that the state received during OAuth callback is valid and not forged. + * + * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. + * + * @param state The state parameter that was received during OAuth callback + */ + verifyState(state: string): MaybePromise; +} +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +declare const isStateStore: (obj: unknown) => obj is StateStore; + +interface AuthorizationUrlResult { + fullUrl: string; + generatedState: string; +} +interface InstallerOptions { + directInstall?: boolean | undefined; + installPath?: string | undefined; + redirectUri: string; + redirectUriPath?: string | undefined; + stateStore: StateStore | string; + stateCookieName?: string | undefined; + stateCookieMaxAge?: number | undefined; +} +/** + * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication + * is initiated server-side, but requires manual authorization from a user, by redirecting the user to Zoom. + * + * In addition to all required fields from {@link AuthOptions}, this class requires a `redirectUri`, as this + * value is appended to the authorization URL when the user is redirected to Zoom and subsequently redirected + * back to an endpoint on this server. + * + * @see {@link https://developers.zoom.us/docs/integrations/oauth/ | OAuth - Zoom Developers} + */ +declare abstract class InteractiveAuth extends Auth { + installerOptions?: ReturnType; + getAuthorizationUrl(): Promise; + getFullRedirectUri(): string; + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }: InstallerOptions): { + directInstall: boolean; + installPath: string; + redirectUri: string; + redirectUriPath: string; + stateStore: StateStore; + stateCookieName: string; + stateCookieMaxAge: number; + }; +} + +/** + * Credentials for access token & refresh token, which are used to access Zoom's APIs. + * + * As access token is short-lived (usually a single hour), its expiration time is checked + * first. If it's possible to use the access token, it's used; however, if it has expired + * or is close to expiring, the refresh token should be used to generate a new access token + * before the API call is made. Refresh tokens are generally valid for 90 days. + * + * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} + * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. + * It's likely that this error will be rare, but it _can_ be thrown. + */ +interface OAuthToken { + accessToken: string; + expirationTimeIso: string; + refreshToken: string; + scopes: string[]; +} +declare class OAuth extends InteractiveAuth { + private assertResponseAccessToken; + private fetchAccessToken; + getToken(): Promise; + initRedirectCode(code: string): Promise; + private mapOAuthToken; + private refreshAccessToken; +} + +interface RivetError extends Error { + readonly errorCode: ErrorCode; +} + +declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ + readonly ApiResponseError: "zoom_rivet_api_response_error"; + readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; + readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; + readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; + readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; + readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; + readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; + readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; + readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; + readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; + readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; + readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; + readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; + readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; + readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; + readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; + readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; +}[K]>; +declare const ApiResponseError: Constructor; +declare const AwsReceiverRequestError: Constructor; +declare const ClientCredentialsRawResponseError: Constructor; +declare const S2SRawResponseError: Constructor; +declare const CommonHttpRequestError: Constructor; +declare const ReceiverInconsistentStateError: Constructor; +declare const ReceiverOAuthFlowError: Constructor; +declare const HTTPReceiverConstructionError: Constructor; +declare const HTTPReceiverPortNotNumberError: Constructor; +declare const HTTPReceiverRequestError: Constructor; +declare const OAuthInstallerNotInitializedError: Constructor; +declare const OAuthTokenDoesNotExistError: Constructor; +declare const OAuthTokenFetchFailedError: Constructor; +declare const OAuthTokenRawResponseError: Constructor; +declare const OAuthTokenRefreshFailedError: Constructor; +declare const OAuthStateVerificationFailedError: Constructor; +declare const ProductClientConstructionError: Constructor; + +interface AwsLambdaReceiverOptions { + webhooksSecretToken: string; +} +declare class AwsLambdaReceiver implements Receiver { + private eventEmitter?; + private readonly webhooksSecretToken; + constructor({ webhooksSecretToken }: AwsLambdaReceiverOptions); + buildResponse(statusCode: StatusCode, body: object): LambdaFunctionURLResult; + canInstall(): false; + init({ eventEmitter }: ReceiverInitOptions): void; + start(): LambdaFunctionURLHandler; + stop(): Promise; +} + +type AppSendAppNotificationsRequestBody = { + notification_id?: string; + message?: { + text?: string; + }; + user_id?: string; +}; +type AppGetUserOrAccountEventSubscriptionQueryParams = { + page_size?: number; + next_page_token?: string; + user_id: string; + subscription_scope?: "user" | "account" | "master_account"; + account_id: string; +}; +type AppGetUserOrAccountEventSubscriptionResponse = { + next_page_token?: string; + page_size?: number; +} & { + event_subscriptions?: { + event_subscription_id?: string; + events?: string[]; + event_subscription_name?: string; + event_webhook_url?: string; + subscription_scope?: "user" | "account" | "master_account"; + created_source?: "default" | "openapi"; + subscriber_id?: string; + }[]; +}; +type AppCreateEventSubscriptionRequestBody = { + events: string[]; + event_subscription_name?: string; + event_webhook_url: string; + user_ids?: string[]; + subscription_scope: "user" | "account" | "master_account"; + account_id?: string; +}; +type AppCreateEventSubscriptionResponse = { + event_subscription_id?: string; +}; +type AppUnsubscribeAppEventSubscriptionQueryParams = { + event_subscription_id: string; + user_ids?: string; + account_id: string; +}; +type AppDeleteEventSubscriptionPathParams = { + eventSubscriptionId: string; +}; +type AppSubscribeEventSubscriptionPathParams = { + eventSubscriptionId: string; +}; +type AppSubscribeEventSubscriptionRequestBody = { + user_ids?: string[]; + account_id: string; +}; +type AppListAppsQueryParams = { + page_size?: number; + next_page_token?: string; + type?: "active_requests" | "past_requests" | "public" | "account_created" | "approved_apps" | "restricted_apps" | "account_added"; +}; +type AppListAppsResponse = { + next_page_token?: string; + page_size?: number; +} & { + apps?: { + app_id?: string; + app_name?: string; + app_type?: "ZoomApp" | "ChatBotApp" | "OAuthApp" | "GeneralApp"; + app_usage?: 1 | 2; + app_status?: "PUBLISHED" | "UNPUBLISHED" | "SHARABLE"; + request_id?: string; + request_total_number?: number; + request_pending_number?: number; + request_approved_number?: number; + request_declined_number?: number; + latest_request_date_time?: string; + reviewer_name?: string; + review_date_time?: string; + app_developer_type?: "THIRD_PARTY" | "ZOOM" | "INTERNAL"; + app_description?: string; + app_icon?: string; + scopes?: { + scope_name?: string; + scope_description?: string; + }[]; + app_privacy_policy_url?: string; + app_directory_url?: string; + app_help_url?: string; + restricted_time?: string; + approval_info?: { + approved_type?: "forAllUser" | "forSpecificUser"; + approver_id?: string; + approved_time?: string; + app_approval_closed?: boolean; + }; + }[]; +}; +type AppCreateAppsRequestBody = { + app_type: "s2s_oauth" | "meeting_sdk" | "general"; + app_name: string; + scopes?: string[]; + contact_name: string; + contact_email: string; + company_name: string; + active?: boolean; + publish?: boolean; + manifest?: object; +}; +type AppCreateAppsResponse = { + created_at?: string; + app_id?: string; + app_name?: string; + app_type?: "s2s_oauth" | "meeting_sdk"; + scopes?: string[]; + production_credentials?: { + client_id?: string; + client_secret?: string; + }; + development_credentials?: { + client_id?: string; + client_secret?: string; + }; +}; +type AppGetInformationAboutAppPathParams = { + appId: string; +}; +type AppGetInformationAboutAppResponse = { + app_id?: string; + app_name?: string; + app_description?: string; + app_type?: "ZoomApp" | "ChatBotApp" | "OAuthApp"; + app_usage?: 1 | 2; + app_status?: "PUBLISHED" | "UNPUBLISHED"; + app_links?: { + documentation_url?: string; + privacy_policy_url?: string; + support_url?: string; + terms_of_use_url?: string; + }; + app_permissions?: { + group?: string; + group_message?: string; + title?: string; + permissions?: { + name?: string; + }[]; + }[]; + app_requirements?: { + user_role?: "admin" | "user"; + min_client_version?: string; + account_eligibility?: { + account_types?: string[]; + premium_events?: { + event_name?: string; + event?: string; + }[]; + }; + }; + app_scopes?: string[]; +}; +type AppDeletesAppPathParams = { + appId: string; +}; +type AppGetAPICallLogsPathParams = { + appId: string; +}; +type AppGetAPICallLogsQueryParams = { + next_page_token?: string; + page_size?: number; + duration?: 7 | 14 | 30; + query?: string; + method?: "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE" | "PATCH"; + status_code?: 400 | 401 | 403 | 404 | 409 | 429 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511; +}; +type AppGetAPICallLogsResponse = { + next_page_token?: string; + page_size?: number; + call_logs?: { + url_pattern?: string; + time?: string; + http_status?: 200 | 201 | 202 | 203 | 204 | 205 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 400 | 401 | 403 | 404 | 405 | 406 | 408 | 409 | 429 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511; + method?: "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE" | "PATCH"; + trace_id?: string; + }[]; +}; +type AppGenerateZoomAppDeeplinkPathParams = { + appId: string; +}; +type AppGenerateZoomAppDeeplinkRequestBody = { + type: 1 | 2; + target: "meeting" | "panel" | "modal"; + action: string; +}; +type AppGenerateZoomAppDeeplinkResponse = { + deeplink: string; +}; +type AppUpdateAppPreApprovalSettingPathParams = { + appId: string; +}; +type AppUpdateAppPreApprovalSettingRequestBody = { + action?: "approve_all" | "approve_user" | "disapprove_user" | "approve_group" | "disapprove_group" | "disapprove_all"; + user_ids?: string[]; + group_ids?: string[]; +}; +type AppUpdateAppPreApprovalSettingResponse = { + executed_at?: string; + user_ids?: string[]; + group_ids?: string[]; +}; +type AppGetAppsUserRequestsPathParams = { + appId: string; +}; +type AppGetAppsUserRequestsQueryParams = { + page_size?: number; + next_page_token?: string; + status?: "pending" | "approved" | "rejected"; +}; +type AppGetAppsUserRequestsResponse = { + next_page_token?: string; + page_size?: number; +} & { + requests?: { + request_user_id?: string; + request_user_name?: string; + request_user_email?: string; + request_user_department?: string; + request_date_time?: string; + reason?: string; + status?: "pending" | "approved" | "rejected"; + }[]; +}; +type AppAddAppAllowRequestsForUsersPathParams = { + appId: string; +}; +type AppAddAppAllowRequestsForUsersRequestBody = { + action: "add_all" | "add_user" | "add_group"; + user_ids?: string[]; + group_ids?: string[]; +}; +type AppAddAppAllowRequestsForUsersResponse = { + added_at?: string; + user_ids?: string[]; + group_ids?: string[]; +}; +type AppUpdateAppsRequestStatusPathParams = { + appId: string; +}; +type AppUpdateAppsRequestStatusRequestBody = { + action: "approve_all" | "approve" | "decline_all" | "decline" | "cancel"; + request_user_ids?: string[]; +}; +type AppRotateClientSecretPathParams = { + appId: string; +}; +type AppRotateClientSecretRequestBody = { + action: "new" | "update"; + revoke_old_secret_time: string; +}; +type AppRotateClientSecretResponse = { + secret_id: string; + new_secret: string; + revoke_old_secret_time: string; +} | { + secret_id: string; + revoke_old_secret_time: string; +}; +type AppGetWebhookLogsPathParams = { + appId: string; +}; +type AppGetWebhookLogsQueryParams = { + next_page_token?: string; + page_size?: number; + from?: string; + to?: string; + event?: string; + type?: 1 | 2 | 3; + retry_num?: number; +}; +type AppGetWebhookLogsResponse = { + next_page_token?: string; + page_size?: number; + webhook_logs?: { + event?: string; + status?: number; + failed_reason_type?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16; + user_id?: string; + endpoint?: string; + subscription_id?: string; + request_headers?: string; + request_body?: string; + response_headers?: string; + response_body?: string; + date_time?: string; + trace_id?: string; + request_id?: string; + retry_num?: 0 | 1 | 2 | 3; + }[]; +}; +type AppGetAppUserEntitlementsQueryParams = { + user_id?: string; +}; +type AppGetAppUserEntitlementsResponse = { + id?: string; + plan_name?: string; + plan_id?: string; +}[]; +type AppGetUsersAppRequestsPathParams = { + userId: string; +}; +type AppGetUsersAppRequestsQueryParams = { + page_size?: number; + next_page_token?: string; + type?: "active_requests" | "past_requests"; +}; +type AppGetUsersAppRequestsResponse = { + next_page_token?: string; + page_size?: number; +} & { + apps?: { + app_id?: string; + app_name?: string; + app_type?: "ZoomApp" | "ChatBotApp" | "OAuthApp"; + app_usage?: 1 | 2; + app_status?: "PUBLISHED" | "UNPUBLISHED"; + request_id?: string; + request_date_time?: string; + request_status?: "pending" | "approved" | "rejected"; + }[]; +}; +type AppEnableOrDisableUserAppSubscriptionPathParams = { + appId: string; + userId: string; +}; +type AppEnableOrDisableUserAppSubscriptionRequestBody = { + action: "enable" | "disable"; +}; +type AppGetUsersEntitlementsPathParams = { + userId: string; +}; +type AppGetUsersEntitlementsResponse = { + entitlements?: { + entitlement_id?: number; + }[]; +}; +type AppsGenerateAppDeeplinkRequestBody = { + type?: 1 | 2; + user_id?: string; + action?: string; +}; +type AppsGenerateAppDeeplinkResponse = { + deeplink?: string; +}; +type ManifestValidateAppManifestRequestBody = { + manifest: object; + app_id?: string; +}; +type ManifestValidateAppManifestResponse = { + ok?: boolean; + error?: string; + errors?: { + message: string; + setting: string; + }[]; +}; +type ManifestExportAppManifestFromExistingAppPathParams = { + appId: string; +}; +type ManifestExportAppManifestFromExistingAppResponse = { + manifest?: object; +}; +type ManifestUpdateAppByManifestPathParams = { + appId: string; +}; +type ManifestUpdateAppByManifestRequestBody = { + manifest: object; +}; +declare class MarketplaceEndpoints extends WebEndpoints { + readonly app: { + sendAppNotifications: (_: object & { + body?: AppSendAppNotificationsRequestBody; + }) => Promise>; + getUserOrAccountEventSubscription: (_: object & { + query: AppGetUserOrAccountEventSubscriptionQueryParams; + }) => Promise>; + createEventSubscription: (_: object & { + body: AppCreateEventSubscriptionRequestBody; + }) => Promise>; + unsubscribeAppEventSubscription: (_: object & { + query: AppUnsubscribeAppEventSubscriptionQueryParams; + }) => Promise>; + deleteEventSubscription: (_: { + path: AppDeleteEventSubscriptionPathParams; + } & object) => Promise>; + subscribeEventSubscription: (_: { + path: AppSubscribeEventSubscriptionPathParams; + } & { + body: AppSubscribeEventSubscriptionRequestBody; + } & object) => Promise>; + listApps: (_: object & { + query?: AppListAppsQueryParams; + }) => Promise>; + createApps: (_: object & { + body: AppCreateAppsRequestBody; + }) => Promise>; + getInformationAboutApp: (_: { + path: AppGetInformationAboutAppPathParams; + } & object) => Promise>; + deletesApp: (_: { + path: AppDeletesAppPathParams; + } & object) => Promise>; + getAPICallLogs: (_: { + path: AppGetAPICallLogsPathParams; + } & object & { + query?: AppGetAPICallLogsQueryParams; + }) => Promise>; + generateZoomAppDeeplink: (_: { + path: AppGenerateZoomAppDeeplinkPathParams; + } & { + body: AppGenerateZoomAppDeeplinkRequestBody; + } & object) => Promise>; + updateAppPreApprovalSetting: (_: { + path: AppUpdateAppPreApprovalSettingPathParams; + } & { + body?: AppUpdateAppPreApprovalSettingRequestBody; + } & object) => Promise>; + getAppsUserRequests: (_: { + path: AppGetAppsUserRequestsPathParams; + } & object & { + query?: AppGetAppsUserRequestsQueryParams; + }) => Promise>; + addAppAllowRequestsForUsers: (_: { + path: AppAddAppAllowRequestsForUsersPathParams; + } & { + body: AppAddAppAllowRequestsForUsersRequestBody; + } & object) => Promise>; + updateAppsRequestStatus: (_: { + path: AppUpdateAppsRequestStatusPathParams; + } & { + body: AppUpdateAppsRequestStatusRequestBody; + } & object) => Promise>; + rotateClientSecret: (_: { + path: AppRotateClientSecretPathParams; + } & { + body: AppRotateClientSecretRequestBody; + } & object) => Promise>; + getWebhookLogs: (_: { + path: AppGetWebhookLogsPathParams; + } & object & { + query?: AppGetWebhookLogsQueryParams; + }) => Promise>; + getAppUserEntitlements: (_: object & { + query?: AppGetAppUserEntitlementsQueryParams; + }) => Promise>; + getUsersAppRequests: (_: { + path: AppGetUsersAppRequestsPathParams; + } & object & { + query?: AppGetUsersAppRequestsQueryParams; + }) => Promise>; + enableOrDisableUserAppSubscription: (_: { + path: AppEnableOrDisableUserAppSubscriptionPathParams; + } & { + body: AppEnableOrDisableUserAppSubscriptionRequestBody; + } & object) => Promise>; + getUsersEntitlements: (_: { + path: AppGetUsersEntitlementsPathParams; + } & object) => Promise>; + }; + readonly apps: { + generateAppDeeplink: (_: object & { + body?: AppsGenerateAppDeeplinkRequestBody; + }) => Promise>; + }; + readonly manifest: { + validateAppManifest: (_: object & { + body: ManifestValidateAppManifestRequestBody; + }) => Promise>; + exportAppManifestFromExistingApp: (_: { + path: ManifestExportAppManifestFromExistingAppPathParams; + } & object) => Promise>; + updateAppByManifest: (_: { + path: ManifestUpdateAppByManifestPathParams; + } & { + body: ManifestUpdateAppByManifestRequestBody; + } & object) => Promise>; + }; +} + +type AppDeauthorizedEvent = Event<"app_deauthorized"> & { + event?: string; + payload?: { + user_id?: string; + account_id?: string; + client_id?: string; + deauthorization_time?: string; + signature?: string; + event_ts?: number; + }; +}; +type AppAuthorizationRequestCreatedEvent = Event<"app.authorization_request_created"> & { + event?: string; + event_ts?: number; + payload?: { + app_name?: string; + app_type?: string; + app_status?: "published" | "development"; + app_description?: string; + app_link?: { + developer_documentation?: string; + developer_privacy_policy?: string; + developer_support?: string; + developer_terms_of_use?: string; + }; + }; +}; +type MarketplaceEvents = AppDeauthorizedEvent | AppAuthorizationRequestCreatedEvent; +declare class MarketplaceEventProcessor extends EventManager { +} + +type MarketplaceOAuthOptions = CommonClientOptions; +declare class MarketplaceOAuthClient = MarketplaceOAuthOptions> extends ProductClient { + protected initAuth({ clientId, clientSecret, tokenStore, ...restOptions }: OptionsType): OAuth; + protected initEndpoints(auth: OAuth, options: OptionsType): MarketplaceEndpoints; + protected initEventProcessor(endpoints: MarketplaceEndpoints): MarketplaceEventProcessor; +} + +type MarketplaceS2SAuthOptions = CommonClientOptions; +declare class MarketplaceS2SAuthClient = MarketplaceS2SAuthOptions> extends ProductClient { + protected initAuth({ clientId, clientSecret, tokenStore, accountId }: OptionsType): S2SAuth; + protected initEndpoints(auth: S2SAuth, options: OptionsType): MarketplaceEndpoints; + protected initEventProcessor(endpoints: MarketplaceEndpoints): MarketplaceEventProcessor; +} + +export { ApiResponseError, AwsLambdaReceiver, AwsReceiverRequestError, ClientCredentialsRawResponseError, CommonHttpRequestError, ConsoleLogger, HTTPReceiverConstructionError, HTTPReceiverPortNotNumberError, HTTPReceiverRequestError, HttpReceiver, LogLevel, MarketplaceEndpoints, MarketplaceEventProcessor, MarketplaceOAuthClient, MarketplaceS2SAuthClient, OAuthInstallerNotInitializedError, OAuthStateVerificationFailedError, OAuthTokenDoesNotExistError, OAuthTokenFetchFailedError, OAuthTokenRawResponseError, OAuthTokenRefreshFailedError, ProductClientConstructionError, ReceiverInconsistentStateError, ReceiverOAuthFlowError, S2SRawResponseError, StatusCode, isCoreError, isStateStore }; +export type { AppAddAppAllowRequestsForUsersPathParams, AppAddAppAllowRequestsForUsersRequestBody, AppAddAppAllowRequestsForUsersResponse, AppAuthorizationRequestCreatedEvent, AppCreateAppsRequestBody, AppCreateAppsResponse, AppCreateEventSubscriptionRequestBody, AppCreateEventSubscriptionResponse, AppDeauthorizedEvent, AppDeleteEventSubscriptionPathParams, AppDeletesAppPathParams, AppEnableOrDisableUserAppSubscriptionPathParams, AppEnableOrDisableUserAppSubscriptionRequestBody, AppGenerateZoomAppDeeplinkPathParams, AppGenerateZoomAppDeeplinkRequestBody, AppGenerateZoomAppDeeplinkResponse, AppGetAPICallLogsPathParams, AppGetAPICallLogsQueryParams, AppGetAPICallLogsResponse, AppGetAppUserEntitlementsQueryParams, AppGetAppUserEntitlementsResponse, AppGetAppsUserRequestsPathParams, AppGetAppsUserRequestsQueryParams, AppGetAppsUserRequestsResponse, AppGetInformationAboutAppPathParams, AppGetInformationAboutAppResponse, AppGetUserOrAccountEventSubscriptionQueryParams, AppGetUserOrAccountEventSubscriptionResponse, AppGetUsersAppRequestsPathParams, AppGetUsersAppRequestsQueryParams, AppGetUsersAppRequestsResponse, AppGetUsersEntitlementsPathParams, AppGetUsersEntitlementsResponse, AppGetWebhookLogsPathParams, AppGetWebhookLogsQueryParams, AppGetWebhookLogsResponse, AppListAppsQueryParams, AppListAppsResponse, AppRotateClientSecretPathParams, AppRotateClientSecretRequestBody, AppRotateClientSecretResponse, AppSendAppNotificationsRequestBody, AppSubscribeEventSubscriptionPathParams, AppSubscribeEventSubscriptionRequestBody, AppUnsubscribeAppEventSubscriptionQueryParams, AppUpdateAppPreApprovalSettingPathParams, AppUpdateAppPreApprovalSettingRequestBody, AppUpdateAppPreApprovalSettingResponse, AppUpdateAppsRequestStatusPathParams, AppUpdateAppsRequestStatusRequestBody, AppsGenerateAppDeeplinkRequestBody, AppsGenerateAppDeeplinkResponse, ClientCredentialsToken, HttpReceiverOptions, JwtToken, Logger, ManifestExportAppManifestFromExistingAppPathParams, ManifestExportAppManifestFromExistingAppResponse, ManifestUpdateAppByManifestPathParams, ManifestUpdateAppByManifestRequestBody, ManifestValidateAppManifestRequestBody, ManifestValidateAppManifestResponse, MarketplaceEvents, MarketplaceOAuthOptions, MarketplaceS2SAuthOptions, OAuthToken, Receiver, ReceiverInitOptions, S2SAuthToken, StateStore, TokenStore }; diff --git a/marketplace/marketplace.mjs b/marketplace/marketplace.mjs new file mode 100644 index 0000000..c4983d2 --- /dev/null +++ b/marketplace/marketplace.mjs @@ -0,0 +1,1155 @@ +import { createHmac, randomBytes } from 'node:crypto'; +import { createServer as createServer$1 } from 'node:http'; +import { createServer } from 'node:https'; +import axios from 'axios'; +import dayjs from 'dayjs'; +import { Buffer as Buffer$1 } from 'node:buffer'; +import { SignJWT, jwtVerify } from 'jose'; +import FormData from 'form-data'; +import os from 'node:os'; +import { basename } from 'node:path'; + +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +const isStateStore = (obj) => typeof obj.generateState === "function" && typeof obj.verifyState === "function"; + +const createRivetErrors = (errors) => ({ + createError: (errorCode) => class extends Error { + errorCode = errors[errorCode]; + constructor(message, options) { + const errorMessage = (message ?? + (options?.cause instanceof Error ? options.cause.message : errorCode)); + super(errorMessage, options); + this.name = errorCode; + Object.setPrototypeOf(this, new.target.prototype); + } + }, + isError: (obj, key) => key ? + Object.keys(errors).some((code) => code === key) && + typeof obj.errorCode === "string" && + obj.errorCode === errors[key] + : typeof obj.errorCode === "string" +}); + +const coreErrors = { + ApiResponseError: "zoom_rivet_api_response_error", + AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error", + ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error", + S2SRawResponseError: "zoom_rivet_s2s_raw_response_error", + CommonHttpRequestError: "zoom_rivet_common_http_request_error", + ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error", + ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error", + HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error", + HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error", + HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error", + OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error", + OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error", + OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error", + OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error", + OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error", + OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error", + ProductClientConstructionError: "zoom_rivet_product_client_construction_error" +}; +const { createError: createCoreError, isError: isCoreError } = createRivetErrors(coreErrors); +const ApiResponseError = createCoreError("ApiResponseError"); +const AwsReceiverRequestError = createCoreError("AwsReceiverRequestError"); +const ClientCredentialsRawResponseError = createCoreError("ClientCredentialsRawResponseError"); +const S2SRawResponseError = createCoreError("S2SRawResponseError"); +const CommonHttpRequestError = createCoreError("CommonHttpRequestError"); +const ReceiverInconsistentStateError = createCoreError("ReceiverInconsistentStateError"); +const ReceiverOAuthFlowError = createCoreError("ReceiverOAuthFlowError"); +const HTTPReceiverConstructionError = createCoreError("HTTPReceiverConstructionError"); +const HTTPReceiverPortNotNumberError = createCoreError("HTTPReceiverPortNotNumberError"); +const HTTPReceiverRequestError = createCoreError("HTTPReceiverRequestError"); +const OAuthInstallerNotInitializedError = createCoreError("OAuthInstallerNotInitializedError"); +const OAuthTokenDoesNotExistError = createCoreError("OAuthTokenDoesNotExistError"); +const OAuthTokenFetchFailedError = createCoreError("OAuthTokenFetchFailedError"); +const OAuthTokenRawResponseError = createCoreError("OAuthTokenRawResponseError"); +const OAuthTokenRefreshFailedError = createCoreError("OAuthTokenRefreshFailedError"); +const OAuthStateVerificationFailedError = createCoreError("OAuthStateVerificationFailedError"); +const ProductClientConstructionError = createCoreError("ProductClientConstructionError"); + +var LogLevel; +(function (LogLevel) { + LogLevel["ERROR"] = "error"; + LogLevel["WARN"] = "warn"; + LogLevel["INFO"] = "info"; + LogLevel["DEBUG"] = "debug"; +})(LogLevel || (LogLevel = {})); +class ConsoleLogger { + level; + name; + static labels = (() => { + const entries = Object.entries(LogLevel); + const map = entries.map(([key, value]) => [value, `[${key}] `]); + return new Map(map); + })(); + static severity = { + [LogLevel.ERROR]: 400, + [LogLevel.WARN]: 300, + [LogLevel.INFO]: 200, + [LogLevel.DEBUG]: 100 + }; + constructor() { + this.level = LogLevel.INFO; + this.name = ""; + } + getLevel() { + return this.level; + } + setLevel(level) { + this.level = level; + } + setName(name) { + this.name = name; + } + debug(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(LogLevel.DEBUG, this.level)) { + console.debug(ConsoleLogger.labels.get(LogLevel.DEBUG), this.name, ...msg); + } + } + info(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(LogLevel.INFO, this.level)) { + console.info(ConsoleLogger.labels.get(LogLevel.INFO), this.name, ...msg); + } + } + warn(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(LogLevel.WARN, this.level)) { + console.warn(ConsoleLogger.labels.get(LogLevel.WARN), this.name, ...msg); + } + } + error(...msg) { + if (ConsoleLogger.isMoreOrEqualSevere(LogLevel.ERROR, this.level)) { + console.error(ConsoleLogger.labels.get(LogLevel.ERROR), this.name, ...msg); + } + } + static isMoreOrEqualSevere(a, b) { + return ConsoleLogger.severity[a] >= ConsoleLogger.severity[b]; + } +} + +class EventManager { + endpoints; + /** @internal */ + listeners; + constructor(endpoints) { + this.endpoints = endpoints; + this.listeners = {}; + } + appendListener(eventName, predicate, listener) { + if (this.listeners[eventName]) { + this.listeners[eventName].push({ predicate, listener }); + } + else { + this.listeners[eventName] = [{ predicate, listener }]; + } + } + filteredEvent(eventName, predicate, listener) { + if (typeof predicate !== "function" || typeof listener !== "function") { + throw new Error("Event predicate and listener must be of type function."); + } + this.appendListener(eventName, predicate, listener); + } + async emit(eventName, payload) { + if (!this.listeners[eventName]) + return; + await Promise.all(this.listeners[eventName].map(async ({ predicate, listener }) => { + if (typeof predicate !== "undefined" && !predicate(payload)) + return; + await Promise.resolve(listener(payload)); + })); + } + event(eventName, listener) { + if (typeof listener !== "function") { + throw new Error("Event listener must be of type function."); + } + this.appendListener(eventName, undefined, listener); + } + withContext() { + throw new Error("Method not implemented. Only to be used for type."); + } +} + +/** @internal */ +const hashUrlValidationEvent = ({ payload: { plainToken } }, webhooksSecretToken) => ({ + encryptedToken: createHmac("sha256", webhooksSecretToken).update(plainToken).digest("hex"), + plainToken +}); +const isHashedUrlValidation = (obj) => typeof obj.encryptedToken === "string" && + typeof obj.plainToken === "string"; +const isRawUrlValidationEvent = (obj) => obj.event === "endpoint.url_validation" && typeof obj.payload.plainToken === "string"; +const isSkeletonEvent = (obj) => typeof obj.event === "string"; +class CommonHttpRequest { + headers; + payload; + webhooksSecretToken; + constructor(headers, payload, webhooksSecretToken) { + this.headers = headers; + this.payload = payload; + this.webhooksSecretToken = webhooksSecretToken; + } + static buildFromAwsEvent({ body, headers, isBase64Encoded }, webhooksSecretToken) { + try { + const rawBody = body ?? ""; + const decodedBody = isBase64Encoded ? Buffer.from(rawBody, "base64").toString("ascii") : rawBody; + const payload = JSON.parse(decodedBody); + return new CommonHttpRequest(headers, payload, webhooksSecretToken); + } + catch (err) { + throw err instanceof SyntaxError ? + new CommonHttpRequestError("Failed to parse payload string to JSON.", err) + : err; + } + } + static async buildFromIncomingMessage(incomingMessage, webhooksSecretToken) { + const bufferAsString = () => { + return new Promise((resolve, reject) => { + const body = []; + incomingMessage.on("data", (chunk) => body.push(chunk)); + incomingMessage.on("error", (err) => { + reject(err); + }); + incomingMessage.on("end", () => { + resolve(Buffer.concat(body).toString()); + }); + }); + }; + try { + const payload = JSON.parse(await bufferAsString()); + return new CommonHttpRequest(incomingMessage.headers, payload, webhooksSecretToken); + } + catch (err) { + if (err instanceof SyntaxError) { + throw new CommonHttpRequestError("Failed to parse payload string to JSON.", err); + } + throw err; + } + } + isEventVerified() { + const { signature, requestTimestamp } = this.parseHeaders(); + const messageToVerify = `v0:${requestTimestamp.toString()}:${JSON.stringify(this.payload)}`; + const hashToVerify = createHmac("sha256", this.webhooksSecretToken).update(messageToVerify).digest("hex"); + const signatureToVerify = `v0=${hashToVerify}`; + return signatureToVerify === signature; + } + parseHeaders() { + const findHeader = (header) => { + const foundHeader = Object.keys(this.headers).find((key) => key.toLowerCase() === header.toLowerCase()); + return foundHeader && this.headers[foundHeader]; + }; + const headerSignature = findHeader("x-zm-signature"); + const headerRequestTimestamp = findHeader("x-zm-request-timestamp"); + if (!headerSignature && !headerRequestTimestamp) { + throw new CommonHttpRequestError("Request payload must have signature and request timestamp from Zoom."); + } + return { + signature: headerSignature, + requestTimestamp: Number(headerRequestTimestamp) + }; + } + processEvent() { + if (!isSkeletonEvent(this.payload)) { + throw new CommonHttpRequestError("Request payload structure does not match expected from Zoom."); + } + if (!this.isEventVerified()) { + throw new CommonHttpRequestError("Failed to verify event originated from Zoom."); + } + if (isRawUrlValidationEvent(this.payload)) { + return hashUrlValidationEvent(this.payload, this.webhooksSecretToken); + } + return this.payload; + } +} + +var StatusCode; +(function (StatusCode) { + StatusCode[StatusCode["OK"] = 200] = "OK"; + StatusCode[StatusCode["TEMPORARY_REDIRECT"] = 302] = "TEMPORARY_REDIRECT"; + StatusCode[StatusCode["BAD_REQUEST"] = 400] = "BAD_REQUEST"; + StatusCode[StatusCode["NOT_FOUND"] = 404] = "NOT_FOUND"; + StatusCode[StatusCode["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED"; + StatusCode[StatusCode["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR"; +})(StatusCode || (StatusCode = {})); + +class AwsLambdaReceiver { + eventEmitter; + webhooksSecretToken; + constructor({ webhooksSecretToken }) { + this.webhooksSecretToken = webhooksSecretToken; + } + buildResponse(statusCode, body) { + return { + body: JSON.stringify(body), + headers: { "Content-Type": "application/json" }, + statusCode + }; + } + canInstall() { + return false; + } + init({ eventEmitter }) { + this.eventEmitter = eventEmitter; + } + start() { + return async (event, context) => { + console.debug("Processing Lambda event ", JSON.stringify(event), " with context ", JSON.stringify(context)); + try { + const request = CommonHttpRequest.buildFromAwsEvent(event, this.webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + return this.buildResponse(StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + return this.buildResponse(StatusCode.OK, { message: "Zoom event processed successfully." }); + } + } + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + return this.buildResponse(StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + return this.buildResponse(StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } + } + }; + } + async stop() { + return Promise.resolve(); + } +} + +const prependSlashes = (strs) => { + const rawStrs = Array.isArray(strs) ? strs : [strs]; + const mappedStrs = rawStrs.map((rawStr) => (rawStr.startsWith("/") ? rawStr : `/${rawStr}`)); + return (Array.isArray(strs) ? mappedStrs : mappedStrs[0]); +}; + +class TokenMemoryStore { + currentToken; + getLatestToken() { + return this.currentToken; + } + storeToken(token) { + this.currentToken = token; + } +} + +/** @internal */ +const EXPIRATION_DELTA_SECONDS = 60; +/** @internal */ +const OAUTH_BASE_URL = "https://zoom.us"; +/** @internal */ +const OAUTH_TOKEN_PATH = "/oauth/token"; +/** + * {@link Auth} is the base implementation of authentication for Zoom's APIs. + * + * It only requires a `clientId` and `tokenStore`, as these options are shared across + * all authentication implementations, namely OAuth and server-to-server auth (client + * credentials, JWT, and server-to-server OAuth.) + */ +class Auth { + clientId; + clientSecret; + tokenStore; + logger; + constructor({ clientId, clientSecret, tokenStore, logger }) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.tokenStore = tokenStore ?? new TokenMemoryStore(); + this.logger = logger; + } + getBasicAuthorization() { + const clientCredentials = `${this.clientId}:${this.clientSecret}`; + return Buffer$1.from(clientCredentials).toString("base64"); + } + isAlmostExpired(isoTime) { + const currentDate = dayjs(); + return dayjs(isoTime).diff(currentDate, "seconds") <= EXPIRATION_DELTA_SECONDS; + } + async makeOAuthTokenRequest(grantType, payload) { + return await axios({ + method: "POST", + url: new URL(OAUTH_TOKEN_PATH, OAUTH_BASE_URL).toString(), + headers: { + Authorization: `Basic ${this.getBasicAuthorization()}`, + "Content-Type": "application/x-www-form-urlencoded" + }, + data: new URLSearchParams({ grant_type: grantType, ...payload }), + validateStatus: (status) => status >= 200 && status <= 299 + }); + } +} + +const DEFAULT_EXPIRATION_SECONDS = 300; // 5 minutes +/** @internal */ +const ISSUER_URN = "urn:zoom:rivet-sdk"; +class JwtStateStore { + encodedSecret; + expirationSeconds; + constructor({ expirationSeconds, stateSecret }) { + this.encodedSecret = new TextEncoder().encode(stateSecret); + this.expirationSeconds = expirationSeconds ?? DEFAULT_EXPIRATION_SECONDS; + } + async generateState() { + const issuedTime = dayjs(); + const expirationTime = issuedTime.add(this.expirationSeconds, "seconds"); + return await new SignJWT({ random: randomBytes(8).toString("hex") }) + .setProtectedHeader({ alg: "HS256", typ: "JWT" }) + .setExpirationTime(expirationTime.toDate()) + .setIssuedAt(issuedTime.toDate()) + .setIssuer(ISSUER_URN) + .sign(this.encodedSecret); + } + async verifyState(state) { + try { + await jwtVerify(state, this.encodedSecret, { + algorithms: ["HS256"], + issuer: ISSUER_URN, + typ: "JWT" + }); + } + catch (err) { + throw new OAuthStateVerificationFailedError(`Failed to verify OAuth state: ${err.name}.`, { + cause: err + }); + } + } +} + +const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; +const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds +const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; +const hasInstallerOptions = (obj) => typeof obj.installerOptions.redirectUri !== "undefined" && + typeof obj.installerOptions.stateStore !== "undefined"; +/** + * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication + * is initiated server-side, but requires manual authorization from a user, by redirecting the user to Zoom. + * + * In addition to all required fields from {@link AuthOptions}, this class requires a `redirectUri`, as this + * value is appended to the authorization URL when the user is redirected to Zoom and subsequently redirected + * back to an endpoint on this server. + * + * @see {@link https://developers.zoom.us/docs/integrations/oauth/ | OAuth - Zoom Developers} + */ +class InteractiveAuth extends Auth { + installerOptions; + async getAuthorizationUrl() { + if (!this.installerOptions?.stateStore) { + throw new OAuthInstallerNotInitializedError("Cannot generate authorization URL, state store not initialized."); + } + const authUrl = new URL(OAUTH_AUTHORIZE_PATH, OAUTH_BASE_URL); + const generatedState = await Promise.resolve(this.installerOptions.stateStore.generateState()); + const { searchParams } = authUrl; + searchParams.set("client_id", this.clientId); + searchParams.set("redirect_uri", this.getFullRedirectUri()); + searchParams.set("response_type", "code"); + searchParams.set("state", generatedState); + return { + fullUrl: authUrl.toString(), + generatedState + }; + } + getFullRedirectUri() { + if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { + throw new OAuthInstallerNotInitializedError("Cannot generate full redirect URI, redirect URI or redirect URI path not initialized."); + } + return new URL(this.installerOptions.redirectUriPath, this.installerOptions.redirectUri).toString(); + } + // Don't return a type; we want it to be as narrow as possible (used for ReturnType). + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { + const updatedOptions = { + directInstall: Boolean(directInstall), + installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, + redirectUri, + redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE + }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } + this.installerOptions = updatedOptions; + return updatedOptions; + } +} + +const mergeDefaultOptions = (options, defaultOptions) => ({ ...defaultOptions, ...options }); + +const withDefaultTemplate = (cardContent, buttonContent) => ` + + + Zoom Rivet + + + + + + + + + +`; +/** + * Get the default HTML template that is shown to the developer/user when they visit the + * `installPath` endpoint, if Rivet currently has OAuth enabled. + * + * If `directInstall` is set to `true`, this function is not called; instead, the developer + * is directly redirected to Zoom's OAuth page. + */ +const defaultInstallTemplate = (authUrl) => withDefaultTemplate(`

Click the button below to navigate to Zoom to authorize your application for use with Rivet.

`, { href: authUrl, text: "Authorize with Zoom" }); +/** + * Get the default HTML template that is shown to the developer/user when they successfully + * authorize Rivet with a Zoom application. This is shown once they have already been redirected + * to Zoom, and the authorization attempt with Rivet was successful. + */ +const defaultCallbackSuccessTemplate = () => withDefaultTemplate(`

Your application has been successfully authorized with Rivet!

+

You may now close this page, or click the button below to redirect to Zoom's Marketplace.

`, { href: "https://marketplace.zoom.us", text: "Go to Marketplace" }); +/** + * Get the default HTML template that is shown to the developer when a known error occurs, meaning + * that the error is a core Rivet error. + */ +const defaultCallbackKnownErrorTemplate = (errName, errMessage) => withDefaultTemplate(`

An error occurred authorizing Rivet with Zoom.

+

[${errName}]: ${errMessage}

`); +/** + * Get the default HTML template that is shown to the developer when an unknown error occurs, + * meaning that the error is not known to be a core Rivet error and was thrown and not wrapped elsewhere. + */ +const defaultCallbackUnknownErrorTemplate = () => withDefaultTemplate(`

An unknown error occurred authorizing Rivet with Zoom. Please see stacktrace for details.

+

Please see stacktrace for further details.

`); + +const secureServerOptionKeys = [ + "ALPNProtocols", + "clientCertEngine", + "enableTrace", + "handshakeTimeout", + "rejectUnauthorized", + "requestCert", + "sessionTimeout", + "SNICallback", + "ticketKeys", + "pskCallback", + "pskIdentityHint", + "ca", + "cert", + "sigalgs", + "ciphers", + "clientCertEngine", + "crl", + "dhparam", + "ecdhCurve", + "honorCipherOrder", + "key", + "privateKeyEngine", + "privateKeyIdentifier", + "maxVersion", + "minVersion", + "passphrase", + "pfx", + "secureOptions", + "secureProtocol", + "sessionIdContext" +]; +class HttpReceiver { + /** @internal */ + static DEFAULT_ENDPOINT = "/zoom/events"; + eventEmitter; + interactiveAuth; + /** @internal */ + options; + server; + logger; + constructor(options) { + this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); + this.options.endpoints = prependSlashes(this.options.endpoints); + this.logger = + options.logger ?? + (() => { + const defaultLogger = new ConsoleLogger(); + defaultLogger.setLevel(options.logLevel ?? LogLevel.ERROR); + return defaultLogger; + })(); + } + canInstall() { + return true; + } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } + getServerCreator() { + return this.hasSecureOptions() ? createServer : createServer$1; + } + hasEndpoint(pathname) { + const { endpoints } = this.options; + return Array.isArray(endpoints) ? endpoints.includes(pathname) : endpoints === pathname; + } + hasSecureOptions() { + return Object.keys(this.options).some((option) => secureServerOptionKeys.includes(option)); + } + init({ eventEmitter, interactiveAuth }) { + this.eventEmitter = eventEmitter; + this.interactiveAuth = interactiveAuth; + } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } + start(port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { + const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; + this.logger.error(errorMessage); + throw new HTTPReceiverPortNotNumberError(errorMessage); + } + const listenPort = port ?? this.options.port; + return new Promise((resolve, reject) => { + this.server = this.getServerCreator()(this.options, (req, res) => void (async () => { + // `req.headers.host` should be used with care, as clients can manipulate this value. + // However, for this use case, the value is completely discarded and only `pathname` + // is used, which is why there's no further validation occurring. + const { pathname, searchParams } = new URL(req.url ?? "", `http://${req.headers.host ?? "localhost"}`); + const { interactiveAuth } = this; + this.logger.debug([pathname, searchParams]); + // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath + if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { + const { installerOptions } = interactiveAuth; + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); + await (installerOptions.directInstall ? + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); + return; + } + // The user has navigated to the redirect page; init the code + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); + try { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { + const errorMessage = "OAuth callback did not include code and/or state in request."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); + return; + } + catch (err) { + const htmlTemplate = isCoreError(err) ? + defaultCallbackKnownErrorTemplate(err.name, err.message) + : defaultCallbackUnknownErrorTemplate(); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); + return; + } + } + } + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, StatusCode.NOT_FOUND); + return; + } + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); + return; + } + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + } + } + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } + } + } + })()); + this.server.on("close", () => (this.server = undefined)); + this.server.on("error", (err) => { + this.logger.error(err.message); + reject(err); + }); + this.server.listen(listenPort, () => { + if (!this.server) { + throw new ReceiverInconsistentStateError(); + } + const { port: listeningPort } = this.server.address(); + this.logger.info(`Listening on port ${listeningPort.toString()}`); + resolve(this.server); + }); + }); + } + stop() { + if (!this.server) { + throw new ReceiverInconsistentStateError(); + } + return new Promise((resolve, reject) => { + this.server?.close((err) => { + if (err) { + this.logger.error(err.message); + reject(err); + } + }); + this.server = undefined; + resolve(); + }); + } + writeTemporaryRedirect(res, location, setCookie) { + return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } + res.writeHead(StatusCode.TEMPORARY_REDIRECT, { Location: location }); + res.end(() => { + resolve(); + }); + }); + } + writeResponse(res, statusCode, bodyContent, setCookie) { + return new Promise((resolve) => { + const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; + bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } + res.writeHead(statusCode, { "Content-Type": mimeType }); + res.end(bodyContent, () => { + resolve(); + }); + }); + } +} + +const version = "0.4.0"; +var packageJson = { + version: version}; + +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; +class WebEndpoints { + /** @internal */ + static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; + /** @internal */ + static DEFAULT_MIME_TYPE = "application/json"; + /** @internal */ + static DEFAULT_TIMEOUT = 0; + /** @internal */ + static GENERIC_ERROR_MESSAGE = "Request was unsuccessful with no further context"; + /** @internal */ + static TRACKING_ID_HEADER = "x-zm-trackingid"; + /** @internal */ + options; + constructor(options) { + this.options = mergeDefaultOptions(options, { + baseUrl: WebEndpoints.DEFAULT_BASE_URL, + hasCustomBaseUrl: typeof options.baseUrl !== "undefined", + timeout: WebEndpoints.DEFAULT_TIMEOUT + }); + } + buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }) { + // @ts-expect-error: Some arguments may not be present, but we pass them to makeRequest() anyway. + // prettier-ignore + // Next AST node is ignored by Prettier, even though it exceed maximum line length, because TypeScript + // won't allow ts-expect-error directive on multiple lines (https://github.com/Microsoft/TypeScript/issues/19573). + return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); + } + buildUserAgent() { + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + + `${basename(process.title)}/${process.version.replace("v", "")} ` + + `${os.platform()}/${os.release()}`); + } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } + getHeaders(bearerToken, contentType) { + return { + Accept: "application/json", + Authorization: `Bearer ${bearerToken}`, + "Content-Type": contentType, + "User-Agent": this.buildUserAgent() + }; + } + getRequestBody(args, mimeType) { + if (mimeType === "multipart/form-data") { + const formData = new FormData(); + Object.entries(args).forEach(([key, value]) => { + formData.append(key, value); + }); + return formData; + } + return args; + } + isOk(response) { + return response.status >= 200 && response.status <= 299; + } + isZoomResponseError(obj) { + return (typeof obj.code !== "undefined" && + typeof obj.message !== "undefined"); + } + async makeRequest(method, baseUrlOverride, url, requestContentType, bodyArgs, queryArgs) { + const { auth, baseUrl, doubleEncodeUrl, hasCustomBaseUrl, timeout } = this.options; + const bearerToken = await Promise.resolve(auth.getToken()); + const urlToSend = doubleEncodeUrl ? encodeURIComponent(encodeURIComponent(url)) : url; + const response = await axios({ + url: urlToSend, + method, + baseURL: hasCustomBaseUrl ? baseUrl : (baseUrlOverride ?? baseUrl), + headers: this.getHeaders(bearerToken, requestContentType), + params: queryArgs, + data: bodyArgs && this.getRequestBody(bodyArgs, requestContentType), + timeout: timeout, + beforeRedirect: (options) => { + options.headers = { + ...this.getHeaders(bearerToken, requestContentType), + ...options.headers + }; + }, + validateStatus: () => true // All responses are valid, not just 2xx + }); + if (!this.isOk(response)) { + const { status: statusCode } = response; + if (this.isZoomResponseError(response.data)) { + const { code: errorCode, message: errorMessage } = response.data; + throw new ApiResponseError(`[${statusCode.toString()}/${errorCode.toString()}]: "${errorMessage}"`); + } + throw new ApiResponseError(`[${statusCode.toString()}]: ${WebEndpoints.GENERIC_ERROR_MESSAGE}`); + } + return { + data: response.data, + statusCode: response.status, + trackingId: response.headers[WebEndpoints.TRACKING_ID_HEADER] + }; + } +} + +class MarketplaceEndpoints extends WebEndpoints { + app = { + sendAppNotifications: this.buildEndpoint({ + method: "POST", + urlPathBuilder: () => `/app/notifications` + }), + getUserOrAccountEventSubscription: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/marketplace/app/event_subscription` }), + createEventSubscription: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/marketplace/app/event_subscription` }), + unsubscribeAppEventSubscription: this.buildEndpoint({ method: "DELETE", urlPathBuilder: () => `/marketplace/app/event_subscription` }), + deleteEventSubscription: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ eventSubscriptionId }) => `/marketplace/app/event_subscription/${eventSubscriptionId}` + }), + subscribeEventSubscription: this.buildEndpoint({ + method: "PATCH", + urlPathBuilder: ({ eventSubscriptionId }) => `/marketplace/app/event_subscription/${eventSubscriptionId}` + }), + listApps: this.buildEndpoint({ + method: "GET", + urlPathBuilder: () => `/marketplace/apps` + }), + createApps: this.buildEndpoint({ + method: "POST", + urlPathBuilder: () => `/marketplace/apps` + }), + getInformationAboutApp: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}` }), + deletesApp: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}` + }), + getAPICallLogs: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/api_call_logs` }), + generateZoomAppDeeplink: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/deeplink` }), + updateAppPreApprovalSetting: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/preApprove` }), + getAppsUserRequests: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/requests` }), + addAppAllowRequestsForUsers: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/requests` }), + updateAppsRequestStatus: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/requests` }), + rotateClientSecret: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/rotate_client_secret` }), + getWebhookLogs: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/webhook_logs` }), + getAppUserEntitlements: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/marketplace/monetization/entitlements` }), + getUsersAppRequests: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/marketplace/users/${userId}/apps` }), + enableOrDisableUserAppSubscription: this.buildEndpoint({ + method: "PATCH", + urlPathBuilder: ({ appId, userId }) => `/marketplace/users/${userId}/apps/${appId}/subscription` + }), + getUsersEntitlements: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/marketplace/users/${userId}/entitlements` }) + }; + apps = { + generateAppDeeplink: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/zoomapp/deeplink` }) + }; + manifest = { + validateAppManifest: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/marketplace/apps/manifest/validate` }), + exportAppManifestFromExistingApp: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/manifest` }), + updateAppByManifest: this.buildEndpoint({ method: "PUT", urlPathBuilder: ({ appId }) => `/marketplace/apps/${appId}/manifest` }) + }; +} + +class MarketplaceEventProcessor extends EventManager { +} + +class OAuth extends InteractiveAuth { + assertResponseAccessToken(data) { + if (typeof data.access_token !== "string" || + typeof data.refresh_token !== "string" || + typeof data.expires_in !== "number" || + typeof data.scope !== "string") { + throw new OAuthTokenRawResponseError(`Failed to match raw response (${JSON.stringify(data)}) to expected shape.`); + } + } + async fetchAccessToken(code) { + try { + const response = await this.makeOAuthTokenRequest("authorization_code", { + code, + redirect_uri: this.getFullRedirectUri() + }); + this.assertResponseAccessToken(response.data); + return this.mapOAuthToken(response.data); + } + catch (err) { + throw new OAuthTokenFetchFailedError("Failed to fetch OAuth token.", { cause: err }); + } + } + async getToken() { + const { tokenStore } = this; + const currentToken = await Promise.resolve(tokenStore.getLatestToken()); + // If we have no OAuth token, app most likely has not been previously authorized. + if (!currentToken) { + throw new OAuthTokenDoesNotExistError("Failed to find OAuth token. Authorize this app first."); + } + // If the OAuth token hasn't already expired (and isn't within the delta), return it. + if (!this.isAlmostExpired(currentToken.expirationTimeIso)) { + return currentToken.accessToken; + } + // Since the token has expired, refresh, store, and return it. + const refreshedToken = await this.refreshAccessToken(currentToken.refreshToken); + await Promise.resolve(tokenStore.storeToken(refreshedToken)); + return refreshedToken.accessToken; + } + async initRedirectCode(code) { + const { tokenStore } = this; + const accessToken = await this.fetchAccessToken(code); + await Promise.resolve(tokenStore.storeToken(accessToken)); + } + mapOAuthToken({ access_token, expires_in, refresh_token, scope }) { + return { + accessToken: access_token, + expirationTimeIso: dayjs().add(expires_in, "seconds").toISOString(), + refreshToken: refresh_token, + scopes: scope.includes(" ") ? scope.split(" ") : [scope] + }; + } + async refreshAccessToken(refreshToken) { + try { + const response = await this.makeOAuthTokenRequest("refresh_token", { + refresh_token: refreshToken + }); + this.assertResponseAccessToken(response.data); + return this.mapOAuthToken(response.data); + } + catch (err) { + throw new OAuthTokenRefreshFailedError("Failed to refresh OAuth token.", { cause: err }); + } + } +} + +// Utility functions for determining if client options include custom receiver, or, if not, +// a webhooks secret token, as one of those is required! +const hasExplicitReceiver = (obj) => typeof obj.receiver !== "undefined"; +const hasWebhooksSecretToken = (obj) => typeof obj.webhooksSecretToken !== "undefined"; +const isReceiverDisabled = (options) => typeof options.disableReceiver !== "undefined" && options.disableReceiver; +const DEFAULT_HTTP_RECEIVER_PORT = 8080; +const DEFAULT_LOGLEVEL = LogLevel.ERROR; +class ProductClient { + auth; + endpoints; + webEventConsumer; + receiver; + constructor(options) { + this.auth = this.initAuth(options); + this.endpoints = this.initEndpoints(this.auth, options); + this.webEventConsumer = this.initEventProcessor(this.endpoints, options); + // Only create an instance of `this.receiver` if the developer did not explicitly disable it. + if (!isReceiverDisabled(options)) { + // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); + } + this.receiver = (hasExplicitReceiver(options) ? + options.receiver + : this.initDefaultReceiver(options)); + this.receiver.init({ + eventEmitter: this.webEventConsumer, + interactiveAuth: this.auth instanceof InteractiveAuth ? this.auth : undefined + }); + } + } + initDefaultReceiver({ port, webhooksSecretToken, logLevel }) { + return new HttpReceiver({ + port: port ?? DEFAULT_HTTP_RECEIVER_PORT, + webhooksSecretToken, + logLevel: logLevel ?? DEFAULT_LOGLEVEL + }); + } + async start() { + if (!this.receiver) { + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); + } + // Method call is wrapped in `await` and `Promise.resolve()`, as the call + // may or may not return a promise. This is not required when implementing `Receiver`. + return (await Promise.resolve(this.receiver.start())); + } +} + +class MarketplaceOAuthClient extends ProductClient { + initAuth({ clientId, clientSecret, tokenStore, ...restOptions }) { + const oAuth = new OAuth({ clientId, clientSecret, tokenStore }); + if (hasInstallerOptions(restOptions)) { + oAuth.setInstallerOptions(restOptions.installerOptions); + } + return oAuth; + } + initEndpoints(auth, options) { + return new MarketplaceEndpoints({ auth, ...options }); + } + initEventProcessor(endpoints) { + return new MarketplaceEventProcessor(endpoints); + } +} + +class S2SAuth extends Auth { + accountId; + constructor({ accountId, ...restOptions }) { + super(restOptions); + this.accountId = accountId; + } + assertRawToken(obj) { + if (typeof obj.access_token !== "string" || + typeof obj.expires_in !== "number" || + typeof obj.scope !== "string") { + throw new S2SRawResponseError(`Failed to match raw response ${JSON.stringify(obj)} to expected shape.`); + } + } + async fetchAccessToken() { + const response = await this.makeOAuthTokenRequest("account_credentials", { + account_id: this.accountId + }); + this.assertRawToken(response.data); + return this.mapAccessToken(response.data); + } + async getToken() { + const { tokenStore } = this; + const currentToken = await Promise.resolve(tokenStore.getLatestToken()); + if (currentToken && !this.isAlmostExpired(currentToken.expirationTimeIso)) { + return currentToken.accessToken; + } + const token = await this.fetchAccessToken(); + await Promise.resolve(tokenStore.storeToken(token)); + return token.accessToken; + } + mapAccessToken({ access_token, expires_in, scope }) { + return { + accessToken: access_token, + expirationTimeIso: dayjs().add(expires_in, "seconds").toISOString(), + scopes: scope.includes(" ") ? scope.split(" ") : [scope] + }; + } +} + +class MarketplaceS2SAuthClient extends ProductClient { + initAuth({ clientId, clientSecret, tokenStore, accountId }) { + return new S2SAuth({ clientId, clientSecret, tokenStore, accountId }); + } + initEndpoints(auth, options) { + return new MarketplaceEndpoints({ auth, ...options }); + } + initEventProcessor(endpoints) { + return new MarketplaceEventProcessor(endpoints); + } +} + +export { ApiResponseError, AwsLambdaReceiver, AwsReceiverRequestError, ClientCredentialsRawResponseError, CommonHttpRequestError, ConsoleLogger, HTTPReceiverConstructionError, HTTPReceiverPortNotNumberError, HTTPReceiverRequestError, HttpReceiver, LogLevel, MarketplaceEndpoints, MarketplaceEventProcessor, MarketplaceOAuthClient, MarketplaceS2SAuthClient, OAuthInstallerNotInitializedError, OAuthStateVerificationFailedError, OAuthTokenDoesNotExistError, OAuthTokenFetchFailedError, OAuthTokenRawResponseError, OAuthTokenRefreshFailedError, ProductClientConstructionError, ReceiverInconsistentStateError, ReceiverOAuthFlowError, S2SRawResponseError, StatusCode, isCoreError, isStateStore }; diff --git a/meetings/meetings.cjs b/meetings/meetings.cjs index 16978aa..55550b8 100644 --- a/meetings/meetings.cjs +++ b/meetings/meetings.cjs @@ -426,6 +426,9 @@ class JwtStateStore { const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; const hasInstallerOptions = (obj) => typeof obj.installerOptions.redirectUri !== "undefined" && typeof obj.installerOptions.stateStore !== "undefined"; @@ -452,7 +455,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -462,14 +468,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -581,9 +593,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -597,6 +606,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? node_https.createServer : node_http.createServer; } @@ -611,8 +633,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -629,69 +663,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, exports.StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, exports.StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -725,18 +774,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(exports.StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -745,87 +800,13 @@ class HttpReceiver { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -854,10 +835,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${node_path.basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -950,6 +944,8 @@ class MeetingsEndpoints extends WebEndpoints { method: "PUT", urlPathBuilder: ({ meetingId, recordingId }) => `/meetings/${meetingId}/recordings/${recordingId}/status` }), + getMeetingTranscript: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/transcript` }), + deleteMeetingOrWebinarTranscript: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/transcript` }), recoverMeetingRecordings: this.buildEndpoint({ method: "PUT", urlPathBuilder: ({ meetingUUID }) => `/meetings/${meetingUUID}/recordings/status` }), listAllRecordings: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/users/${userId}/recordings` }) }; @@ -964,7 +960,8 @@ class MeetingsEndpoints extends WebEndpoints { }), getZDMGroupInfo: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/devices/groups` }), assignDeviceToUserOrCommonarea: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/devices/zpa/assignment` }), - upgradeZpaOsApp: this.buildEndpoint({ + getZoomPhoneApplianceSettingsByUserID: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/devices/zpa/settings` }), + upgradeZPAFirmwareOrApp: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/devices/zpa/upgrade` }), @@ -982,6 +979,7 @@ class MeetingsEndpoints extends WebEndpoints { method: "PATCH", urlPathBuilder: ({ deviceId }) => `/devices/${deviceId}` }), + assignDeviceToGroup: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ deviceId }) => `/devices/${deviceId}/assign_group` }), changeDeviceAssociation: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ deviceId }) => `/devices/${deviceId}/assignment` }) }; h323Devices = { @@ -1003,7 +1001,8 @@ class MeetingsEndpoints extends WebEndpoints { urlPathBuilder: ({ meetingId, messageId }) => `/live_meetings/${meetingId}/chat/messages/${messageId}` }), useInMeetingControls: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ meetingId }) => `/live_meetings/${meetingId}/events` }), - listMeetingSummariesOfAccount: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/meetings/meeting_summaries` }), + updateParticipantRealTimeMediaStreamsRTMSAppStatus: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ meetingId }) => `/live_meetings/${meetingId}/rtms_app/status` }), + listAccountsMeetingOrWebinarSummaries: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/meetings/meeting_summaries` }), getMeeting: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}` }), deleteMeeting: this.buildEndpoint({ method: "DELETE", @@ -1020,7 +1019,8 @@ class MeetingsEndpoints extends WebEndpoints { getLivestreamDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/livestream` }), updateLivestream: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/livestream` }), updateLivestreamStatus: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/livestream/status` }), - getMeetingSummary: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/meeting_summary` }), + getMeetingOrWebinarSummary: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/meeting_summary` }), + deleteMeetingOrWebinarSummary: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/meeting_summary` }), addMeetingApp: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/open_apps` }), deleteMeetingApp: this.buildEndpoint({ method: "DELETE", @@ -1079,6 +1079,7 @@ class MeetingsEndpoints extends WebEndpoints { getBillingInvoiceReports: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/billing/invoices` }), getCloudRecordingUsageReport: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/cloud_recording` }), getDailyUsageReport: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/daily` }), + getHistoryMeetingAndWebinarList: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/history_meetings` }), getMeetingActivitiesReport: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/meeting_activities` }), getMeetingDetailReports: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/report/meetings/${meetingId}` }), getMeetingParticipantReports: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/report/meetings/${meetingId}/participants` }), @@ -1097,15 +1098,15 @@ class MeetingsEndpoints extends WebEndpoints { getWebinarSurveyReport: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ webinarId }) => `/report/webinars/${webinarId}/survey` }) }; sIPPhone = { - listSIPPhones: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/sip_phones` }), - enableSIPPhone: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/sip_phones` }), + listSIPPhones: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/sip_phones/phones` }), + enableSIPPhone: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/sip_phones/phones` }), deleteSIPPhone: this.buildEndpoint({ method: "DELETE", - urlPathBuilder: ({ phoneId }) => `/sip_phones/${phoneId}` + urlPathBuilder: ({ phoneId }) => `/sip_phones/phones/${phoneId}` }), updateSIPPhone: this.buildEndpoint({ method: "PATCH", - urlPathBuilder: ({ phoneId }) => `/sip_phones/${phoneId}` + urlPathBuilder: ({ phoneId }) => `/sip_phones/phones/${phoneId}` }) }; tSP = { @@ -1322,7 +1323,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -1343,7 +1346,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. diff --git a/meetings/meetings.d.ts b/meetings/meetings.d.ts index 2efb504..9ca7689 100644 --- a/meetings/meetings.d.ts +++ b/meetings/meetings.d.ts @@ -1,96 +1,9 @@ -import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { AxiosResponse } from 'axios'; +import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { Server } from 'node:http'; import { ServerOptions } from 'node:https'; import { ReadStream } from 'node:fs'; -type AllKeysOf = T extends any ? keyof T : never; -type AllPropsOptional = Exclude<{ - [P in keyof T]: undefined extends T[P] ? True : False; -}[keyof T], undefined> extends True ? True : False; -type Constructor = new (...args: any[]) => T; -type ExactlyOneOf = { - [K in keyof T]: T[K] & ProhibitKeys, keyof T[K]>>; -}[number]; -type MaybeArray = T | T[]; -type MaybePromise = T | Promise; -type ProhibitKeys = { - [P in K]?: never; -}; -type StringIndexed = Record; - -/** - * {@link StateStore} defines methods for generating and verifying OAuth state. - * - * This interface is implemented internally for the default state store; however, - * it can also be implemented and passed to an OAuth client as well. - */ -interface StateStore { - /** - * Generate a new state string, which is directly appended to the OAuth `state` parameter. - */ - generateState(): MaybePromise; - /** - * Verify that the state received during OAuth callback is valid and not forged. - * - * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. - * - * @param state The state parameter that was received during OAuth callback - */ - verifyState(state: string): MaybePromise; -} -/** - * Guard if an object implements the {@link StateStore} interface — most notably, - * `generateState()` and `verifyState(state: string)`. - */ -declare const isStateStore: (obj: unknown) => obj is StateStore; - -interface TokenStore { - getLatestToken(): MaybePromise; - storeToken(token: Token): MaybePromise; -} - -interface RivetError extends Error { - readonly errorCode: ErrorCode; -} - -declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ - readonly ApiResponseError: "zoom_rivet_api_response_error"; - readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; - readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; - readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; - readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; - readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; - readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; - readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; - readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; - readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; - readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; - readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; - readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; - readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; - readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; - readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; - readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; -}[K]>; -declare const ApiResponseError: Constructor; -declare const AwsReceiverRequestError: Constructor; -declare const ClientCredentialsRawResponseError: Constructor; -declare const S2SRawResponseError: Constructor; -declare const CommonHttpRequestError: Constructor; -declare const ReceiverInconsistentStateError: Constructor; -declare const ReceiverOAuthFlowError: Constructor; -declare const HTTPReceiverConstructionError: Constructor; -declare const HTTPReceiverPortNotNumberError: Constructor; -declare const HTTPReceiverRequestError: Constructor; -declare const OAuthInstallerNotInitializedError: Constructor; -declare const OAuthTokenDoesNotExistError: Constructor; -declare const OAuthTokenFetchFailedError: Constructor; -declare const OAuthTokenRawResponseError: Constructor; -declare const OAuthTokenRefreshFailedError: Constructor; -declare const OAuthStateVerificationFailedError: Constructor; -declare const ProductClientConstructionError: Constructor; - declare enum LogLevel { ERROR = "error", WARN = "warn", @@ -150,6 +63,19 @@ declare class ConsoleLogger implements Logger { private static isMoreOrEqualSevere; } +type AllPropsOptional = Exclude<{ + [P in keyof T]: undefined extends T[P] ? True : False; +}[keyof T], undefined> extends True ? True : False; +type Constructor = new (...args: any[]) => T; +type MaybeArray = T | T[]; +type MaybePromise = T | Promise; +type StringIndexed = Record; + +interface TokenStore { + getLatestToken(): MaybePromise; + storeToken(token: Token): MaybePromise; +} + interface AuthOptions { clientId: string; clientSecret: string; @@ -195,6 +121,17 @@ declare abstract class Auth { }>, "grant_type">): Promise; } +interface ClientCredentialsToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} + +interface JwtToken { + token: string; + expirationTimeIso: string; +} + interface S2SAuthToken { accessToken: string; expirationTimeIso: string; @@ -233,12 +170,31 @@ declare class EventManager { protected withContext, Context>(): ContextListener; } +declare enum StatusCode { + OK = 200, + TEMPORARY_REDIRECT = 302, + BAD_REQUEST = 400, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + INTERNAL_SERVER_ERROR = 500 +} +interface ReceiverInitOptions { + eventEmitter?: GenericEventManager | undefined; + interactiveAuth?: InteractiveAuth | undefined; +} +interface Receiver { + canInstall(): true | false; + init(options: ReceiverInitOptions): void; + start(...args: any[]): MaybePromise; + stop(...args: any[]): MaybePromise; +} + interface HttpReceiverOptions extends Partial { endpoints?: MaybeArray | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; port?: number | string | undefined; - webhooksSecretToken: string; - logger?: Logger; - logLevel?: LogLevel; + webhooksSecretToken?: string | undefined; } type SecureServerOptions = { [K in (typeof secureServerOptionKeys)[number]]: ServerOptions[K]; @@ -251,10 +207,15 @@ declare class HttpReceiver implements Receiver { private logger; constructor(options: HttpReceiverOptions); canInstall(): true; + private buildDeletedStateCookieHeader; + private buildStateCookieHeader; + private getRequestCookie; private getServerCreator; private hasEndpoint; private hasSecureOptions; init({ eventEmitter, interactiveAuth }: ReceiverInitOptions): void; + private setResponseCookie; + private areNormalizedUrlsEqual; start(port?: number | string): Promise; stop(): Promise; private writeTemporaryRedirect; @@ -277,6 +238,7 @@ interface WebEndpointOptions { baseUrl?: string | undefined; doubleEncodeUrl?: boolean | undefined; timeout?: number | undefined; + userAgentName?: string | undefined; } type EndpointArguments = (PathSchema extends NoParams ? object : AllPropsOptional extends "t" ? { path?: PathSchema; @@ -298,6 +260,7 @@ declare class WebEndpoints { constructor(options: WebEndpointOptions); protected buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }: BuildEndpointOptions): (_: EndpointArguments) => Promise>; private buildUserAgent; + private getCustomUserAgentName; private getHeaders; private getRequestBody; private isOk; @@ -305,7 +268,7 @@ declare class WebEndpoints { private makeRequest; } -type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & { +type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & Pick & { disableReceiver?: boolean | undefined; logger?: Logger | undefined; logLevel?: LogLevel | undefined; @@ -313,38 +276,68 @@ type CommonClientOptions = GetAuthOptions interface ClientReceiverOptions { receiver: R; } -type ClientConstructorOptions, R extends Receiver> = IsReceiverDisabled extends true ? O : O & (ClientReceiverOptions | HttpReceiverOptions); +type ClientConstructorOptions, R extends Receiver> = (O & { + disableReceiver: true; +}) | (O & (ClientReceiverOptions | HttpReceiverOptions)); type ExtractInstallerOptions = A extends InteractiveAuth ? [ ReturnType ] extends [true] ? WideInstallerOptions : object : object; type ExtractAuthTokenType = A extends Auth ? T : never; -type GenericClientOptions = CommonClientOptions; type GetAuthOptions = AuthOptions> & (A extends S2SAuth ? S2SAuthOptions : object); -type IsReceiverDisabled> = [ - O["disableReceiver"] -] extends [true] ? true : false; type WideInstallerOptions = { installerOptions: InstallerOptions; }; declare abstract class ProductClient, ReceiverType extends Receiver> { private readonly auth; readonly endpoints: EndpointsType; - readonly webEventConsumer: EventProcessorType; + readonly webEventConsumer?: EventProcessorType | undefined; private readonly receiver?; constructor(options: ClientConstructorOptions); protected abstract initAuth(options: OptionsType): AuthType; protected abstract initEndpoints(auth: AuthType, options: OptionsType): EndpointsType; - protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType; + protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType | undefined; private initDefaultReceiver; - start(this: IsReceiverDisabled extends true ? never : this): Promise>; + start(): Promise>; +} + +/** + * {@link StateStore} defines methods for generating and verifying OAuth state. + * + * This interface is implemented internally for the default state store; however, + * it can also be implemented and passed to an OAuth client as well. + */ +interface StateStore { + /** + * Generate a new state string, which is directly appended to the OAuth `state` parameter. + */ + generateState(): MaybePromise; + /** + * Verify that the state received during OAuth callback is valid and not forged. + * + * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. + * + * @param state The state parameter that was received during OAuth callback + */ + verifyState(state: string): MaybePromise; } +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +declare const isStateStore: (obj: unknown) => obj is StateStore; +interface AuthorizationUrlResult { + fullUrl: string; + generatedState: string; +} interface InstallerOptions { directInstall?: boolean | undefined; installPath?: string | undefined; redirectUri: string; redirectUriPath?: string | undefined; stateStore: StateStore | string; + stateCookieName?: string | undefined; + stateCookieMaxAge?: number | undefined; } /** * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication @@ -358,36 +351,87 @@ interface InstallerOptions { */ declare abstract class InteractiveAuth extends Auth { installerOptions?: ReturnType; - getAuthorizationUrl(): Promise; + getAuthorizationUrl(): Promise; getFullRedirectUri(): string; - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }: InstallerOptions): { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }: InstallerOptions): { directInstall: boolean; installPath: string; redirectUri: string; redirectUriPath: string; stateStore: StateStore; + stateCookieName: string; + stateCookieMaxAge: number; }; } -declare enum StatusCode { - OK = 200, - TEMPORARY_REDIRECT = 302, - BAD_REQUEST = 400, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - INTERNAL_SERVER_ERROR = 500 +/** + * Credentials for access token & refresh token, which are used to access Zoom's APIs. + * + * As access token is short-lived (usually a single hour), its expiration time is checked + * first. If it's possible to use the access token, it's used; however, if it has expired + * or is close to expiring, the refresh token should be used to generate a new access token + * before the API call is made. Refresh tokens are generally valid for 90 days. + * + * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} + * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. + * It's likely that this error will be rare, but it _can_ be thrown. + */ +interface OAuthToken { + accessToken: string; + expirationTimeIso: string; + refreshToken: string; + scopes: string[]; } -interface ReceiverInitOptions { - eventEmitter: GenericEventManager; - interactiveAuth?: InteractiveAuth | undefined; +declare class OAuth extends InteractiveAuth { + private assertResponseAccessToken; + private fetchAccessToken; + getToken(): Promise; + initRedirectCode(code: string): Promise; + private mapOAuthToken; + private refreshAccessToken; } -interface Receiver { - canInstall(): true | false; - init(options: ReceiverInitOptions): void; - start(...args: any[]): MaybePromise; - stop(...args: any[]): MaybePromise; + +interface RivetError extends Error { + readonly errorCode: ErrorCode; } +declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ + readonly ApiResponseError: "zoom_rivet_api_response_error"; + readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; + readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; + readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; + readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; + readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; + readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; + readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; + readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; + readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; + readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; + readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; + readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; + readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; + readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; + readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; + readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; +}[K]>; +declare const ApiResponseError: Constructor; +declare const AwsReceiverRequestError: Constructor; +declare const ClientCredentialsRawResponseError: Constructor; +declare const S2SRawResponseError: Constructor; +declare const CommonHttpRequestError: Constructor; +declare const ReceiverInconsistentStateError: Constructor; +declare const ReceiverOAuthFlowError: Constructor; +declare const HTTPReceiverConstructionError: Constructor; +declare const HTTPReceiverPortNotNumberError: Constructor; +declare const HTTPReceiverRequestError: Constructor; +declare const OAuthInstallerNotInitializedError: Constructor; +declare const OAuthTokenDoesNotExistError: Constructor; +declare const OAuthTokenFetchFailedError: Constructor; +declare const OAuthTokenRawResponseError: Constructor; +declare const OAuthTokenRefreshFailedError: Constructor; +declare const OAuthStateVerificationFailedError: Constructor; +declare const ProductClientConstructionError: Constructor; + interface AwsLambdaReceiverOptions { webhooksSecretToken: string; } @@ -407,7 +451,7 @@ type ArchivingListArchivedFilesQueryParams = { next_page_token?: string; from?: string; to?: string; - query_date_type?: string; + query_date_type?: "meeting_start_time" | "archive_complete_time"; group_id?: string; group_ids?: string; }; @@ -420,17 +464,17 @@ type ArchivingListArchivedFilesResponse = { file_extension: string; file_path?: string; file_size: number; - file_type: string; + file_type: "MP4" | "M4A" | "CHAT" | "CC" | "CHAT_MESSAGE" | "TRANSCRIPT" | "SUB_GROUP_MEMBER_LOG" | "AIC_COVERSATION"; id: string; individual: boolean; participant_email?: string; participant_join_time: string; participant_leave_time: string; - recording_type: string; - status: string; + recording_type: "shared_screen_with_speaker_view" | "audio_only" | "chat_file" | "closed_caption" | "chat_message" | "audio_transcript" | "aic_conversation"; + status: "completed" | "processing" | "failed"; encryption_fingerprint: string; number_of_messages?: number; - storage_location?: string; + storage_location?: "US" | "AU" | "BR" | "CA" | "EU" | "IN" | "JP" | "SG" | "CH"; auto_delete?: boolean; }[]; complete_time: string; @@ -439,17 +483,23 @@ type ArchivingListArchivedFilesResponse = { host_id: string; id: number; is_breakout_room: boolean; - meeting_type: string; + meeting_type: "internal" | "external"; parent_meeting_id?: string; recording_count: number; start_time: string; timezone: string; topic: string; total_size: number; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 100; uuid: string; - status: string; + status: "completed" | "processing"; group_id?: string; + physical_files?: { + file_id?: string; + file_name?: string; + file_size?: number; + download_url?: string; + }[]; }[]; next_page_token?: string; page_size?: number; @@ -493,17 +543,17 @@ type ArchivingGetMeetingsArchivedFilesResponse = { file_extension: string; file_path?: string; file_size: number; - file_type: string; + file_type: "MP4" | "M4A" | "CHAT" | "CC" | "CHAT_MESSAGE" | "TRANSCRIPT" | "SUB_GROUP_MEMBER_LOG" | "AIC_COVERSATION"; id: string; individual: boolean; participant_email?: string; participant_join_time: string; participant_leave_time: string; - recording_type: string; - status: string; + recording_type: "shared_screen_with_speaker_view" | "audio_only" | "chat_file" | "closed_caption" | "chat_message" | "audio_transcript" | "aic_conversation"; + status: "completed" | "processing" | "failed"; encryption_fingerprint: string; number_of_messages?: number; - storage_location?: string; + storage_location?: "US" | "AU" | "BR" | "CA" | "EU" | "IN" | "JP" | "SG" | "CH"; auto_delete?: boolean; }[]; complete_time: string; @@ -512,17 +562,23 @@ type ArchivingGetMeetingsArchivedFilesResponse = { host_id: string; id: number; is_breakout_room: boolean; - meeting_type: string; + meeting_type: "internal" | "external"; parent_meeting_id?: string; recording_count: number; start_time: string; timezone: string; topic: string; total_size: number; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 100; uuid: string; - status: string; + status: "completed" | "processing"; group_id?: string; + physical_files?: { + file_id?: string; + file_name?: string; + file_size?: number; + download_url?: string; + }[]; }; type ArchivingDeleteMeetingsArchivedFilesPathParams = { meetingUUID: string; @@ -543,7 +599,7 @@ type CloudRecordingGetMeetingRecordingsResponse = ({ start_time?: string; topic?: string; total_size?: number; - type?: string; + type?: "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "99"; uuid?: string; recording_play_passcode?: string; auto_delete?: boolean; @@ -554,15 +610,15 @@ type CloudRecordingGetMeetingRecordingsResponse = ({ download_url?: string; file_path?: string; file_size?: number; - file_type?: string; - file_extension?: string; + file_type?: "MP4" | "M4A" | "CHAT" | "TRANSCRIPT" | "CSV" | "TB" | "CC" | "CHAT_MESSAGE" | "SUMMARY"; + file_extension?: "MP4" | "M4A" | "TXT" | "VTT" | "CSV" | "JSON" | "JPG"; id?: string; meeting_id?: string; play_url?: string; recording_end?: string; recording_start?: string; - recording_type?: string; - status?: string; + recording_type?: "shared_screen_with_speaker_view(CC)" | "shared_screen_with_speaker_view" | "shared_screen_with_gallery_view" | "active_speaker" | "gallery_view" | "shared_screen" | "audio_only" | "audio_transcript" | "chat_file" | "poll" | "host_video" | "closed_caption" | "timeline" | "thumbnail" | "audio_interpretation" | "summary" | "summary_next_steps" | "summary_smart_chapters" | "sign_interpretation" | "production_studio"; + status?: "completed"; }[]; }) & { download_access_token?: string; @@ -579,14 +635,14 @@ type CloudRecordingGetMeetingRecordingsResponse = ({ play_url?: string; recording_end?: string; recording_start?: string; - status?: string; + status?: "completed"; }[]; }; type CloudRecordingDeleteMeetingOrWebinarRecordingsPathParams = { meetingId: string; }; type CloudRecordingDeleteMeetingOrWebinarRecordingsQueryParams = { - action?: string; + action?: "trash" | "delete"; }; type CloudRecordingGetMeetingOrWebinarRecordingsAnalyticsDetailsPathParams = { meetingId: string; @@ -596,7 +652,7 @@ type CloudRecordingGetMeetingOrWebinarRecordingsAnalyticsDetailsQueryParams = { next_page_token?: string; from?: string; to?: string; - type?: string; + type?: "by_view" | "by_download"; }; type CloudRecordingGetMeetingOrWebinarRecordingsAnalyticsDetailsResponse = { from?: string; @@ -631,7 +687,7 @@ type CloudRecordingListRecordingRegistrantsPathParams = { meetingId: number; }; type CloudRecordingListRecordingRegistrantsQueryParams = { - status?: string; + status?: "pending" | "approved" | "denied"; page_size?: number; page_number?: number; next_page_token?: string; @@ -659,13 +715,13 @@ type CloudRecordingListRecordingRegistrantsResponse = { industry?: string; job_title?: string; last_name?: string; - no_of_employees?: string; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-250" | "251-500" | "501-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; org?: string; phone?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; state?: string; - status?: string; + status?: "approved" | "denied" | "pending"; zip?: string; })[]; }; @@ -686,13 +742,13 @@ type CloudRecordingCreateRecordingRegistrantRequestBody = { industry?: string; job_title?: string; last_name?: string; - no_of_employees?: string; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-250" | "251-500" | "501-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; org?: string; phone?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; state?: string; - status?: string; + status?: "approved" | "denied" | "pending"; zip?: string; }; type CloudRecordingCreateRecordingRegistrantResponse = { @@ -709,10 +765,10 @@ type CloudRecordingGetRegistrationQuestionsResponse = { answers?: string[]; required?: boolean; title?: string; - type?: string; + type?: "short" | "single" | "multiple"; }[]; questions?: { - field_name?: string; + field_name?: "last_name" | "address" | "city" | "country" | "zip" | "state" | "phone" | "industry" | "org" | "job_title" | "purchasing_time_frame" | "role_in_purchase_process" | "no_of_employees" | "comments"; required?: boolean; }[]; }; @@ -724,10 +780,10 @@ type CloudRecordingUpdateRegistrationQuestionsRequestBody = { answers?: string[]; required?: boolean; title?: string; - type?: string; + type?: "short" | "single" | "multiple"; }[]; questions?: { - field_name?: string; + field_name?: "last_name" | "address" | "city" | "country" | "zip" | "state" | "phone" | "industry" | "org" | "job_title" | "purchasing_time_frame" | "role_in_purchase_process" | "no_of_employees" | "comments"; required?: boolean; }[]; }; @@ -735,7 +791,7 @@ type CloudRecordingUpdateRegistrantsStatusPathParams = { meetingId: number; }; type CloudRecordingUpdateRegistrantsStatusRequestBody = { - action: string; + action: "approve" | "deny"; registrants?: { id?: string; }[]; @@ -744,14 +800,15 @@ type CloudRecordingGetMeetingRecordingSettingsPathParams = { meetingId: string; }; type CloudRecordingGetMeetingRecordingSettingsResponse = { - approval_type?: number; + approval_type?: 0 | 1 | 2; authentication_domains?: string; authentication_option?: string; + authentication_name?: string; on_demand?: boolean; password?: string; recording_authentication?: boolean; send_email_to_host?: boolean; - share_recording?: string; + share_recording?: "publicly" | "internally" | "none"; show_social_share_buttons?: boolean; topic?: string; viewer_download?: boolean; @@ -762,14 +819,14 @@ type CloudRecordingUpdateMeetingRecordingSettingsPathParams = { meetingId: string; }; type CloudRecordingUpdateMeetingRecordingSettingsRequestBody = { - approval_type?: number; + approval_type?: 0 | 1 | 2; authentication_domains?: string; authentication_option?: string; on_demand?: boolean; password?: string; recording_authentication?: boolean; send_email_to_host?: boolean; - share_recording?: string; + share_recording?: "publicly" | "internally" | "none"; show_social_share_buttons?: boolean; topic?: string; viewer_download?: boolean; @@ -780,20 +837,38 @@ type CloudRecordingDeleteRecordingFileForMeetingOrWebinarPathParams = { recordingId: string; }; type CloudRecordingDeleteRecordingFileForMeetingOrWebinarQueryParams = { - action?: string; + action?: "trash" | "delete"; }; type CloudRecordingRecoverSingleRecordingPathParams = { meetingId: string; recordingId: string; }; type CloudRecordingRecoverSingleRecordingRequestBody = { - action?: string; + action?: "recover"; +}; +type CloudRecordingGetMeetingTranscriptPathParams = { + meetingId: string; +}; +type CloudRecordingGetMeetingTranscriptResponse = { + meeting_id?: string; + account_id?: string; + meeting_topic?: string; + host_id?: string; + transcript_created_time?: string; + can_download?: boolean; + auto_delete?: boolean; + auto_delete_date?: string; + download_url?: string; + download_restriction_reason?: "DELETED_OR_TRASHED" | "UNSUPPORTED" | "NO_TRANSCRIPT_DATA" | "NOT_READY"; +}; +type CloudRecordingDeleteMeetingOrWebinarTranscriptPathParams = { + meetingId: string; }; type CloudRecordingRecoverMeetingRecordingsPathParams = { meetingUUID: string; }; type CloudRecordingRecoverMeetingRecordingsRequestBody = { - action?: string; + action?: "recover"; }; type CloudRecordingListAllRecordingsPathParams = { userId: string; @@ -826,7 +901,7 @@ type CloudRecordingListAllRecordingsResponse = { start_time?: string; topic?: string; total_size?: number; - type?: string; + type?: "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "99"; uuid?: string; recording_play_passcode?: string; auto_delete?: boolean; @@ -837,26 +912,26 @@ type CloudRecordingListAllRecordingsResponse = { download_url?: string; file_path?: string; file_size?: number; - file_type?: string; - file_extension?: string; + file_type?: "MP4" | "M4A" | "CHAT" | "TRANSCRIPT" | "CSV" | "TB" | "CC" | "CHAT_MESSAGE" | "SUMMARY"; + file_extension?: "MP4" | "M4A" | "TXT" | "VTT" | "CSV" | "JSON" | "JPG"; id?: string; meeting_id?: string; play_url?: string; recording_end?: string; recording_start?: string; - recording_type?: string; - status?: string; + recording_type?: "shared_screen_with_speaker_view(CC)" | "shared_screen_with_speaker_view" | "shared_screen_with_gallery_view" | "active_speaker" | "gallery_view" | "shared_screen" | "audio_only" | "audio_transcript" | "chat_file" | "poll" | "host_video" | "closed_caption" | "timeline" | "thumbnail" | "audio_interpretation" | "summary" | "summary_next_steps" | "summary_smart_chapters" | "sign_interpretation" | "production_studio"; + status?: "completed"; }[]; })[]; }; type DevicesListDevicesQueryParams = { search_text?: string; - platform_os?: string; + platform_os?: "win" | "mac" | "ipad" | "iphone" | "android" | "linux"; is_enrolled_in_zdm?: boolean; - device_type?: number; + device_type?: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6; device_vendor?: string; device_model?: string; - device_status?: number; + device_status?: -1 | 0 | 1; page_size?: number; next_page_token?: string; }; @@ -877,9 +952,9 @@ type DevicesListDevicesResponse = { connected_to_zdm?: boolean; room_id?: string; room_name?: string; - device_type?: number; + device_type?: 0 | 1 | 2 | 3 | 4 | 5 | 6; skd_version?: string; - device_status?: number; + device_status?: -1 | 0 | 1; last_online?: string; user_email?: string; }[]; @@ -892,7 +967,7 @@ type DevicesAddNewDeviceRequestBody = { model: string; room_id?: string; user_email?: string; - device_type: number; + device_type: 0 | 1 | 5; tag?: string; zdm_group_id?: string; extension_number?: string; @@ -915,7 +990,29 @@ type DevicesAssignDeviceToUserOrCommonareaRequestBody = { mac_address: string; vendor: string; }; -type DevicesUpgradeZpaOsAppRequestBody = { +type DevicesGetZoomPhoneApplianceSettingsByUserIDQueryParams = { + user_id?: string; +}; +type DevicesGetZoomPhoneApplianceSettingsByUserIDResponse = { + language?: string; + timezone?: string; + device_infos?: { + device_id?: string; + device_type?: string; + vendor?: string; + model?: string; + status?: "online" | "offline"; + policy?: { + hot_desking?: { + status?: "online" | "offline"; + }; + call_control?: { + status?: "unsupported" | "on" | "off"; + }; + }; + }[]; +}; +type DevicesUpgradeZPAFirmwareOrAppRequestBody = { zdm_group_id: string; data: { firmware_versions?: { @@ -923,10 +1020,10 @@ type DevicesUpgradeZpaOsAppRequestBody = { version?: string; model?: string; }[]; - upgrade_type: string; + upgrade_type: "UPGRADE_FIRMWARE"; } | { app_version?: string; - upgrade_type: string; + upgrade_type: "UPGRADE_APP"; }; }; type DevicesDeleteZPADeviceByVendorAndMacAddressPathParams = { @@ -962,9 +1059,9 @@ type DevicesGetDeviceDetailResponse = { connected_to_zdm?: boolean; room_id?: string; room_name?: string; - device_type?: number; + device_type?: 0 | 1 | 2 | 3 | 4 | 5 | 6; sdk_version?: string; - device_status?: number; + device_status?: -1 | 0 | 1; last_online?: string; user_email?: string; }; @@ -978,14 +1075,20 @@ type DevicesChangeDeviceRequestBody = { device_name: string; tag?: string; room_id?: string; - device_type?: number; + device_type?: 0 | 1 | 3; +}; +type DevicesAssignDeviceToGroupPathParams = { + deviceId: string; +}; +type DevicesAssignDeviceToGroupQueryParams = { + group_id: string; }; type DevicesChangeDeviceAssociationPathParams = { deviceId: string; }; type DevicesChangeDeviceAssociationRequestBody = { room_id?: string; - app_type?: string; + app_type?: "ZR" | "ZRC" | "ZRP" | "ZRW"; }; type H323DevicesListHSIPDevicesQueryParams = { page_size?: number; @@ -1002,25 +1105,25 @@ type H323DevicesListHSIPDevicesResponse = { devices?: ({ id?: string; } & { - encryption: string; + encryption: "auto" | "yes" | "no"; ip: string; name: string; - protocol: string; + protocol: "H.323" | "SIP"; })[]; }; type H323DevicesCreateHSIPDeviceRequestBody = { - encryption: string; + encryption: "auto" | "yes" | "no"; ip: string; name: string; - protocol: string; + protocol: "H.323" | "SIP"; }; type H323DevicesCreateHSIPDeviceResponse = { id?: string; } & { - encryption: string; + encryption: "auto" | "yes" | "no"; ip: string; name: string; - protocol: string; + protocol: "H.323" | "SIP"; }; type H323DevicesDeleteHSIPDevicePathParams = { deviceId: string; @@ -1029,10 +1132,10 @@ type H323DevicesUpdateHSIPDevicePathParams = { deviceId: string; }; type H323DevicesUpdateHSIPDeviceRequestBody = { - encryption: string; + encryption: "auto" | "yes" | "no"; ip: string; name: string; - protocol: string; + protocol: "H.323" | "SIP"; }; type MeetingsDeleteLiveMeetingMessagePathParams = { meetingId: number; @@ -1052,7 +1155,7 @@ type MeetingsUseInMeetingControlsPathParams = { meetingId: string; }; type MeetingsUseInMeetingControlsRequestBody = { - method?: string; + method?: "recording.start" | "recording.stop" | "recording.pause" | "recording.resume" | "participant.invite" | "participant.invite.callout" | "participant.invite.room_system_callout" | "waiting_room.update"; params?: { contacts?: { email?: string; @@ -1079,15 +1182,27 @@ type MeetingsUseInMeetingControlsRequestBody = { value?: string; }[]; }; + waiting_room_title?: string; + waiting_room_description?: string; }; }; -type MeetingsListMeetingSummariesOfAccountQueryParams = { +type MeetingsUpdateParticipantRealTimeMediaStreamsRTMSAppStatusPathParams = { + meetingId: number; +}; +type MeetingsUpdateParticipantRealTimeMediaStreamsRTMSAppStatusRequestBody = { + action?: "start" | "stop" | "pause" | "resume"; + settings?: { + participant_user_id?: string; + client_id: string; + }; +}; +type MeetingsListAccountsMeetingOrWebinarSummariesQueryParams = { page_size?: number; next_page_token?: string; from?: string; to?: string; }; -type MeetingsListMeetingSummariesOfAccountResponse = { +type MeetingsListAccountsMeetingOrWebinarSummariesResponse = { page_size?: number; next_page_token?: string; from?: string; @@ -1131,7 +1246,7 @@ type MeetingsGetMeetingResponse = { duration?: number; occurrence_id?: string; start_time?: string; - status?: string; + status?: "available" | "deleted"; }[]; password?: string; pmi?: string; @@ -1140,25 +1255,27 @@ type MeetingsGetMeetingResponse = { end_date_time?: string; end_times?: number; monthly_day?: number; - monthly_week?: number; - monthly_week_day?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; repeat_interval?: number; - type: number; - weekly_days?: string; + type: 1 | 2 | 3; + weekly_days?: "1" | "2" | "3" | "4" | "5" | "6" | "7"; }; settings?: { allow_multiple_devices?: boolean; alternative_hosts?: string; alternative_hosts_email_notification?: boolean; alternative_host_update_polls?: boolean; - approval_type?: number; + alternative_host_manage_meeting_summary?: boolean; + alternative_host_manage_cloud_recording?: boolean; + approval_type?: 0 | 1 | 2; approved_or_denied_countries_or_regions?: { approved_list?: string[]; denied_list?: string[]; enable?: boolean; - method?: string; + method?: "approve" | "deny"; }; - audio?: string; + audio?: "both" | "telephony" | "voip" | "thirdParty"; audio_conference_info?: string; authentication_domains?: string; authentication_exception?: { @@ -1168,7 +1285,14 @@ type MeetingsGetMeetingResponse = { }[]; authentication_name?: string; authentication_option?: string; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; + auto_add_recording_to_video_management?: { + enable: boolean; + channels?: { + channel_id: string; + name?: string; + }[]; + }; breakout_room?: { enable?: boolean; rooms?: { @@ -1176,7 +1300,7 @@ type MeetingsGetMeetingResponse = { participants?: string[]; }[]; }; - calendar_type?: number; + calendar_type?: 1 | 2; close_registration?: boolean; cn_meeting?: boolean; contact_email?: string; @@ -1186,7 +1310,7 @@ type MeetingsGetMeetingResponse = { value?: string; }[]; email_notification?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; enforce_login?: boolean; enforce_login_domains?: string; focus_mode?: boolean; @@ -1196,17 +1320,26 @@ type MeetingsGetMeetingResponse = { country?: string; country_name?: string; number?: string; - type?: string; + type?: "toll" | "tollfree"; }[]; host_video?: boolean; in_meeting?: boolean; - jbh_time?: number; + jbh_time?: 0 | 5 | 10 | 15; join_before_host?: boolean; + question_and_answer?: { + enable?: boolean; + allow_submit_questions?: boolean; + allow_anonymous_questions?: boolean; + question_visibility?: "answered" | "all"; + attendees_can_comment?: boolean; + attendees_can_upvote?: boolean; + }; language_interpretation?: { enable?: boolean; interpreters?: { email?: string; languages?: string; + interpreter_languages?: string; }[]; }; sign_language_interpretation?: { @@ -1222,10 +1355,14 @@ type MeetingsGetMeetingResponse = { private_meeting?: boolean; registrants_confirmation_email?: boolean; registrants_email_notification?: boolean; - registration_type?: number; + registration_type?: 1 | 2 | 3; show_share_button?: boolean; use_pmi?: boolean; waiting_room?: boolean; + waiting_room_options?: { + mode: "follow_setting" | "custom"; + who_goes_to_waiting_room?: "everyone" | "users_not_in_account" | "users_not_in_account_or_whitelisted_domains" | "users_not_on_invite"; + }; watermark?: boolean; host_save_video_order?: boolean; internal_meeting?: boolean; @@ -1242,17 +1379,23 @@ type MeetingsGetMeetingResponse = { participant_focused_meeting?: boolean; push_change_to_calendar?: boolean; resources?: { - resource_type?: string; + resource_type?: "whiteboard"; resource_id?: string; - permission_level?: string; + permission_level?: "editor" | "commenter" | "viewer"; }[]; auto_start_meeting_summary?: boolean; + who_will_receive_summary?: 1 | 2 | 3 | 4; auto_start_ai_companion_questions?: boolean; + who_can_ask_questions?: 1 | 2 | 3 | 4 | 5; + summary_template_id?: string; device_testing?: boolean; + allow_host_control_participant_mute_state?: boolean; + disable_participant_video?: boolean; + email_in_attendee_report?: boolean; }; start_time?: string; start_url?: string; - status?: string; + status?: "waiting" | "started"; timezone?: string; topic?: string; tracking_fields?: { @@ -1260,8 +1403,9 @@ type MeetingsGetMeetingResponse = { value?: string; visible?: boolean; }[]; - type?: number; + type?: 1 | 2 | 3 | 4 | 8 | 10; dynamic_host_key?: string; + creation_source?: "other" | "open_api" | "web_portal"; }; type MeetingsDeleteMeetingPathParams = { meetingId: number; @@ -1287,25 +1431,27 @@ type MeetingsUpdateMeetingRequestBody = { end_date_time?: string; end_times?: number; monthly_day?: number; - monthly_week?: number; - monthly_week_day?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; repeat_interval?: number; - type: number; - weekly_days?: string; + type: 1 | 2 | 3; + weekly_days?: "1" | "2" | "3" | "4" | "5" | "6" | "7"; }; settings?: { allow_multiple_devices?: boolean; alternative_hosts?: string; alternative_hosts_email_notification?: boolean; alternative_host_update_polls?: boolean; - approval_type?: number; + alternative_host_manage_meeting_summary?: boolean; + alternative_host_manage_cloud_recording?: boolean; + approval_type?: 0 | 1 | 2; approved_or_denied_countries_or_regions?: { approved_list?: string[]; denied_list?: string[]; enable?: boolean; - method?: string; + method?: "approve" | "deny"; }; - audio?: string; + audio?: "both" | "telephony" | "voip" | "thirdParty"; audio_conference_info?: string; authentication_domains?: string; authentication_exception?: { @@ -1315,7 +1461,14 @@ type MeetingsUpdateMeetingRequestBody = { }[]; authentication_name?: string; authentication_option?: string; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; + auto_add_recording_to_video_management?: { + enable: boolean; + channels?: { + channel_id: string; + name?: string; + }[]; + }; breakout_room?: { enable?: boolean; rooms?: { @@ -1323,7 +1476,7 @@ type MeetingsUpdateMeetingRequestBody = { participants?: string[]; }[]; }; - calendar_type?: number; + calendar_type?: 1 | 2; close_registration?: boolean; cn_meeting?: boolean; contact_email?: string; @@ -1333,7 +1486,7 @@ type MeetingsUpdateMeetingRequestBody = { value?: string; }[]; email_notification?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; enforce_login?: boolean; enforce_login_domains?: string; focus_mode?: boolean; @@ -1343,17 +1496,26 @@ type MeetingsUpdateMeetingRequestBody = { country?: string; country_name?: string; number?: string; - type?: string; + type?: "toll" | "tollfree"; }[]; host_video?: boolean; in_meeting?: boolean; - jbh_time?: number; + jbh_time?: 0 | 5 | 10 | 15; join_before_host?: boolean; + question_and_answer?: { + enable?: boolean; + allow_submit_questions?: boolean; + allow_anonymous_questions?: boolean; + question_visibility?: "answered" | "all"; + attendees_can_comment?: boolean; + attendees_can_upvote?: boolean; + }; language_interpretation?: { enable?: boolean; interpreters?: { email?: string; languages?: string; + interpreter_languages?: string; }[]; }; sign_language_interpretation?: { @@ -1372,10 +1534,14 @@ type MeetingsUpdateMeetingRequestBody = { private_meeting?: boolean; registrants_confirmation_email?: boolean; registrants_email_notification?: boolean; - registration_type?: number; + registration_type?: 1 | 2 | 3; show_share_button?: boolean; use_pmi?: boolean; waiting_room?: boolean; + waiting_room_options?: { + mode: "follow_setting" | "custom"; + who_goes_to_waiting_room?: "everyone" | "users_not_in_account" | "users_not_in_account_or_whitelisted_domains" | "users_not_on_invite"; + }; watermark?: boolean; host_save_video_order?: boolean; internal_meeting?: boolean; @@ -1385,14 +1551,21 @@ type MeetingsUpdateMeetingRequestBody = { auto_add_meeting_participants?: boolean; }; participant_focused_meeting?: boolean; + push_change_to_calendar?: boolean; resources?: { - resource_type?: string; + resource_type?: "whiteboard"; resource_id?: string; - permission_level?: string; + permission_level?: "editor" | "commenter" | "viewer"; }[]; auto_start_meeting_summary?: boolean; + who_will_receive_summary?: 1 | 2 | 3 | 4; auto_start_ai_companion_questions?: boolean; + who_can_ask_questions?: 1 | 2 | 3 | 4 | 5; + summary_template_id?: string; device_testing?: boolean; + allow_host_control_participant_mute_state?: boolean; + disable_participant_video?: boolean; + email_in_attendee_report?: boolean; }; start_time?: string; template_id?: string; @@ -1402,7 +1575,7 @@ type MeetingsUpdateMeetingRequestBody = { field?: string; value?: string; }[]; - type?: number; + type?: 1 | 2 | 3 | 8 | 10; }; type MeetingsPerformBatchPollCreationPathParams = { meetingId: string; @@ -1410,7 +1583,7 @@ type MeetingsPerformBatchPollCreationPathParams = { type MeetingsPerformBatchPollCreationRequestBody = { polls?: { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -1428,7 +1601,7 @@ type MeetingsPerformBatchPollCreationRequestBody = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; }[]; @@ -1437,7 +1610,7 @@ type MeetingsPerformBatchPollCreationResponse = { polls?: { anonymous?: boolean; id?: string; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -1455,9 +1628,9 @@ type MeetingsPerformBatchPollCreationResponse = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; - status?: string; + status?: "notstart" | "started" | "ended" | "sharing"; title?: string; }[]; }; @@ -1494,6 +1667,8 @@ type MeetingsCreateMeetingsInviteLinksPathParams = { type MeetingsCreateMeetingsInviteLinksRequestBody = { attendees?: { name: string; + disable_video?: boolean; + disable_audio?: boolean; }[]; ttl?: number; }; @@ -1507,14 +1682,14 @@ type MeetingsGetMeetingsJoinTokenForLiveStreamingPathParams = { meetingId: number; }; type MeetingsGetMeetingsJoinTokenForLiveStreamingResponse = { - expire_in?: number; + expire_in?: 120; token?: string; }; type MeetingsGetMeetingsArchiveTokenForLocalArchivingPathParams = { meetingId: number; }; type MeetingsGetMeetingsArchiveTokenForLocalArchivingResponse = { - expire_in?: number; + expire_in?: 120; token?: string; }; type MeetingsGetMeetingsJoinTokenForLocalRecordingPathParams = { @@ -1524,7 +1699,7 @@ type MeetingsGetMeetingsJoinTokenForLocalRecordingQueryParams = { bypass_waiting_room?: boolean; }; type MeetingsGetMeetingsJoinTokenForLocalRecordingResponse = { - expire_in?: number; + expire_in?: 120; token?: string; }; type MeetingsGetLivestreamDetailsPathParams = { @@ -1549,18 +1724,18 @@ type MeetingsUpdateLivestreamStatusPathParams = { meetingId: number; }; type MeetingsUpdateLivestreamStatusRequestBody = { - action?: string; + action?: "start" | "stop" | "mode"; settings?: { active_speaker_name?: boolean; display_name?: string; - layout?: string; - close_caption?: string; + layout?: "follow_host" | "gallery_view" | "speaker_view"; + close_caption?: "burnt-in" | "embedded" | "off"; }; }; -type MeetingsGetMeetingSummaryPathParams = { +type MeetingsGetMeetingOrWebinarSummaryPathParams = { meetingId: string; }; -type MeetingsGetMeetingSummaryResponse = { +type MeetingsGetMeetingOrWebinarSummaryResponse = { meeting_host_id?: string; meeting_host_email?: string; meeting_uuid?: string; @@ -1572,6 +1747,8 @@ type MeetingsGetMeetingSummaryResponse = { summary_end_time?: string; summary_created_time?: string; summary_last_modified_time?: string; + summary_last_modified_user_id?: string; + summary_last_modified_user_email?: string; summary_title?: string; summary_overview?: string; summary_details?: { @@ -1580,9 +1757,15 @@ type MeetingsGetMeetingSummaryResponse = { }[]; next_steps?: string[]; edited_summary?: { + summary_overview?: string; summary_details?: string; next_steps?: string[]; }; + summary_content?: string; + summary_doc_url?: string; +}; +type MeetingsDeleteMeetingOrWebinarSummaryPathParams = { + meetingId: string; }; type MeetingsAddMeetingAppPathParams = { meetingId: number; @@ -1604,10 +1787,10 @@ type MeetingsListMeetingPollsQueryParams = { type MeetingsListMeetingPollsResponse = { polls?: ({ id?: string; - status?: string; + status?: "notstart" | "started" | "ended" | "sharing" | "deactivated"; } & { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -1625,7 +1808,7 @@ type MeetingsListMeetingPollsResponse = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; })[]; @@ -1636,7 +1819,7 @@ type MeetingsCreateMeetingPollPathParams = { }; type MeetingsCreateMeetingPollRequestBody = { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -1654,16 +1837,16 @@ type MeetingsCreateMeetingPollRequestBody = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; }; type MeetingsCreateMeetingPollResponse = { id?: string; - status?: string; + status?: "notstart" | "started" | "ended" | "sharing"; } & { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -1681,7 +1864,7 @@ type MeetingsCreateMeetingPollResponse = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; }; @@ -1691,10 +1874,10 @@ type MeetingsGetMeetingPollPathParams = { }; type MeetingsGetMeetingPollResponse = { id?: string; - status?: string; + status?: "notstart" | "started" | "ended" | "sharing" | "deactivated"; } & { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -1712,7 +1895,7 @@ type MeetingsGetMeetingPollResponse = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; }; @@ -1722,7 +1905,7 @@ type MeetingsUpdateMeetingPollPathParams = { }; type MeetingsUpdateMeetingPollRequestBody = { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -1740,7 +1923,7 @@ type MeetingsUpdateMeetingPollRequestBody = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; }; @@ -1753,7 +1936,7 @@ type MeetingsListMeetingRegistrantsPathParams = { }; type MeetingsListMeetingRegistrantsQueryParams = { occurrence_id?: string; - status?: string; + status?: "pending" | "approved" | "denied"; page_size?: number; page_number?: number; next_page_token?: string; @@ -1781,13 +1964,13 @@ type MeetingsListMeetingRegistrantsResponse = { industry?: string; job_title?: string; last_name?: string; - no_of_employees?: string; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-250" | "251-500" | "501-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; org?: string; phone?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; state?: string; - status?: string; + status?: "approved" | "denied" | "pending"; zip?: string; } & { create_time?: string; @@ -1819,12 +2002,12 @@ type MeetingsAddMeetingRegistrantRequestBody = { }[]; industry?: string; job_title?: string; - no_of_employees?: string; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-500" | "500-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; org?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; } & { - language?: string; + language?: "en-US" | "de-DE" | "es-ES" | "fr-FR" | "jp-JP" | "pt-PT" | "ru-RU" | "zh-CN" | "zh-TW" | "ko-KO" | "it-IT" | "vi-VN" | "pl-PL" | "Tr-TR"; } & { auto_approve?: boolean; }; @@ -1850,10 +2033,10 @@ type MeetingsListRegistrationQuestionsResponse = { answers?: string[]; required?: boolean; title?: string; - type?: string; + type?: "short" | "single"; }[]; questions?: { - field_name?: string; + field_name?: "last_name" | "address" | "city" | "country" | "zip" | "state" | "phone" | "industry" | "org" | "job_title" | "purchasing_time_frame" | "role_in_purchase_process" | "no_of_employees" | "comments"; required?: boolean; }[]; }; @@ -1865,10 +2048,10 @@ type MeetingsUpdateRegistrationQuestionsRequestBody = { answers?: string[]; required?: boolean; title?: string; - type?: string; + type?: "short" | "single"; }[]; questions?: { - field_name?: string; + field_name?: "last_name" | "address" | "city" | "country" | "zip" | "state" | "phone" | "industry" | "org" | "job_title" | "purchasing_time_frame" | "role_in_purchase_process" | "no_of_employees" | "comments"; required?: boolean; }[]; }; @@ -1879,7 +2062,7 @@ type MeetingsUpdateRegistrantsStatusQueryParams = { occurrence_id?: string; }; type MeetingsUpdateRegistrantsStatusRequestBody = { - action: string; + action: "approve" | "cancel" | "deny"; registrants?: { email?: string; id?: string; @@ -1905,18 +2088,18 @@ type MeetingsGetMeetingRegistrantResponse = { industry?: string; job_title?: string; last_name?: string; - no_of_employees?: string; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-250" | "251-500" | "501-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; org?: string; phone?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; state?: string; - status?: string; + status?: "approved" | "denied" | "pending"; zip?: string; } & { create_time?: string; join_url?: string; - status?: string; + status?: "approved" | "pending" | "denied"; participant_pin_code?: number; }; type MeetingsDeleteMeetingRegistrantPathParams = { @@ -1942,7 +2125,7 @@ type MeetingsUpdateMeetingStatusPathParams = { meetingId: number; }; type MeetingsUpdateMeetingStatusRequestBody = { - action?: string; + action?: "end" | "recover"; }; type MeetingsGetMeetingSurveyPathParams = { meetingId: number; @@ -1956,7 +2139,7 @@ type MeetingsGetMeetingSurveyResponse = { feedback?: string; questions?: { name?: string; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; answer_required?: boolean; show_as_dropdown?: boolean; answers?: string[]; @@ -1989,7 +2172,7 @@ type MeetingsUpdateMeetingSurveyRequestBody = { feedback?: string; questions?: { name?: string; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; answer_required?: boolean; show_as_dropdown?: boolean; answers?: string[]; @@ -2011,13 +2194,13 @@ type MeetingsGetMeetingsTokenPathParams = { meetingId: number; }; type MeetingsGetMeetingsTokenQueryParams = { - type?: string; + type?: "closed_caption_token"; }; type MeetingsGetMeetingsTokenResponse = { token?: string; }; type MeetingsGetPastMeetingDetailsPathParams = { - meetingId: number | string; + meetingId: string; }; type MeetingsGetPastMeetingDetailsResponse = { id?: number; @@ -2031,7 +2214,7 @@ type MeetingsGetPastMeetingDetailsResponse = { source?: string; topic?: string; total_minutes?: number; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 7 | 8; user_email?: string; user_name?: string; }; @@ -2067,7 +2250,7 @@ type MeetingsGetPastMeetingParticipantsResponse = { leave_time?: string; duration?: number; failover?: boolean; - status?: string; + status?: "in_meeting" | "in_waiting_room"; internal_user?: boolean; }[]; }; @@ -2133,7 +2316,7 @@ type MeetingsListMeetingsPathParams = { userId: string; }; type MeetingsListMeetingsQueryParams = { - type?: string; + type?: "scheduled" | "live" | "upcoming" | "upcoming_meetings" | "previous_meetings"; page_size?: number; next_page_token?: string; page_number?: number; @@ -2159,7 +2342,7 @@ type MeetingsListMeetingsResponse = { start_time?: string; timezone?: string; topic?: string; - type?: number; + type?: 1 | 2 | 3 | 8; uuid?: string; }[]; }; @@ -2176,11 +2359,11 @@ type MeetingsCreateMeetingRequestBody = { end_date_time?: string; end_times?: number; monthly_day?: number; - monthly_week?: number; - monthly_week_day?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; repeat_interval?: number; - type: number; - weekly_days?: string; + type: 1 | 2 | 3; + weekly_days?: "1" | "2" | "3" | "4" | "5" | "6" | "7"; }; schedule_for?: string; settings?: { @@ -2188,14 +2371,14 @@ type MeetingsCreateMeetingRequestBody = { allow_multiple_devices?: boolean; alternative_hosts?: string; alternative_hosts_email_notification?: boolean; - approval_type?: number; + approval_type?: 0 | 1 | 2; approved_or_denied_countries_or_regions?: { approved_list?: string[]; denied_list?: string[]; enable?: boolean; - method?: string; + method?: "approve" | "deny"; }; - audio?: string; + audio?: "both" | "telephony" | "voip" | "thirdParty"; audio_conference_info?: string; authentication_domains?: string; authentication_exception?: { @@ -2203,7 +2386,14 @@ type MeetingsCreateMeetingRequestBody = { name?: string; }[]; authentication_option?: string; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; + auto_add_recording_to_video_management?: { + enable: boolean; + channels?: { + channel_id: string; + name?: string; + }[]; + }; breakout_room?: { enable?: boolean; rooms?: { @@ -2211,24 +2401,33 @@ type MeetingsCreateMeetingRequestBody = { participants?: string[]; }[]; }; - calendar_type?: number; + calendar_type?: 1 | 2; close_registration?: boolean; cn_meeting?: boolean; contact_email?: string; contact_name?: string; email_notification?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; focus_mode?: boolean; global_dial_in_countries?: string[]; host_video?: boolean; in_meeting?: boolean; - jbh_time?: number; + jbh_time?: 0 | 5 | 10 | 15; join_before_host?: boolean; + question_and_answer?: { + enable?: boolean; + allow_submit_questions?: boolean; + allow_anonymous_questions?: boolean; + question_visibility?: "answered" | "all"; + attendees_can_comment?: boolean; + attendees_can_upvote?: boolean; + }; language_interpretation?: { enable?: boolean; interpreters?: { email?: string; languages?: string; + interpreter_languages?: string; }[]; }; sign_language_interpretation?: { @@ -2247,13 +2446,19 @@ type MeetingsCreateMeetingRequestBody = { private_meeting?: boolean; registrants_confirmation_email?: boolean; registrants_email_notification?: boolean; - registration_type?: number; + registration_type?: 1 | 2 | 3; show_share_button?: boolean; use_pmi?: boolean; waiting_room?: boolean; + waiting_room_options?: { + mode: "follow_setting" | "custom"; + who_goes_to_waiting_room?: "everyone" | "users_not_in_account" | "users_not_in_account_or_whitelisted_domains" | "users_not_on_invite"; + }; watermark?: boolean; host_save_video_order?: boolean; alternative_host_update_polls?: boolean; + alternative_host_manage_meeting_summary?: boolean; + alternative_host_manage_cloud_recording?: boolean; internal_meeting?: boolean; continuous_meeting_chat?: { enable?: boolean; @@ -2263,13 +2468,19 @@ type MeetingsCreateMeetingRequestBody = { participant_focused_meeting?: boolean; push_change_to_calendar?: boolean; resources?: { - resource_type?: string; + resource_type?: "whiteboard"; resource_id?: string; - permission_level?: string; + permission_level?: "editor" | "commenter" | "viewer"; }[]; auto_start_meeting_summary?: boolean; + who_will_receive_summary?: 1 | 2 | 3 | 4; auto_start_ai_companion_questions?: boolean; + who_can_ask_questions?: 1 | 2 | 3 | 4 | 5; + summary_template_id?: string; device_testing?: boolean; + allow_host_control_participant_mute_state?: boolean; + disable_participant_video?: boolean; + email_in_attendee_report?: boolean; }; start_time?: string; template_id?: string; @@ -2279,7 +2490,7 @@ type MeetingsCreateMeetingRequestBody = { field: string; value?: string; }[]; - type?: number; + type?: 1 | 2 | 3 | 8 | 10; }; type MeetingsCreateMeetingResponse = { assistant_id?: string; @@ -2298,7 +2509,7 @@ type MeetingsCreateMeetingResponse = { duration?: number; occurrence_id?: string; start_time?: string; - status?: string; + status?: "available" | "deleted"; }[]; password?: string; pmi?: string; @@ -2307,25 +2518,27 @@ type MeetingsCreateMeetingResponse = { end_date_time?: string; end_times?: number; monthly_day?: number; - monthly_week?: number; - monthly_week_day?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; repeat_interval?: number; - type: number; - weekly_days?: string; + type: 1 | 2 | 3; + weekly_days?: "1" | "2" | "3" | "4" | "5" | "6" | "7"; }; settings?: { allow_multiple_devices?: boolean; alternative_hosts?: string; alternative_hosts_email_notification?: boolean; alternative_host_update_polls?: boolean; - approval_type?: number; + alternative_host_manage_meeting_summary?: boolean; + alternative_host_manage_cloud_recording?: boolean; + approval_type?: 0 | 1 | 2; approved_or_denied_countries_or_regions?: { approved_list?: string[]; denied_list?: string[]; enable?: boolean; - method?: string; + method?: "approve" | "deny"; }; - audio?: string; + audio?: "both" | "telephony" | "voip" | "thirdParty"; audio_conference_info?: string; authentication_domains?: string; authentication_exception?: { @@ -2335,7 +2548,14 @@ type MeetingsCreateMeetingResponse = { }[]; authentication_name?: string; authentication_option?: string; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; + auto_add_recording_to_video_management?: { + enable: boolean; + channels?: { + channel_id: string; + name?: string; + }[]; + }; breakout_room?: { enable?: boolean; rooms?: { @@ -2343,7 +2563,7 @@ type MeetingsCreateMeetingResponse = { participants?: string[]; }[]; }; - calendar_type?: number; + calendar_type?: 1 | 2; close_registration?: boolean; cn_meeting?: boolean; contact_email?: string; @@ -2353,7 +2573,7 @@ type MeetingsCreateMeetingResponse = { value?: string; }[]; email_notification?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; enforce_login?: boolean; enforce_login_domains?: string; focus_mode?: boolean; @@ -2363,17 +2583,26 @@ type MeetingsCreateMeetingResponse = { country?: string; country_name?: string; number?: string; - type?: string; + type?: "toll" | "tollfree"; }[]; host_video?: boolean; in_meeting?: boolean; - jbh_time?: number; + jbh_time?: 0 | 5 | 10 | 15; join_before_host?: boolean; + question_and_answer?: { + enable?: boolean; + allow_submit_questions?: boolean; + allow_anonymous_questions?: boolean; + question_visibility?: "answered" | "all"; + attendees_can_comment?: boolean; + attendees_can_upvote?: boolean; + }; language_interpretation?: { enable?: boolean; interpreters?: { email?: string; languages?: string; + interpreter_languages?: string; }[]; }; sign_language_interpretation?: { @@ -2389,10 +2618,14 @@ type MeetingsCreateMeetingResponse = { private_meeting?: boolean; registrants_confirmation_email?: boolean; registrants_email_notification?: boolean; - registration_type?: number; + registration_type?: 1 | 2 | 3; show_share_button?: boolean; use_pmi?: boolean; waiting_room?: boolean; + waiting_room_options?: { + mode: "follow_setting" | "custom"; + who_goes_to_waiting_room?: "everyone" | "users_not_in_account" | "users_not_in_account_or_whitelisted_domains" | "users_not_on_invite"; + }; watermark?: boolean; host_save_video_order?: boolean; internal_meeting?: boolean; @@ -2408,13 +2641,19 @@ type MeetingsCreateMeetingResponse = { participant_focused_meeting?: boolean; push_change_to_calendar?: boolean; resources?: { - resource_type?: string; + resource_type?: "whiteboard"; resource_id?: string; - permission_level?: string; + permission_level?: "editor" | "commenter" | "viewer"; }[]; auto_start_meeting_summary?: boolean; + who_will_receive_summary?: 1 | 2 | 3 | 4; auto_start_ai_companion_questions?: boolean; + who_can_ask_questions?: 1 | 2 | 3 | 4 | 5; + summary_template_id?: string; device_testing?: boolean; + allow_host_control_participant_mute_state?: boolean; + disable_participant_video?: boolean; + email_in_attendee_report?: boolean; }; start_time?: string; start_url?: string; @@ -2425,9 +2664,9 @@ type MeetingsCreateMeetingResponse = { value?: string; visible?: boolean; }[]; - type?: number; + type?: 1 | 2 | 3 | 8 | 10; dynamic_host_key?: string; - creation_source?: string; + creation_source?: "other" | "open_api" | "web_portal"; }; type MeetingsListUpcomingMeetingsPathParams = { userId: string; @@ -2437,12 +2676,15 @@ type MeetingsListUpcomingMeetingsResponse = { meetings?: { id?: number; topic?: string; - type?: number; + type?: 1 | 2 | 3 | 8; start_time?: string; duration?: number; timezone?: string; created_at?: string; join_url?: string; + passcode?: string; + use_pmi?: boolean; + is_host?: boolean; }[]; }; type PACListUsersPACAccountsPathParams = { @@ -2475,7 +2717,7 @@ type ReportsGetSignInSignOutActivityReportResponse = { email?: string; ip_address?: string; time?: string; - type?: string; + type?: "Sign in" | "Sign out"; version?: string; }[]; from?: string; @@ -2490,7 +2732,7 @@ type ReportsGetBillingReportsResponse = { start_date?: string; tax_amount?: string; total_amount?: string; - type?: number; + type?: 0 | 1; }[]; currency?: string; }; @@ -2541,6 +2783,88 @@ type ReportsGetDailyUsageReportResponse = { month?: number; year?: number; }; +type ReportsGetHistoryMeetingAndWebinarListQueryParams = { + from: string; + to: string; + date_type?: "start_time" | "end_time"; + meeting_type?: "meeting" | "webinar" | "all"; + report_type?: "all" | "poll" | "survey" | "qa" | "resource" | "reaction"; + search_key?: string; + page_size?: number; + next_page_token?: string; + group_id?: string; + meeting_feature?: "screen_sharing" | "video_on" | "remote_control" | "closed_caption" | "language_interpretation" | "telephone_usage" | "in_meeting_chat" | "poll" | "join_by_room" | "waiting_room" | "live_transcription" | "reaction" | "zoom_apps" | "annotation" | "raise_hand" | "virtual_background" | "whiteboard" | "immersive_scene" | "avatar" | "switch_to_mobile" | "file_sharing" | "meeting_summary" | "meeting_questions" | "record_to_computer" | "record_to_cloud" | "live_translation" | "registration" | "smart_recording" | "multi_speaker" | "meeting_wallpaper" | "gen_ai_virtual_background" | "multi_share" | "document_collaboration" | "portrait_lighting" | "personalized_audio_isolation" | "color_themes"; +}; +type ReportsGetHistoryMeetingAndWebinarListResponse = { + next_page_token?: string; + page_size?: number; + history_meetings?: { + meeting_uuid?: string; + meeting_id?: number; + type?: "Meeting" | "Webinar"; + host_display_name?: string; + host_email?: string; + start_time?: string; + end_time?: string; + topic?: string; + participants?: number; + duration?: number; + total_participant_minutes?: number; + department?: string; + group?: string[]; + source?: string; + unique_viewers?: number; + max_concurrent_views?: number; + create_time?: string; + custom_fields?: { + key?: string; + value?: string; + }[]; + tracking_fields?: { + field?: string; + value?: string; + }[]; + feature_used?: { + screen_sharing?: boolean; + video_on?: boolean; + remote_control?: boolean; + closed_caption?: boolean; + breakout_room?: boolean; + language_interpretation?: boolean; + telephone_usage?: boolean; + in_meeting_chat?: boolean; + poll?: boolean; + join_by_room?: boolean; + waiting_room?: boolean; + live_transcription?: boolean; + reaction?: boolean; + zoom_apps?: boolean; + annotation?: boolean; + raise_hand?: boolean; + virtual_background?: boolean; + whiteboard?: boolean; + immersive_scene?: boolean; + avatar?: boolean; + switch_to_mobile?: boolean; + file_sharing?: boolean; + meeting_summary?: boolean; + meeting_questions?: boolean; + record_to_computer?: boolean; + record_to_cloud?: boolean; + live_translation?: boolean; + registration?: boolean; + smart_recording?: boolean; + multi_speaker?: boolean; + meeting_wallpaper?: boolean; + gen_ai_virtual_background?: boolean; + multi_share?: boolean; + document_collaboration?: boolean; + portrait_lighting?: boolean; + personalized_audio_isolation?: boolean; + color_themes?: boolean; + }; + }[]; +}; type ReportsGetMeetingActivitiesReportQueryParams = { from: string; to: string; @@ -2548,7 +2872,7 @@ type ReportsGetMeetingActivitiesReportQueryParams = { next_page_token?: string; meeting_number?: string; search_key?: string; - activity_type: string; + activity_type: "All Activities" | "Meeting Created" | "Meeting Started" | "User Join" | "User Left" | "Remote Control" | "In-Meeting Chat" | "Meeting Ended"; }; type ReportsGetMeetingActivitiesReportResponse = { meeting_activity_logs?: { @@ -2593,7 +2917,7 @@ type ReportsGetMeetingParticipantReportsPathParams = { type ReportsGetMeetingParticipantReportsQueryParams = { page_size?: number; next_page_token?: string; - include_fields?: string; + include_fields?: "registrant_id"; }; type ReportsGetMeetingParticipantReportsResponse = { next_page_token?: string; @@ -2610,7 +2934,7 @@ type ReportsGetMeetingParticipantReportsResponse = { leave_time?: string; name?: string; registrant_id?: string; - status?: string; + status?: "in_meeting" | "in_waiting_room"; user_email?: string; user_id?: string; bo_mtg_id?: string; @@ -2651,14 +2975,14 @@ type ReportsGetMeetingQAReportResponse = { question?: string; question_id?: string; create_time?: string; - question_status?: string; + question_status?: "default" | "open" | "dismissed" | "answered" | "deleted"; answer_details?: { user_id?: string; name?: string; email?: string; content?: string; create_time?: string; - type?: string; + type?: "default" | "host_answered_publicly" | "host_answered_privately" | "participant_commented" | "host_answered"; }[]; }[]; }[]; @@ -2692,7 +3016,7 @@ type ReportsGetOperationLogsReportQueryParams = { to: string; page_size?: number; next_page_token?: string; - category_type?: string; + category_type?: "all" | "user" | "user_settings" | "account" | "billing" | "im" | "recording" | "phone_contacts" | "webinar" | "sub_account" | "role" | "zoom_rooms"; }; type ReportsGetOperationLogsReportResponse = { next_page_token?: string; @@ -2707,8 +3031,8 @@ type ReportsGetOperationLogsReportResponse = { }[]; }; type ReportsGetTelephoneReportsQueryParams = { - type?: string; - query_date_type?: string; + type?: "1" | "2" | "3"; + query_date_type?: "start_time" | "end_time" | "meeting_start_time" | "meeting_end_time"; from: string; to: string; page_size?: number; @@ -2739,7 +3063,7 @@ type ReportsGetTelephoneReportsResponse = { signaled_number?: string; start_time?: string; total?: number; - type?: string; + type?: "toll-free" | "call-out" | "call-in" | "US toll-number" | "global toll-number" | "premium" | "premium call-in" | "Toll"; uuid?: string; }[]; }; @@ -2748,7 +3072,7 @@ type ReportsGetUpcomingEventsReportQueryParams = { to: string; page_size?: number; next_page_token?: string; - type?: string; + type?: "meeting" | "webinar" | "all"; group_id?: string; }; type ReportsGetUpcomingEventsReportResponse = { @@ -2766,7 +3090,7 @@ type ReportsGetUpcomingEventsReportResponse = { }[]; }; type ReportsGetActiveOrInactiveHostReportsQueryParams = { - type?: string; + type?: "active" | "inactive"; from: string; to: string; page_size?: number; @@ -2803,14 +3127,14 @@ type ReportsGetActiveOrInactiveHostReportsResponse = { }[]; }; type ReportsGetMeetingReportsPathParams = { - userId: string | string | string; + userId: string; }; type ReportsGetMeetingReportsQueryParams = { from: string; to: string; page_size?: number; next_page_token?: string; - type?: string; + type?: "past" | "pastOne" | "pastJoined"; }; type ReportsGetMeetingReportsResponse = { next_page_token?: string; @@ -2834,7 +3158,7 @@ type ReportsGetMeetingReportsResponse = { start_time?: string; topic?: string; total_minutes?: number; - type?: number; + type?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; user_email?: string; user_name?: string; uuid?: string; @@ -2847,7 +3171,7 @@ type ReportsGetMeetingReportsResponse = { has_screen_share?: boolean; has_recording?: boolean; has_chat?: boolean; - meeting_encryption_status?: number; + meeting_encryption_status?: 1 | 2; participants_count_my_account?: number; }[]; next_page_token?: string; @@ -2884,7 +3208,7 @@ type ReportsGetWebinarParticipantReportsPathParams = { type ReportsGetWebinarParticipantReportsQueryParams = { page_size?: number; next_page_token?: string; - include_fields?: string; + include_fields?: "registrant_id"; }; type ReportsGetWebinarParticipantReportsResponse = { next_page_token?: string; @@ -2901,7 +3225,7 @@ type ReportsGetWebinarParticipantReportsResponse = { leave_time?: string; name?: string; registrant_id?: string; - status?: string; + status?: "in_meeting" | "in_waiting_room"; user_email?: string; user_id?: string; participant_user_id?: string; @@ -2942,14 +3266,14 @@ type ReportsGetWebinarQAReportResponse = { question?: string; question_id?: string; create_time?: string; - question_status?: string; + question_status?: "default" | "open" | "dismissed" | "answered" | "deleted"; answer_details?: { user_id?: string; name?: string; email?: string; content?: string; create_time?: string; - type?: string; + type?: "default" | "host_answered_publicly" | "host_answered_privately" | "participant_commented" | "host_answered"; }[]; }[]; }[]; @@ -2979,73 +3303,90 @@ type ReportsGetWebinarSurveyReportResponse = { }[]; }; type SIPPhoneListSIPPhonesQueryParams = { - page_number?: number; search_key?: string; page_size?: number; next_page_token?: string; }; type SIPPhoneListSIPPhonesResponse = { next_page_token?: string; - page_count?: number; - page_number?: number; page_size?: number; phones?: { authorization_name?: string; domain?: string; - id?: string; + phone_id?: string; password?: string; - proxy_server?: string; - proxy_server2?: string; - proxy_server3?: string; - register_server?: string; - register_server2?: string; - register_server3?: string; registration_expire_time?: number; - transport_protocol?: string; - transport_protocol2?: string; - transport_protocol3?: string; user_email?: string; user_name?: string; voice_mail?: string; + display_number?: string; + server?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; + server_2?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; + server_3?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; }[]; - total_records?: number; }; type SIPPhoneEnableSIPPhoneRequestBody = { authorization_name: string; domain: string; password: string; - proxy_server: string; - proxy_server2?: string; - proxy_server3?: string; - register_server: string; - register_server2?: string; - register_server3?: string; registration_expire_time?: number; - transport_protocol?: string; - transport_protocol2?: string; - transport_protocol3?: string; user_email: string; user_name: string; - voice_mail: string; + voice_mail?: string; + display_number?: string; + server: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; + server_2?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; + server_3?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; }; type SIPPhoneEnableSIPPhoneResponse = { - id?: string; + phone_id?: string; authorization_name?: string; domain?: string; password?: string; - proxy_server?: string; - proxy_server2?: string; - proxy_server3?: string; - register_server?: string; - register_server2?: string; - register_server3?: string; registration_expire_time?: number; - transport_protocol?: string; - transport_protocol2?: string; - transport_protocol3?: string; user_email?: string; user_name?: string; voice_mail?: string; + display_number?: string; + server?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; + server_2?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; + server_3?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; }; type SIPPhoneDeleteSIPPhonePathParams = { phoneId: string; @@ -3054,21 +3395,28 @@ type SIPPhoneUpdateSIPPhonePathParams = { phoneId: string; }; type SIPPhoneUpdateSIPPhoneRequestBody = { - authorization_name: string; - domain: string; - password: string; - proxy_server: string; - proxy_server2: string; - proxy_server3: string; - register_server: string; - register_server2: string; - register_server3: string; + authorization_name?: string; + domain?: string; + password?: string; registration_expire_time?: number; - transport_protocol?: string; - transport_protocol2?: string; - transport_protocol3?: string; - user_name: string; - voice_mail: string; + user_name?: string; + voice_mail?: string; + display_number?: string; + server?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; + server_2?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; + server_3?: { + proxy_server?: string; + register_server?: string; + transport_protocol?: "UDP" | "TCP" | "TLS" | "AUTO"; + }; }; type TSPGetAccountsTSPInformationResponse = { dial_in_number_unrestricted?: boolean; @@ -3080,7 +3428,7 @@ type TSPGetAccountsTSPInformationResponse = { enable?: boolean; master_account_setting_extended?: boolean; modify_credential_forbidden?: boolean; - tsp_bridge?: string; + tsp_bridge?: "US_TSP_TB" | "EU_TSP_TB"; tsp_enabled?: boolean; tsp_provider?: string; }; @@ -3089,7 +3437,7 @@ type TSPUpdateAccountsTSPInformationRequestBody = { enable?: boolean; master_account_setting_extended?: boolean; modify_credential_forbidden?: boolean; - tsp_bridge?: string; + tsp_bridge?: "US_TSP_TB" | "EU_TSP_TB"; tsp_enabled?: boolean; tsp_provider?: string; }; @@ -3103,11 +3451,11 @@ type TSPListUsersTSPAccountsResponse = { code?: string; country_label?: string; number?: string; - type?: string; + type?: "toll" | "tollfree" | "media_link"; }[]; - id?: string; + id?: "1" | "2"; leader_pin: string; - tsp_bridge?: string; + tsp_bridge?: "US_TSP_TB" | "EU_TSP_TB"; }[]; }; type TSPAddUsersTSPAccountPathParams = { @@ -3119,10 +3467,10 @@ type TSPAddUsersTSPAccountRequestBody = { code?: string; country_label?: string; number?: string; - type?: string; + type?: "toll" | "tollfree" | "media_link"; }[]; leader_pin: string; - tsp_bridge?: string; + tsp_bridge?: "US_TSP_TB" | "EU_TSP_TB"; }; type TSPAddUsersTSPAccountResponse = { id?: string; @@ -3132,10 +3480,10 @@ type TSPAddUsersTSPAccountResponse = { code?: string; country_label?: string; number?: string; - type?: string; + type?: "toll" | "tollfree" | "media_link"; }[]; leader_pin: string; - tsp_bridge?: string; + tsp_bridge?: "US_TSP_TB" | "EU_TSP_TB"; }; type TSPSetGlobalDialInURLForTSPUserPathParams = { userId: string; @@ -3145,7 +3493,7 @@ type TSPSetGlobalDialInURLForTSPUserRequestBody = { }; type TSPGetUsersTSPAccountPathParams = { userId: string; - tspId: string; + tspId: "1" | "2"; }; type TSPGetUsersTSPAccountResponse = { conference_code: string; @@ -3153,19 +3501,19 @@ type TSPGetUsersTSPAccountResponse = { code?: string; country_label?: string; number?: string; - type?: string; + type?: "toll" | "tollfree" | "media_link"; }[]; id?: string; leader_pin: string; - tsp_bridge?: string; + tsp_bridge?: "US_TSP_TB" | "EU_TSP_TB"; }; type TSPDeleteUsersTSPAccountPathParams = { userId: string; - tspId: string; + tspId: "1" | "2"; }; type TSPUpdateTSPAccountPathParams = { userId: string; - tspId: string; + tspId: "1" | "2"; }; type TSPUpdateTSPAccountRequestBody = { conference_code: string; @@ -3173,21 +3521,20 @@ type TSPUpdateTSPAccountRequestBody = { code?: string; country_label?: string; number?: string; - type?: string; + type?: "toll" | "tollfree" | "media_link"; }[]; leader_pin: string; - tsp_bridge?: string; + tsp_bridge?: "US_TSP_TB" | "EU_TSP_TB"; }; type TrackingFieldListTrackingFieldsResponse = { total_records?: number; - tracking_fields?: ({ + tracking_fields?: { id?: string; - } & { field?: string; recommended_values?: string[]; required?: boolean; visible?: boolean; - })[]; + }[]; }; type TrackingFieldCreateTrackingFieldRequestBody = { field?: string; @@ -3208,7 +3555,6 @@ type TrackingFieldGetTrackingFieldPathParams = { }; type TrackingFieldGetTrackingFieldResponse = { id?: string; -} & { field?: string; recommended_values?: string[]; required?: boolean; @@ -3264,13 +3610,13 @@ type WebinarsGetWebinarAbsenteesResponse = { industry?: string; job_title?: string; last_name?: string; - no_of_employees?: string; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-250" | "251-500" | "501-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; org?: string; phone?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; state?: string; - status?: string; + status?: "approved" | "denied" | "pending"; zip?: string; } & { create_time?: string; @@ -3308,7 +3654,7 @@ type WebinarsListWebinarParticipantsResponse = { leave_time?: string; duration?: number; failover?: boolean; - status?: string; + status?: "in_meeting" | "in_waiting_room"; internal_user?: boolean; }[]; total_records?: number; @@ -3375,7 +3721,7 @@ type WebinarsListWebinarsPathParams = { userId: string; }; type WebinarsListWebinarsQueryParams = { - type?: string; + type?: "scheduled" | "upcoming"; page_size?: number; page_number?: number; }; @@ -3396,7 +3742,7 @@ type WebinarsListWebinarsResponse = { start_time?: string; timezone?: string; topic?: string; - type?: number; + type?: 5 | 6 | 9; uuid?: string; is_simulive?: boolean; }[]; @@ -3413,10 +3759,10 @@ type WebinarsCreateWebinarRequestBody = { end_date_time?: string; end_times?: number; monthly_day?: number; - monthly_week?: number; - monthly_week_day?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; repeat_interval?: number; - type: number; + type: 1 | 2 | 3; weekly_days?: string; }; schedule_for?: string; @@ -3424,16 +3770,16 @@ type WebinarsCreateWebinarRequestBody = { allow_multiple_devices?: boolean; alternative_hosts?: string; alternative_host_update_polls?: boolean; - approval_type?: number; + approval_type?: 0 | 1 | 2; attendees_and_panelists_reminder_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; - audio?: string; + audio?: "both" | "telephony" | "voip" | "thirdParty"; audio_conference_info?: string; authentication_domains?: string; authentication_option?: string; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; close_registration?: boolean; contact_email?: string; contact_name?: string; @@ -3442,11 +3788,11 @@ type WebinarsCreateWebinarRequestBody = { enforce_login_domains?: string; follow_up_absentees_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; follow_up_attendees_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; global_dial_in_countries?: string[]; hd_video?: boolean; @@ -3457,6 +3803,7 @@ type WebinarsCreateWebinarRequestBody = { interpreters?: { email?: string; languages?: string; + interpreter_languages?: string; }[]; }; sign_language_interpretation?: { @@ -3478,7 +3825,7 @@ type WebinarsCreateWebinarRequestBody = { question_and_answer?: { allow_submit_questions?: boolean; allow_anonymous_questions?: boolean; - answer_questions?: string; + answer_questions?: "only" | "all"; attendees_can_comment?: boolean; attendees_can_upvote?: boolean; allow_auto_reply?: boolean; @@ -3487,11 +3834,13 @@ type WebinarsCreateWebinarRequestBody = { }; registrants_email_notification?: boolean; registrants_restrict_number?: number; - registration_type?: number; + registration_type?: 1 | 2 | 3; send_1080p_video_to_attendees?: boolean; show_share_button?: boolean; survey_url?: string; enable_session_branding?: boolean; + allow_host_control_participant_mute_state?: boolean; + email_in_attendee_report?: boolean; }; start_time?: string; template_id?: string; @@ -3501,9 +3850,15 @@ type WebinarsCreateWebinarRequestBody = { field: string; value?: string; }[]; - type?: number; + type?: 5 | 6 | 9; is_simulive?: boolean; record_file_id?: string; + transition_to_live?: boolean; + simulive_delay_start?: { + enable?: boolean; + time?: number; + timeunit?: "second" | "minute"; + }; }; type WebinarsCreateWebinarResponse = { host_email?: string; @@ -3520,7 +3875,7 @@ type WebinarsCreateWebinarResponse = { duration?: number; occurrence_id?: string; start_time?: string; - status?: string; + status?: "available" | "deleted"; }[]; password?: string; encrypted_passcode?: string; @@ -3529,27 +3884,27 @@ type WebinarsCreateWebinarResponse = { end_date_time?: string; end_times?: number; monthly_day?: number; - monthly_week?: number; - monthly_week_day?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; repeat_interval?: number; - type: number; + type: 1 | 2 | 3; weekly_days?: string; }; settings?: { allow_multiple_devices?: boolean; alternative_hosts?: string; alternative_host_update_polls?: boolean; - approval_type?: number; + approval_type?: 0 | 1 | 2; attendees_and_panelists_reminder_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; - audio?: string; + audio?: "both" | "telephony" | "voip" | "thirdParty"; audio_conference_info?: string; authentication_domains?: string; authentication_name?: string; authentication_option?: string; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; close_registration?: boolean; contact_email?: string; contact_name?: string; @@ -3558,13 +3913,20 @@ type WebinarsCreateWebinarResponse = { enforce_login_domains?: string; follow_up_absentees_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; follow_up_attendees_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; global_dial_in_countries?: string[]; + global_dial_in_numbers?: { + city?: string; + country?: string; + country_name?: string; + number?: string; + type?: "toll" | "tollfree" | "premium"; + }[]; hd_video?: boolean; hd_video_for_attendees?: boolean; host_video?: boolean; @@ -3573,6 +3935,7 @@ type WebinarsCreateWebinarResponse = { interpreters?: { email?: string; languages?: string; + interpreter_languages?: string; }[]; }; sign_language_interpretation?: { @@ -3586,7 +3949,6 @@ type WebinarsCreateWebinarResponse = { meeting_authentication?: boolean; add_watermark?: boolean; add_audio_watermark?: boolean; - notify_registrants?: boolean; on_demand?: boolean; panelists_invitation_email_notification?: boolean; panelists_video?: boolean; @@ -3595,7 +3957,7 @@ type WebinarsCreateWebinarResponse = { question_and_answer?: { allow_submit_questions?: boolean; allow_anonymous_questions?: boolean; - answer_questions?: string; + answer_questions?: "only" | "all"; attendees_can_comment?: boolean; attendees_can_upvote?: boolean; allow_auto_reply?: boolean; @@ -3605,11 +3967,13 @@ type WebinarsCreateWebinarResponse = { registrants_confirmation_email?: boolean; registrants_email_notification?: boolean; registrants_restrict_number?: number; - registration_type?: number; + registration_type?: 1 | 2 | 3; send_1080p_video_to_attendees?: boolean; show_share_button?: boolean; survey_url?: string; enable_session_branding?: boolean; + allow_host_control_participant_mute_state?: boolean; + email_in_attendee_report?: boolean; }; start_time?: string; start_url?: string; @@ -3619,10 +3983,16 @@ type WebinarsCreateWebinarResponse = { field?: string; value?: string; }[]; - type?: number; + type?: 5 | 6 | 9; is_simulive?: boolean; record_file_id?: string; - creation_source?: string; + transition_to_live?: boolean; + simulive_delay_start?: { + enable?: boolean; + time?: number; + timeunit?: string; + }; + creation_source?: "other" | "open_api" | "web_portal"; }; type WebinarsGetWebinarPathParams = { webinarId: string; @@ -3644,7 +4014,7 @@ type WebinarsGetWebinarResponse = { duration?: number; occurrence_id?: string; start_time?: string; - status?: string; + status?: "available" | "deleted"; }[]; password?: string; encrypted_passcode?: string; @@ -3653,27 +4023,27 @@ type WebinarsGetWebinarResponse = { end_date_time?: string; end_times?: number; monthly_day?: number; - monthly_week?: number; - monthly_week_day?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; repeat_interval?: number; - type: number; + type: 1 | 2 | 3; weekly_days?: string; }; settings?: { allow_multiple_devices?: boolean; alternative_hosts?: string; alternative_host_update_polls?: boolean; - approval_type?: number; + approval_type?: 0 | 1 | 2; attendees_and_panelists_reminder_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; - audio?: string; + audio?: "both" | "telephony" | "voip" | "thirdParty"; audio_conference_info?: string; authentication_domains?: string; authentication_name?: string; authentication_option?: string; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; close_registration?: boolean; contact_email?: string; contact_name?: string; @@ -3682,13 +4052,20 @@ type WebinarsGetWebinarResponse = { enforce_login_domains?: string; follow_up_absentees_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; follow_up_attendees_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; global_dial_in_countries?: string[]; + global_dial_in_numbers?: { + city?: string; + country?: string; + country_name?: string; + number?: string; + type?: "toll" | "tollfree" | "premium"; + }[]; hd_video?: boolean; hd_video_for_attendees?: boolean; host_video?: boolean; @@ -3697,6 +4074,7 @@ type WebinarsGetWebinarResponse = { interpreters?: { email?: string; languages?: string; + interpreter_languages?: string; }[]; }; sign_language_interpretation?: { @@ -3710,7 +4088,6 @@ type WebinarsGetWebinarResponse = { meeting_authentication?: boolean; add_watermark?: boolean; add_audio_watermark?: boolean; - notify_registrants?: boolean; on_demand?: boolean; panelists_invitation_email_notification?: boolean; panelists_video?: boolean; @@ -3719,7 +4096,7 @@ type WebinarsGetWebinarResponse = { question_and_answer?: { allow_submit_questions?: boolean; allow_anonymous_questions?: boolean; - answer_questions?: string; + answer_questions?: "only" | "all"; attendees_can_comment?: boolean; attendees_can_upvote?: boolean; allow_auto_reply?: boolean; @@ -3729,11 +4106,13 @@ type WebinarsGetWebinarResponse = { registrants_confirmation_email?: boolean; registrants_email_notification?: boolean; registrants_restrict_number?: number; - registration_type?: number; + registration_type?: 1 | 2 | 3; send_1080p_video_to_attendees?: boolean; show_share_button?: boolean; survey_url?: string; enable_session_branding?: boolean; + allow_host_control_participant_mute_state?: boolean; + email_in_attendee_report?: boolean; }; start_time?: string; start_url?: string; @@ -3743,10 +4122,16 @@ type WebinarsGetWebinarResponse = { field?: string; value?: string; }[]; - type?: number; + type?: 5 | 6 | 9; is_simulive?: boolean; record_file_id?: string; - creation_source?: string; + transition_to_live?: boolean; + simulive_delay_start?: { + enable?: boolean; + time?: number; + timeunit?: "second" | "minute"; + }; + creation_source?: "other" | "open_api" | "web_portal"; }; type WebinarsDeleteWebinarPathParams = { webinarId: number; @@ -3770,27 +4155,27 @@ type WebinarsUpdateWebinarRequestBody = { end_date_time?: string; end_times?: number; monthly_day?: number; - monthly_week?: number; - monthly_week_day?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; repeat_interval?: number; - type: number; - weekly_days?: string; + type: 1 | 2 | 3; + weekly_days?: "1" | "2" | "3" | "4" | "5" | "6" | "7"; }; settings?: { allow_multiple_devices?: boolean; alternative_hosts?: string; alternative_host_update_polls?: boolean; - approval_type?: number; + approval_type?: 0 | 1 | 2; attendees_and_panelists_reminder_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; - audio?: string; + audio?: "both" | "telephony" | "voip" | "thirdParty"; audio_conference_info?: string; authentication_domains?: string; authentication_name?: string; authentication_option?: string; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; close_registration?: boolean; contact_email?: string; contact_name?: string; @@ -3799,11 +4184,11 @@ type WebinarsUpdateWebinarRequestBody = { enforce_login_domains?: string; follow_up_absentees_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; follow_up_attendees_email_notification?: { enable?: boolean; - type?: number; + type?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; }; global_dial_in_countries?: string[]; hd_video?: boolean; @@ -3814,6 +4199,7 @@ type WebinarsUpdateWebinarRequestBody = { interpreters?: { email?: string; languages?: string; + interpreter_languages?: string; }[]; }; sign_language_interpretation?: { @@ -3836,7 +4222,7 @@ type WebinarsUpdateWebinarRequestBody = { question_and_answer?: { allow_submit_questions?: boolean; allow_anonymous_questions?: boolean; - answer_questions?: string; + answer_questions?: "only" | "all"; attendees_can_comment?: boolean; attendees_can_upvote?: boolean; allow_auto_reply?: boolean; @@ -3846,11 +4232,13 @@ type WebinarsUpdateWebinarRequestBody = { registrants_confirmation_email?: boolean; registrants_email_notification?: boolean; registrants_restrict_number?: number; - registration_type?: number; + registration_type?: 1 | 2 | 3; send_1080p_video_to_attendees?: boolean; show_share_button?: boolean; survey_url?: string; enable_session_branding?: boolean; + allow_host_control_participant_mute_state?: boolean; + email_in_attendee_report?: boolean; }; start_time?: string; timezone?: string; @@ -3859,9 +4247,15 @@ type WebinarsUpdateWebinarRequestBody = { field?: string; value?: string; }[]; - type?: number; + type?: 5 | 6 | 9; is_simulive?: boolean; record_file_id?: string; + transition_to_live?: boolean; + simulive_delay_start?: { + enable?: boolean; + time?: number; + timeunit?: "second" | "minute"; + }; }; type WebinarsPerformBatchRegistrationPathParams = { webinarId: string; @@ -3952,7 +4346,7 @@ type WebinarsUploadWebinarsBrandingVirtualBackgroundResponse = { name?: string; is_default?: boolean; size?: number; - type?: string; + type?: "image"; }; type WebinarsDeleteWebinarsBrandingVirtualBackgroundsPathParams = { webinarId: number; @@ -3977,7 +4371,7 @@ type WebinarsUploadWebinarsBrandingWallpaperResponse = { id?: string; name?: string; size?: number; - type?: string; + type?: "image"; }; type WebinarsDeleteWebinarsBrandingWallpaperPathParams = { webinarId: number; @@ -3988,6 +4382,8 @@ type WebinarsCreateWebinarsInviteLinksPathParams = { type WebinarsCreateWebinarsInviteLinksRequestBody = { attendees?: { name: string; + disable_video?: boolean; + disable_audio?: boolean; }[]; ttl?: number; }; @@ -4001,21 +4397,21 @@ type WebinarsGetWebinarsJoinTokenForLiveStreamingPathParams = { webinarId: number; }; type WebinarsGetWebinarsJoinTokenForLiveStreamingResponse = { - expire_in?: number; + expire_in?: 120; token?: string; }; type WebinarsGetWebinarsArchiveTokenForLocalArchivingPathParams = { webinarId: number; }; type WebinarsGetWebinarsArchiveTokenForLocalArchivingResponse = { - expire_in?: number; + expire_in?: 120; token?: string; }; type WebinarsGetWebinarsJoinTokenForLocalRecordingPathParams = { webinarId: number; }; type WebinarsGetWebinarsJoinTokenForLocalRecordingResponse = { - expire_in?: number; + expire_in?: 120; token?: string; }; type WebinarsGetLiveStreamDetailsPathParams = { @@ -4040,7 +4436,7 @@ type WebinarsUpdateLiveStreamStatusPathParams = { webinarId: number; }; type WebinarsUpdateLiveStreamStatusRequestBody = { - action?: string; + action?: "start" | "stop"; settings?: { active_speaker_name?: boolean; display_name?: string; @@ -4101,10 +4497,10 @@ type WebinarsListWebinarsPollsQueryParams = { type WebinarsListWebinarsPollsResponse = { polls?: ({ id?: string; - status?: string; + status?: "notstart" | "started" | "ended" | "sharing" | "deactivated"; } & { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -4122,7 +4518,7 @@ type WebinarsListWebinarsPollsResponse = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; })[]; @@ -4133,7 +4529,7 @@ type WebinarsCreateWebinarsPollPathParams = { }; type WebinarsCreateWebinarsPollRequestBody = { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -4151,16 +4547,16 @@ type WebinarsCreateWebinarsPollRequestBody = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; }; type WebinarsCreateWebinarsPollResponse = { id?: string; - status?: string; + status?: "notstart" | "started" | "ended" | "sharing"; } & { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -4178,7 +4574,7 @@ type WebinarsCreateWebinarsPollResponse = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; }; @@ -4188,10 +4584,10 @@ type WebinarsGetWebinarPollPathParams = { }; type WebinarsGetWebinarPollResponse = { id?: string; - status?: string; + status?: "notstart" | "started" | "ended" | "sharing" | "deactivated"; } & { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -4209,7 +4605,7 @@ type WebinarsGetWebinarPollResponse = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; }; @@ -4219,7 +4615,7 @@ type WebinarsUpdateWebinarPollPathParams = { }; type WebinarsUpdateWebinarPollRequestBody = { anonymous?: boolean; - poll_type?: number; + poll_type?: 1 | 2 | 3; questions?: { answer_max_character?: number; answer_min_character?: number; @@ -4237,7 +4633,7 @@ type WebinarsUpdateWebinarPollRequestBody = { rating_min_value?: number; right_answers?: string[]; show_as_dropdown?: boolean; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; }[]; title?: string; }; @@ -4250,7 +4646,7 @@ type WebinarsListWebinarRegistrantsPathParams = { }; type WebinarsListWebinarRegistrantsQueryParams = { occurrence_id?: string; - status?: string; + status?: "pending" | "approved" | "denied"; tracking_source_id?: string; page_size?: number; page_number?: number; @@ -4279,13 +4675,13 @@ type WebinarsListWebinarRegistrantsResponse = { industry?: string; job_title?: string; last_name?: string; - no_of_employees?: string; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-250" | "251-500" | "501-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; org?: string; phone?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; state?: string; - status?: string; + status?: "approved" | "denied" | "pending"; zip?: string; } & { create_time?: string; @@ -4316,12 +4712,11 @@ type WebinarsAddWebinarRegistrantRequestBody = { }[]; industry?: string; job_title?: string; - no_of_employees?: string; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-500" | "500-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; org?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; -} & { - language?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; + language?: "en-US" | "de-DE" | "es-ES" | "fr-FR" | "jp-JP" | "pt-PT" | "ru-RU" | "zh-CN" | "zh-TW" | "ko-KO" | "it-IT" | "vi-VN" | "pl-PL" | "Tr-TR"; source_id?: string; }; type WebinarsAddWebinarRegistrantResponse = { @@ -4345,10 +4740,10 @@ type WebinarsListRegistrationQuestionsResponse = { answers?: string[]; required?: boolean; title?: string; - type?: string; + type?: "short" | "single_radio" | "single_dropdown" | "multiple"; }[]; questions?: { - field_name?: string; + field_name?: "last_name" | "address" | "city" | "country" | "zip" | "state" | "phone" | "industry" | "org" | "job_title" | "purchasing_time_frame" | "role_in_purchase_process" | "no_of_employees" | "comments"; required?: boolean; }[]; }; @@ -4360,10 +4755,10 @@ type WebinarsUpdateRegistrationQuestionsRequestBody = { answers?: string[]; required?: boolean; title?: string; - type?: string; + type?: "short" | "single_radio" | "single_dropdown" | "multiple"; }[]; questions?: { - field_name?: string; + field_name?: "last_name" | "address" | "city" | "country" | "zip" | "state" | "phone" | "industry" | "org" | "job_title" | "purchasing_time_frame" | "role_in_purchase_process" | "no_of_employees" | "comments"; required?: boolean; }[]; }; @@ -4374,7 +4769,7 @@ type WebinarsUpdateRegistrantsStatusQueryParams = { occurrence_id?: string; }; type WebinarsUpdateRegistrantsStatusRequestBody = { - action: string; + action: "approve" | "deny" | "cancel"; registrants?: { email?: string; id?: string; @@ -4403,16 +4798,16 @@ type WebinarsGetWebinarRegistrantResponse = { industry?: string; job_title?: string; last_name?: string; - no_of_employees?: string; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-250" | "251-500" | "501-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; org?: string; phone?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; state?: string; - status?: string; + status?: "approved" | "denied" | "pending"; zip?: string; } & { - language?: string; + language?: "en-US" | "de-DE" | "es-ES" | "fr-FR" | "jp-JP" | "pt-PT" | "ru-RU" | "zh-CN" | "zh-TW" | "ko-KO" | "it-IT" | "vi-VN" | "pl-PL" | "Tr-TR"; }) & { create_time?: string; join_url?: string; @@ -4441,7 +4836,7 @@ type WebinarsUpdateWebinarStatusPathParams = { webinarId: number; }; type WebinarsUpdateWebinarStatusRequestBody = { - action?: string; + action?: "end"; }; type WebinarsGetWebinarSurveyPathParams = { webinarId: number; @@ -4455,7 +4850,7 @@ type WebinarsGetWebinarSurveyResponse = { feedback?: string; questions?: { name?: string; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; answer_required?: boolean; show_as_dropdown?: boolean; answers?: string[]; @@ -4489,7 +4884,7 @@ type WebinarsUpdateWebinarSurveyRequestBody = { feedback?: string; questions?: { name?: string; - type?: string; + type?: "single" | "multiple" | "matching" | "rank_order" | "short_answer" | "long_answer" | "fill_in_the_blank" | "rating_scale"; answer_required?: boolean; show_as_dropdown?: boolean; answers?: string[]; @@ -4512,7 +4907,7 @@ type WebinarsGetWebinarsTokenPathParams = { webinarId: number; }; type WebinarsGetWebinarsTokenQueryParams = { - type?: string; + type?: "closed_caption_token"; }; type WebinarsGetWebinarsTokenResponse = { token?: string; @@ -4612,6 +5007,12 @@ declare class MeetingsEndpoints extends WebEndpoints { } & { body?: CloudRecordingRecoverSingleRecordingRequestBody; } & object) => Promise>; + getMeetingTranscript: (_: { + path: CloudRecordingGetMeetingTranscriptPathParams; + } & object) => Promise>; + deleteMeetingOrWebinarTranscript: (_: { + path: CloudRecordingDeleteMeetingOrWebinarTranscriptPathParams; + } & object) => Promise>; recoverMeetingRecordings: (_: { path: CloudRecordingRecoverMeetingRecordingsPathParams; } & { @@ -4636,8 +5037,11 @@ declare class MeetingsEndpoints extends WebEndpoints { assignDeviceToUserOrCommonarea: (_: object & { body: DevicesAssignDeviceToUserOrCommonareaRequestBody; }) => Promise>; - upgradeZpaOsApp: (_: object & { - body: DevicesUpgradeZpaOsAppRequestBody; + getZoomPhoneApplianceSettingsByUserID: (_: object & { + query?: DevicesGetZoomPhoneApplianceSettingsByUserIDQueryParams; + }) => Promise>; + upgradeZPAFirmwareOrApp: (_: object & { + body: DevicesUpgradeZPAFirmwareOrAppRequestBody; }) => Promise>; deleteZPADeviceByVendorAndMacAddress: (_: { path: DevicesDeleteZPADeviceByVendorAndMacAddressPathParams; @@ -4656,6 +5060,11 @@ declare class MeetingsEndpoints extends WebEndpoints { } & { body: DevicesChangeDeviceRequestBody; } & object) => Promise>; + assignDeviceToGroup: (_: { + path: DevicesAssignDeviceToGroupPathParams; + } & object & { + query: DevicesAssignDeviceToGroupQueryParams; + }) => Promise>; changeDeviceAssociation: (_: { path: DevicesChangeDeviceAssociationPathParams; } & { @@ -4694,9 +5103,14 @@ declare class MeetingsEndpoints extends WebEndpoints { } & { body?: MeetingsUseInMeetingControlsRequestBody; } & object) => Promise>; - listMeetingSummariesOfAccount: (_: object & { - query?: MeetingsListMeetingSummariesOfAccountQueryParams; - }) => Promise>; + updateParticipantRealTimeMediaStreamsRTMSAppStatus: (_: { + path: MeetingsUpdateParticipantRealTimeMediaStreamsRTMSAppStatusPathParams; + } & { + body?: MeetingsUpdateParticipantRealTimeMediaStreamsRTMSAppStatusRequestBody; + } & object) => Promise>; + listAccountsMeetingOrWebinarSummaries: (_: object & { + query?: MeetingsListAccountsMeetingOrWebinarSummariesQueryParams; + }) => Promise>; getMeeting: (_: { path: MeetingsGetMeetingPathParams; } & object & { @@ -4756,9 +5170,12 @@ declare class MeetingsEndpoints extends WebEndpoints { } & { body?: MeetingsUpdateLivestreamStatusRequestBody; } & object) => Promise>; - getMeetingSummary: (_: { - path: MeetingsGetMeetingSummaryPathParams; - } & object) => Promise>; + getMeetingOrWebinarSummary: (_: { + path: MeetingsGetMeetingOrWebinarSummaryPathParams; + } & object) => Promise>; + deleteMeetingOrWebinarSummary: (_: { + path: MeetingsDeleteMeetingOrWebinarSummaryPathParams; + } & object) => Promise>; addMeetingApp: (_: { path: MeetingsAddMeetingAppPathParams; } & object) => Promise>; @@ -4905,6 +5322,9 @@ declare class MeetingsEndpoints extends WebEndpoints { getDailyUsageReport: (_: object & { query?: ReportsGetDailyUsageReportQueryParams; }) => Promise>; + getHistoryMeetingAndWebinarList: (_: object & { + query: ReportsGetHistoryMeetingAndWebinarListQueryParams; + }) => Promise>; getMeetingActivitiesReport: (_: object & { query: ReportsGetMeetingActivitiesReportQueryParams; }) => Promise>; @@ -4973,7 +5393,7 @@ declare class MeetingsEndpoints extends WebEndpoints { updateSIPPhone: (_: { path: SIPPhoneUpdateSIPPhonePathParams; } & { - body: SIPPhoneUpdateSIPPhoneRequestBody; + body?: SIPPhoneUpdateSIPPhoneRequestBody; } & object) => Promise>; }; readonly tSP: { @@ -5260,7 +5680,7 @@ declare class MeetingsEndpoints extends WebEndpoints { }; } -type WebinarSharingStartedEvent = Event<"webinar.sharing_started"> & { +type MeetingParticipantJbhWaitingEvent = Event<"meeting.participant_jbh_waiting"> & { event: string; event_ts: number; payload: { @@ -5270,37 +5690,8 @@ type WebinarSharingStartedEvent = Event<"webinar.sharing_started"> & { uuid: string; host_id: string; topic: string; - type: number; - start_time: string; - timezone?: string; - duration: number; - participant: { - user_id: string; - user_name?: string; - id?: string; - sharing_details: { - content: string; - link_source: string; - file_link: string; - date_time: string; - source: string; - }; - }; - }; - }; -}; -type MeetingParticipantJbhWaitingEvent = Event<"meeting.participant_jbh_waiting"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - id: string; - uuid: string; - host_id: string; - topic: string; - type: number; - start_time?: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time?: string; timezone?: string; duration: number; participant: { @@ -5311,57 +5702,6 @@ type MeetingParticipantJbhWaitingEvent = Event<"meeting.participant_jbh_waiting" }; }; }; -type MeetingRegistrationCreatedEvent = Event<"meeting.registration_created"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - uuid: string; - id: number; - host_id: string; - topic: string; - type: number; - start_time: string; - duration: number; - timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; - registrant: { - id: string; - first_name: string; - last_name?: string; - email: string; - address?: string; - city?: string; - country?: string; - zip?: string; - state?: string; - phone?: string; - industry?: string; - org?: string; - job_title?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; - no_of_employees?: string; - comments?: string; - custom_questions?: ExactlyOneOf<[ - { - title: string; - value: string; - } - ]>[]; - status: string; - join_url: string; - participant_pin_code?: number; - }; - }; - }; -}; type MeetingSummaryRecoveredEvent = Event<"meeting.summary_recovered"> & { event: string; event_ts: number; @@ -5385,102 +5725,6 @@ type MeetingSummaryRecoveredEvent = Event<"meeting.summary_recovered"> & { }; }; }; -type RecordingRecoveredEvent = Event<"recording.recovered"> & { - event: string; - event_ts: number; - download_token?: string; - payload: { - account_id: string; - operator: string; - operator_id: string; - object: { - id: number; - uuid: string; - host_id: string; - topic: string; - type: number; - start_time: string; - account_id: string; - timezone?: string; - duration: number; - share_url: string; - total_size: number; - recording_count: number; - recording_files: { - id: string; - meeting_id: string; - recording_start: string; - recording_end: string; - file_type: string; - file_size: number; - file_extension: string; - file_name?: string; - play_url?: string; - download_url: string; - status: string; - recording_type: string; - }[]; - participant_audio_files?: { - id: string; - recording_start: string; - recording_end: string; - file_type: string; - file_name: string; - file_size: number; - file_extension: string; - play_url?: string; - download_url: string; - file_path?: string; - status: string; - }[]; - }; - }; -}; -type MeetingSharingEndedEvent = Event<"meeting.sharing_ended"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - id: string; - uuid: string; - host_id: string; - topic: string; - type: number; - start_time: string; - timezone?: string; - duration: number; - participant: { - user_id: string; - user_name?: string; - id?: string; - sharing_details: { - content: string; - link_source: string; - file_link: string; - date_time: string; - source: string; - }; - }; - }; - }; -}; -type RecordingCloudStorageUsageUpdatedEvent = Event<"recording.cloud_storage_usage_updated"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - free_storage: string; - plan_storage: string; - plan_type: string; - storage_used: string; - storage_used_percentage: number; - storage_exceed: string; - max_exceed_date?: string; - }; - }; -}; type MeetingParticipantLeftBreakoutRoomEvent = Event<"meeting.participant_left_breakout_room"> & { event: string; event_ts: number; @@ -5492,7 +5736,7 @@ type MeetingParticipantLeftBreakoutRoomEvent = Event<"meeting.participant_left_b breakout_room_uuid: string; host_id: string; topic: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time: string; timezone?: string; duration: number; @@ -5524,9 +5768,9 @@ type MeetingDeviceTestedEvent = Event<"meeting.device_tested"> & { test_result: { user_id: string; user_name: string; - camera_status: number; - speaker_status: number; - microphone_status: number; + camera_status: 0 | 1 | 2; + speaker_status: 0 | 1 | 2; + microphone_status: 0 | 1 | 2; os?: string; }; }; @@ -5558,80 +5802,103 @@ type MeetingSummarySharedEvent = Event<"meeting.summary_shared"> & { }; }; }; -type RecordingTranscriptCompletedEvent = Event<"recording.transcript_completed"> & { +type WebinarChatMessageFileDownloadedEvent = Event<"webinar.chat_message_file_downloaded"> & { event: string; event_ts: number; - download_token?: string; payload: { - account_id: string; + account_id?: string; + operator: string; + operator_id?: string; object: { id: number; uuid: string; + host_account_id: string; + chat_message_file: { + file_id: string; + file_name: string; + file_size: number; + file_type: string; + file_owner_id?: string; + }; + }; + }; +}; +type WebinarDeletedEvent = Event<"webinar.deleted"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id?: string; + operation?: "all" | "single"; + object: { + uuid: string; + id: number; host_id: string; - account_id: string; - topic: string; - type: number; - start_time: string; + topic?: string; + type: 5 | 6 | 9; + start_time?: string; + duration?: number; timezone?: string; - host_email: string; - duration: number; - password?: string; - share_url: string; - total_size: number; - recording_count: number; - recording_files: { - id: string; - meeting_id: string; - recording_start: string; - recording_end: string; - file_type: string; - file_size: number; - file_extension: string; - file_name?: string; - play_url?: string; - download_url: string; - file_path?: string; - status: string; - recording_type: string; + occurrences?: { + occurrence_id: string; + start_time: string; }[]; }; }; }; -type RecordingStoppedEvent = Event<"recording.stopped"> & { +type RecordingRegistrationApprovedEvent = Event<"recording.registration_approved"> & { event: string; event_ts: number; payload: { account_id: string; + operator: string; + operator_id: string; object: { id: number; uuid: string; host_id: string; topic: string; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; start_time: string; timezone?: string; duration: number; - recording_file: { - recording_start: string; - recording_end: string; + registrant: { + id?: string; + email: string; + first_name: string; + last_name: string; }; }; }; }; -type RecordingBatchTrashedEvent = Event<"recording.batch_trashed"> & { +type MeetingRiskAlertEvent = Event<"meeting.risk_alert"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id: string; - operation: string; - object?: { - meeting_uuids: string[]; + object: { + id: number; + uuid: string; + host_id: string; + host_email: string; + topic?: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time?: string; + timezone?: string; + armn_details: { + post_platform?: string; + social_link?: string; + post_time?: string; + post_user?: string; + meeting_url?: string; + recommended_enable_settings?: ("enablePassword" | "enableWaitingRoom" | "enableOnlyAuthenticated" | "enableRegistration" | "enableScreenShareLock" | "enableScreenShareHostOnly" | "enableSpecifiedDomain")[]; + recommended_disable_settings?: ("enableAnnotation" | "enableMeetingChat" | "enableScreenShare" | "enableMultipleShare")[]; + }; }; }; }; -type WebinarStartedEvent = Event<"webinar.started"> & { +type WebinarParticipantFeedbackEvent = Event<"webinar.participant_feedback"> & { event: string; event_ts: number; payload: { @@ -5639,16 +5906,23 @@ type WebinarStartedEvent = Event<"webinar.started"> & { object: { id: string; uuid: string; - host_id: string; - topic: string; - type: number; - start_time: string; - timezone: string; - duration: number; + participant: { + participant_uuid: string; + participant_user_id: string; + user_name: string; + feedback: { + satisfied: boolean; + feedback_details?: { + id: string; + name: string; + }[]; + comments?: string; + }; + }; }; }; }; -type WebinarSharingEndedEvent = Event<"webinar.sharing_ended"> & { +type MeetingParticipantJoinedWaitingRoomEvent = Event<"meeting.participant_joined_waiting_room"> & { event: string; event_ts: number; payload: { @@ -5658,7 +5932,7 @@ type WebinarSharingEndedEvent = Event<"webinar.sharing_ended"> & { uuid: string; host_id: string; topic: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time: string; timezone?: string; duration: number; @@ -5666,201 +5940,210 @@ type WebinarSharingEndedEvent = Event<"webinar.sharing_ended"> & { user_id: string; user_name?: string; id?: string; - sharing_details: { - content: string; - link_source: string; - file_link: string; - date_time: string; - source: string; - }; - }; + participant_uuid?: string; + date_time: string; + email: string; + phone_number?: string; + participant_user_id?: string; + customer_key?: string; + registrant_id?: string; + }; }; }; }; -type MeetingSummaryTrashedEvent = Event<"meeting.summary_trashed"> & { +type WebinarConvertedToMeetingEvent = Event<"webinar.converted_to_meeting"> & { event: string; - event_ts: number; + event_ts?: number; payload: { account_id: string; operator: string; - operator_id: string; + operator_id?: string; object: { - meeting_host_id: string; - meeting_host_email: string; - meeting_uuid: string; - meeting_id: number; - meeting_topic: string; - meeting_start_time: string; - meeting_end_time: string; - summary_start_time: string; - summary_end_time: string; - summary_created_time: string; - summary_last_modified_time: string; - summary_title: string; + uuid: string; + id: number; + host_id: string; + topic?: string; + type: 2 | 3 | 8; + start_time?: string; + duration?: number; + timezone?: string; }; }; }; -type WebinarDeletedEvent = Event<"webinar.deleted"> & { +type MeetingParticipantPhoneCalloutRingingEvent = Event<"meeting.participant_phone_callout_ringing"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id?: string; - operation?: string; object: { - uuid: string; id: number; + uuid: string; host_id: string; - topic?: string; - type: number; - start_time?: string; - duration?: number; - timezone?: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; + participant: { + invitee_name: string; + phone_number: number; + }; }; }; }; -type RecordingRegistrationApprovedEvent = Event<"recording.registration_approved"> & { +type MeetingParticipantJbhJoinedEvent = Event<"meeting.participant_jbh_joined"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id: string; object: { - id: number; + id: string; uuid: string; host_id: string; topic: string; - type: number; - start_time: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time?: string; timezone?: string; duration: number; - registrant: { + participant: { id?: string; - email: string; - first_name: string; - last_name: string; + user_name?: string; + customer_key?: string; + registrant_id?: string; }; }; }; }; -type MeetingSharingStartedEvent = Event<"meeting.sharing_started"> & { +type MeetingInvitationAcceptedEvent = Event<"meeting.invitation_accepted"> & { event: string; event_ts: number; payload: { account_id: string; + operator: string; + operator_id: string; object: { id: string; uuid: string; host_id: string; topic: string; - type: number; - start_time: string; - timezone?: string; - duration: number; participant: { - user_id: string; - user_name?: string; - id?: string; - sharing_details: { - content: string; - link_source: string; - file_link: string; - date_time: string; - source: string; - }; + participant_user_id: string; + email: string; }; }; }; }; -type WebinarRegistrationCreatedEvent = Event<"webinar.registration_created"> & { +type RecordingArchiveFilesCompletedEvent = Event<"recording.archive_files_completed"> & { + event: string; + event_ts: number; + download_token: string; + payload: { + account_id?: string; + object?: { + uuid?: string; + id?: number; + host_id?: string; + topic?: string; + type?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 100; + start_time?: string; + timezone?: string; + duration?: number; + duration_in_second?: number; + total_size?: number; + recording_count?: number; + meeting_type?: "internal" | "external"; + account_name?: string; + complete_time?: string; + is_breakout_room?: boolean; + parent_meeting_id?: string; + archive_files?: { + id?: string; + file_type?: "MP4" | "M4A" | "TRANSCRIPT" | "CHAT" | "CC" | "CHAT_MESSAGE"; + file_extension?: string; + file_name?: string; + file_size?: number; + download_url?: string; + status?: "completed" | "processing" | "failed"; + recording_type?: "shared_screen_with_speaker_view" | "audio_only" | "chat_file" | "closed_caption" | "chat_message"; + individual?: boolean; + participant_email?: string; + participant_join_time?: string; + participant_leave_time?: string; + encryption_fingerprint?: string; + number_of_messages?: number; + storage_location?: "US" | "AU" | "BR" | "CA" | "EU" | "IN" | "JP" | "SG" | "CH"; + }[]; + status?: "completed" | "processing"; + group_id?: string; + }; + }; +}; +type MeetingAlertEvent = Event<"meeting.alert"> & { event: string; event_ts: number; payload: { account_id: string; object: { + id: string; uuid: string; - id: number; host_id: string; topic: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time: string; + timezone?: string; duration: number; - timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; - registrant: { - id: string; - first_name: string; - last_name?: string; - email: string; - address?: string; - city?: string; - country?: string; - zip?: string; - state?: string; - phone?: string; - industry?: string; - org?: string; - job_title?: string; - purchasing_time_frame?: string; - role_in_purchase_process?: string; - no_of_employees?: string; - comments?: string; - custom_questions?: ExactlyOneOf<[ - { - title: string; - value: string; - } - ]>[]; - status: string; - join_url: string; - tracking_source?: { - id: string; - source_name: string; - tracking_url: string; - }; - }; + issues: ("Unstable audio quality" | "Unstable video quality" | "Unstable screen share quality" | "High CPU occupation" | "Call Reconnection")[]; }; }; }; -type MeetingChatMessageSentEvent = Event<"meeting.chat_message_sent"> & { +type MeetingChatMessageFileSentEvent = Event<"meeting.chat_message_file_sent"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id: number; - uuid: string; - chat_message: { + meeting_id: number; + meeting_uuid: string; + chat_message_file: { date_time: string; sender_session_id: string; sender_name: string; sender_email?: string; - sender_type: string; + sender_type: "host" | "guest"; recipient_session_id?: string; recipient_name?: string; recipient_email?: string; - recipient_type: string; + recipient_type: "everyone" | "host" | "guest" | "group"; message_id: string; - message_content: string; - file_ids?: string[]; + file_id: string; + file_name: string; + file_size: number; + file_type: string; + download_url: string; }; }; }; }; -type MeetingParticipantRoleChangedEvent = Event<"meeting.participant_role_changed"> & { +type MeetingDeletedEvent = Event<"meeting.deleted"> & { + event: string; + event_ts?: number; + payload: { + account_id: string; + operator: string; + operator_id?: string; + operation?: "all" | "single"; + object: { + uuid: string; + id: number; + host_id: string; + topic?: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time?: string; + duration?: number; + timezone?: string; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; + }; + }; +}; +type MeetingParticipantJoinedEvent = Event<"meeting.participant_joined"> & { event: string; event_ts: number; payload: { @@ -5870,269 +6153,159 @@ type MeetingParticipantRoleChangedEvent = Event<"meeting.participant_role_change uuid: string; host_id: string; topic?: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time?: string; timezone?: string; duration: number; participant: { user_id: string; user_name: string; + id?: string; + participant_uuid?: string; + join_time: string; email: string; registrant_id?: string; participant_user_id?: string; - participant_uuid?: string; - date_time: string; - old_role: string; - new_role: string; + customer_key?: string; + phone_number?: string; }; }; }; }; -type MeetingRiskAlertEvent = Event<"meeting.risk_alert"> & { +type UserTspDeletedEvent = Event<"user.tsp_deleted"> & { + event?: string; + event_ts?: number; + payload?: { + account_id?: string; + operator?: string; + operator_id?: string; + object?: { + id?: string; + email?: string; + tsp_credentials?: { + conference_code?: string; + leader_pin?: string; + tsp_bridge?: string; + dial_in_numbers?: { + code?: string; + number?: string; + type?: "toll" | "tollfree" | "media_link"; + country_label?: "US_TSP_TB" | "EU_TSP_TB"; + }[]; + }; + }; + }; +}; +type MeetingInvitationDispatchedEvent = Event<"meeting.invitation_dispatched"> & { event: string; event_ts: number; payload: { account_id: string; + operator: string; + operator_id: string; object: { - id: number; + id: string; uuid: string; host_id: string; - host_email: string; - topic?: string; - type: number; - start_time?: string; - timezone?: string; - armn_details: { - post_platform?: string; - social_link?: string; - post_time?: string; - post_user?: string; - meeting_url?: string; - recommended_enable_settings?: string[]; - recommended_disable_settings?: string[]; + topic: string; + participant: { + participant_user_id: string; + email: string; }; }; }; }; -type MeetingPermanentlyDeletedEvent = Event<"meeting.permanently_deleted"> & { +type WebinarEndedEvent = Event<"webinar.ended"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id: string; - operation?: string; object: { + id: string; uuid: string; - id: number; host_id: string; topic: string; - type: number; + type: 5 | 6 | 9; start_time: string; - duration: number; timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; + end_time?: string; + duration: number; + practice_session?: boolean; }; }; }; -type WebinarParticipantFeedbackEvent = Event<"webinar.participant_feedback"> & { +type MeetingConvertedToWebinarEvent = Event<"meeting.converted_to_webinar"> & { event: string; - event_ts: number; + event_ts?: number; payload: { account_id: string; + operator: string; + operator_id?: string; object: { - id: string; uuid: string; - participant: { - participant_uuid: string; - participant_user_id: string; - user_name: string; - feedback: { - satisfied: boolean; - feedback_details?: { - id: string; - name: string; - }[]; - comments?: string; - }; - }; - }; - }; -}; -type RecordingRegistrationCreatedEvent = Event<"recording.registration_created"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { id: number; - uuid: string; - host_id: string; - topic: string; - type: number; - start_time: string; - timezone?: string; - duration: number; - registrant: { - id?: string; - email: string; - status: string; - first_name: string; - last_name: string; - address: string; - city: string; - country: string; - zip: string; - state: string; - phone: string; - industry: string; - org: string; - job_title: string; - purchasing_time_frame: string; - role_in_purchase_process: string; - no_of_employees: string; - comments: string; - custom_questions?: { - title?: string; - value?: string; - }[]; - }; - }; - }; -}; -type MeetingParticipantJoinedWaitingRoomEvent = Event<"meeting.participant_joined_waiting_room"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - id: string; - uuid: string; host_id: string; - topic: string; - type: number; - start_time: string; + topic?: string; + type: 5 | 6 | 9; + start_time?: string; + duration?: number; timezone?: string; - duration: number; - participant: { - user_id: string; - user_name?: string; - id?: string; - participant_uuid?: string; - date_time: string; - email: string; - phone_number?: string; - participant_user_id?: string; - customer_key?: string; - registrant_id?: string; - }; - }; - }; -}; -type MeetingParticipantPhoneCalloutRingingEvent = Event<"meeting.participant_phone_callout_ringing"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - id: number; - uuid: string; - host_id: string; - participant: { - invitee_name: string; - phone_number: number; - }; }; }; }; -type MeetingCreatedEvent = Event<"meeting.created"> & { +type WebinarRecoveredEvent = Event<"webinar.recovered"> & { event: string; event_ts: number; payload: { account_id: string; operator: string; operator_id: string; - operation?: string; + operation?: "all" | "single"; object: { uuid: string; id: number; host_id: string; topic: string; - type: number; - start_time?: string; + type: 5 | 6 | 9; + start_time: string; duration: number; - timezone?: string; - join_url: string; - password?: string; - pmi?: string; + timezone: string; occurrences?: { occurrence_id: string; start_time: string; - duration?: number; - status?: string; - }[]; - settings: { - use_pmi: boolean; - alternative_hosts: string; - meeting_invitees?: { - email?: string; - }[]; - join_before_host?: boolean; - jbh_time?: number; - }; - recurrence?: { - type?: number; - repeat_interval?: number; - weekly_days?: string; - monthly_day?: number; - monthly_week_day?: number; - end_times?: number; - end_date_time?: string; - monthly_week?: number; - }; - tracking_fields?: { - field?: string; - value?: string; - visible?: boolean; }[]; }; }; }; -type MeetingLiveStreamingStartedEvent = Event<"meeting.live_streaming_started"> & { +type WebinarParticipantRoleChangedEvent = Event<"webinar.participant_role_changed"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id: string; object: { - id: number; + id: string; uuid: string; host_id: string; topic: string; - type: number; + type: 5 | 6 | 9; start_time: string; - timezone?: string; + timezone: string; duration: number; - live_streaming: { - service: string; - custom_live_streaming_settings?: { - stream_url: string; - stream_key: string; - page_url: string; - resolution?: string; - }; + participant: { + user_id: string; + user_name: string; + email: string; + registrant_id?: string; + participant_user_id?: string; + participant_uuid?: string; date_time: string; + old_role: "host" | "co-host" | "attendee"; + new_role: "host" | "co-host" | "attendee"; }; }; }; }; -type MeetingParticipantRoomSystemCalloutAcceptedEvent = Event<"meeting.participant_room_system_callout_accepted"> & { +type MeetingParticipantJbhWaitingLeftEvent = Event<"meeting.participant_jbh_waiting_left"> & { event: string; event_ts: number; payload: { @@ -6141,121 +6314,45 @@ type MeetingParticipantRoomSystemCalloutAcceptedEvent = Event<"meeting.participa id: number; uuid: string; host_id: string; - message_id: string; - inviter_name: string; - participant: { - call_type: string; - device_ip: string; - }; - }; - }; -}; -type WebinarRegistrationApprovedEvent = Event<"webinar.registration_approved"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - operator: string; - operator_id: string; - object: { - uuid: string; - id: number; - host_id: string; topic: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time: string; - duration: number; - timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; - registrant: { - id: string; - first_name: string; - last_name?: string; - email: string; - join_url: string; - tracking_source?: { - id: string; - source_name: string; - tracking_url: string; - }; - }; - }; - }; -}; -type MeetingParticipantJbhJoinedEvent = Event<"meeting.participant_jbh_joined"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - id: string; - uuid: string; - host_id: string; - topic: string; - type: number; - start_time?: string; timezone?: string; duration: number; participant: { id?: string; - user_name?: string; + user_name: string; customer_key?: string; registrant_id?: string; }; }; }; }; -type RecordingArchiveFilesCompletedEvent = Event<"recording.archive_files_completed"> & { - event: string; - event_ts: number; - download_token: string; - payload: { +type UserTspCreatedEvent = Event<"user.tsp_created"> & { + event?: string; + event_ts?: number; + payload?: { account_id?: string; + operator?: string; + operator_id?: string; object?: { - uuid?: string; - id?: number; - host_id?: string; - topic?: string; - type?: number; - start_time?: string; - timezone?: string; - duration?: number; - duration_in_second?: number; - total_size?: number; - recording_count?: number; - meeting_type?: string; - account_name?: string; - complete_time?: string; - is_breakout_room?: boolean; - parent_meeting_id?: string; - archive_files?: { - id?: string; - file_type?: string; - file_extension?: string; - file_name?: string; - file_size?: number; - download_url?: string; - status?: string; - recording_type?: string; - individual?: boolean; - participant_email?: string; - participant_join_time?: string; - participant_leave_time?: string; - encryption_fingerprint?: string; - number_of_messages?: number; - storage_location?: string; - }[]; - status?: string; - group_id?: string; + id?: string; + email?: string; + tsp_credentials?: { + conference_code?: string; + leader_pin?: string; + tsp_bridge?: string; + dial_in_numbers?: { + code?: string; + number?: string; + type?: "toll" | "tollfree" | "media_link"; + country_label?: "US_TSP_TB" | "EU_TSP_TB"; + }[]; + }; }; }; }; -type MeetingBreakoutRoomSharingStartedEvent = Event<"meeting.breakout_room_sharing_started"> & { +type MeetingBreakoutRoomSharingEndedEvent = Event<"meeting.breakout_room_sharing_ended"> & { event: string; event_ts: number; payload: { @@ -6266,7 +6363,7 @@ type MeetingBreakoutRoomSharingStartedEvent = Event<"meeting.breakout_room_shari breakout_room_uuid: string; host_id: string; topic: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time: string; timezone?: string; duration: number; @@ -6276,52 +6373,399 @@ type MeetingBreakoutRoomSharingStartedEvent = Event<"meeting.breakout_room_shari user_name?: string; id?: string; sharing_details: { - content: string; - link_source: string; + content: "application" | "whiteboard" | "desktop" | "airplay" | "camera" | "unknown"; + link_source: "" | "deep_link" | "in_meeting"; file_link: string; date_time: string; - source: string; + source: "" | "dropbox"; }; }; }; }; }; -type MeetingAlertEvent = Event<"meeting.alert"> & { +type MeetingUpdatedEvent = Event<"meeting.updated"> & { event: string; event_ts: number; payload: { account_id: string; + operator: string; + operator_id: string; + scope?: "single" | "all"; object: { - id: string; - uuid: string; - host_id: string; - topic: string; - type: number; - start_time: string; + id: number; + uuid?: string; + host_id?: string; + topic?: string; + type?: 0 | 1 | 2 | 3 | 4 | 7 | 8 | 10; + start_time?: string; + duration?: number; timezone?: string; - duration: number; - issues: string[]; - }; - }; + join_url?: string; + password?: string; + agenda?: string; + registration_url?: string; + occurrences?: { + occurrence_id: string; + start_time: string; + duration?: number; + status?: "available" | "deleted"; + }[]; + settings?: { + host_video?: boolean; + participant_video?: boolean; + join_before_host?: boolean; + jbh_time?: 0 | 5 | 10; + mute_upon_entry?: boolean; + audio?: "telephony" | "voip" | "both"; + auto_recording?: "local" | "cloud" | "none"; + use_pmi?: boolean; + waiting_room?: boolean; + watermark?: boolean; + enforce_login?: boolean; + enforce_login_domains?: string; + approval_type?: 0 | 1 | 2; + registration_type?: 1 | 2 | 3; + alternative_hosts?: string; + meeting_authentication?: boolean; + authentication_option?: string; + authentication_name?: string; + authentication_domains?: string; + meeting_invitees?: { + email?: string; + }[]; + language_interpretation?: { + enable: boolean; + interpreters?: { + email?: string; + languages?: string; + interpreter_languages?: string; + }[]; + }; + sign_language_interpretation?: { + enable: boolean; + interpreters?: { + email?: string; + sign_language?: string; + }[]; + }; + continuous_meeting_chat?: { + enable?: boolean; + auto_add_invited_external_users?: boolean; + auto_add_meeting_participants?: boolean; + channel_id?: string; + }; + auto_start_meeting_summary?: boolean; + who_will_receive_summary?: 1 | 2 | 3 | 4; + auto_start_ai_companion_questions?: boolean; + who_can_ask_questions?: 1 | 2 | 3 | 4 | 5; + summary_template_id?: string; + allow_host_control_participant_mute_state?: boolean; + email_in_attendee_report?: boolean; + }; + recurrence?: { + type?: 1 | 2 | 3; + repeat_interval?: number; + weekly_days?: "1" | "2" | "3" | "4" | "5" | "6" | "7"; + monthly_day?: number; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; + end_times?: number; + end_date_time?: string; + monthly_week?: -1 | 1 | 2 | 3 | 4; + }; + tracking_fields?: { + field?: string; + value?: string; + }[]; + }; + time_stamp: number; + old_object: { + id: number; + uuid?: string; + host_id?: string; + topic?: string; + type?: 0 | 1 | 2 | 3 | 4 | 7 | 8 | 10; + start_time?: string; + duration?: number; + timezone?: string; + join_url?: string; + password?: string; + agenda?: string; + registration_url?: string; + occurrences?: { + occurrence_id: string; + start_time: string; + duration?: number; + status?: "available" | "deleted"; + }[]; + settings?: { + host_video?: boolean; + participant_video?: boolean; + join_before_host?: boolean; + jbh_time?: 0 | 5 | 10; + mute_upon_entry?: boolean; + audio?: "telephony" | "voip" | "both"; + auto_recording?: "local" | "cloud" | "none"; + use_pmi?: boolean; + waiting_room?: boolean; + watermark?: boolean; + enforce_login?: boolean; + enforce_login_domains?: string; + approval_type?: 0 | 1 | 2; + registration_type?: 1 | 2 | 3; + alternative_hosts?: string; + meeting_authentication?: boolean; + authentication_option?: string; + authentication_name?: string; + authentication_domains?: string; + meeting_invitees?: { + email?: string; + }[]; + language_interpretation?: { + enable: boolean; + interpreters?: { + email?: string; + languages?: string; + interpreter_languages?: string; + }[]; + }; + sign_language_interpretation?: { + enable: boolean; + interpreters?: { + email?: string; + sign_language?: string; + }[]; + }; + continuous_meeting_chat?: { + enable?: boolean; + auto_add_invited_external_users?: boolean; + auto_add_meeting_participants?: boolean; + channel_id?: string; + }; + auto_start_meeting_summary?: boolean; + who_will_receive_summary?: 1 | 2 | 3 | 4; + auto_start_ai_companion_questions?: boolean; + who_can_ask_questions?: 1 | 2 | 3 | 4 | 5; + summary_template_id?: string; + allow_host_control_participant_mute_state?: boolean; + email_in_attendee_report?: boolean; + }; + recurrence?: { + type?: 1 | 2 | 3; + repeat_interval?: number; + weekly_days?: "1" | "2" | "3" | "4" | "5" | "6" | "7"; + monthly_day?: number; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; + end_times?: number; + end_date_time?: string; + monthly_week?: -1 | 1 | 2 | 3 | 4; + }; + tracking_fields?: { + field?: string; + value?: string; + }[]; + }; + }; }; -type MeetingChatMessageFileSentEvent = Event<"meeting.chat_message_file_sent"> & { +type MeetingRegistrationDeniedEvent = Event<"meeting.registration_denied"> & { event: string; event_ts: number; payload: { account_id: string; + operator: string; + operator_id: string; object: { - meeting_id: number; - meeting_uuid: string; + uuid: string; + id: number; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + duration: number; + timezone: string; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; + registrant: { + id: string; + first_name: string; + last_name?: string; + email: string; + }; + }; + }; +}; +type WebinarRegistrationDeniedEvent = Event<"webinar.registration_denied"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id: string; + object: { + uuid: string; + id: number; + host_id: string; + topic: string; + type: 5 | 6 | 9; + start_time: string; + duration: number; + timezone: string; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; + registrant: { + id: string; + first_name: string; + last_name?: string; + email: string; + tracking_source?: { + id: string; + source_name: string; + tracking_url: string; + }; + }; + }; + }; +}; +type MeetingRegistrationApprovedEvent = Event<"meeting.registration_approved"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id: string; + object: { + uuid: string; + id: number; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + duration: number; + timezone: string; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; + registrant: { + id: string; + first_name: string; + last_name?: string; + email: string; + join_url: string; + participant_pin_code?: number; + }; + }; + }; +}; +type WebinarParticipantLeftEvent = Event<"webinar.participant_left"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + id: string; + uuid: string; + host_id: string; + topic: string; + type: 5 | 6 | 9; + start_time: string; + timezone: string; + duration: number; + participant: { + user_id: string; + user_name?: string; + id?: string; + leave_time?: string; + leave_reason?: string; + email: string; + registrant_id?: string; + participant_user_id?: string; + participant_uuid?: string; + customer_key?: string; + phone_number?: string; + }; + }; + }; +}; +type RecordingBatchDeletedEvent = Event<"recording.batch_deleted"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id: string; + object?: { + meetings: { + meeting_uuid?: string; + recording_file_ids?: string[]; + }[]; + }; + }; +}; +type WebinarAlertEvent = Event<"webinar.alert"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + id: string; + uuid: string; + host_id: string; + topic: string; + type: 5 | 6 | 9; + start_time: string; + timezone: string; + duration: number; + issues: ("Unstable audio quality" | "Unstable video quality" | "Unstable screen share quality" | "High CPU occupation" | "Call Reconnection")[]; + }; + }; +}; +type WebinarChatMessageSentEvent = Event<"webinar.chat_message_sent"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + id: number; + uuid: string; + chat_message: { + date_time: string; + sender_session_id: string; + sender_name: string; + sender_email?: string; + sender_type: "host" | "alternative-host" | "panelist" | "guest"; + recipient_session_id?: string; + recipient_name?: string; + recipient_email?: string; + recipient_type: "everyone" | "host" | "guest" | "group"; + message_id: string; + message_content: string; + file_ids?: string[]; + }; + }; + }; +}; +type WebinarChatMessageFileSentEvent = Event<"webinar.chat_message_file_sent"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + webinar_id: number; + webinar_uuid: string; chat_message_file: { date_time: string; sender_session_id: string; sender_name: string; sender_email?: string; - sender_type: string; + sender_type: "host" | "guest"; recipient_session_id?: string; recipient_name?: string; recipient_email?: string; - recipient_type: string; + recipient_type: "everyone" | "host" | "guest" | "group"; message_id: string; file_id: string; file_name: string; @@ -6332,140 +6776,301 @@ type MeetingChatMessageFileSentEvent = Event<"meeting.chat_message_file_sent"> & }; }; }; -type MeetingParticipantBindEvent = Event<"meeting.participant_bind"> & { +type MeetingEndedEvent = Event<"meeting.ended"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id?: string; + id: string; uuid: string; host_id: string; - topic?: string; - type: number; - start_time?: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; timezone?: string; - duration?: number; + duration: number; + end_time: string; + }; + }; +}; +type MeetingParticipantJoinedBreakoutRoomEvent = Event<"meeting.participant_joined_breakout_room"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + id: string; + uuid: string; + breakout_room_uuid: string; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + timezone?: string; + duration: number; participant: { user_id: string; - bind_user_id: string; - user_name?: string; + parent_user_id?: string; + user_name: string; id?: string; participant_uuid?: string; - bind_participant_uuid?: string; join_time: string; + email: string; registrant_id?: string; - phone_number: string; + participant_user_id?: string; + phone_number?: string; + customer_key?: string; }; }; }; }; -type MeetingDeletedEvent = Event<"meeting.deleted"> & { +type MeetingParticipantLeftWaitingRoomEvent = Event<"meeting.participant_left_waiting_room"> & { event: string; - event_ts?: number; + event_ts: number; + payload: { + account_id: string; + object: { + id: string; + uuid: string; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + timezone?: string; + duration: number; + participant: { + user_id: string; + user_name?: string; + id?: string; + participant_uuid?: string; + date_time: string; + email: string; + phone_number?: string; + participant_user_id?: string; + customer_key?: string; + registrant_id?: string; + }; + }; + }; +}; +type MeetingStartedEvent = Event<"meeting.started"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + id: string; + uuid: string; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + timezone?: string; + duration: number; + }; + }; +}; +type MeetingRegistrationCancelledEvent = Event<"meeting.registration_cancelled"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id?: string; + object: { + uuid: string; + id: number; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + duration: number; + timezone: string; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; + registrant: { + id: string; + first_name: string; + last_name?: string; + email: string; + }; + }; + }; +}; +type MeetingSummaryCompletedEvent = Event<"meeting.summary_completed"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + meeting_host_id: string; + meeting_host_email: string; + meeting_uuid: string; + meeting_id: number; + meeting_topic: string; + meeting_start_time: string; + meeting_end_time: string; + summary_start_time: string; + summary_end_time: string; + summary_created_time: string; + summary_last_modified_time: string; + summary_title: string; + summary_overview?: string; + summary_details: { + label: string; + summary: string; + }[]; + next_steps: string[]; + summary_content?: string; + summary_doc_url?: string; + }; + }; +}; +type MeetingParticipantLeftEvent = Event<"meeting.participant_left"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + id: string; + uuid: string; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + timezone?: string; + duration: number; + participant: { + user_id: string; + user_name?: string; + id?: string; + participant_uuid?: string; + leave_time: string; + leave_reason?: string; + email: string; + registrant_id?: string; + participant_user_id?: string; + customer_key?: string; + phone_number?: string; + }; + }; + }; +}; +type MeetingParticipantPhoneCalloutRejectedEvent = Event<"meeting.participant_phone_callout_rejected"> & { + event: string; + event_ts: number; payload: { account_id: string; - operator: string; - operator_id?: string; - operation?: string; object: { - uuid: string; id: number; + uuid: string; host_id: string; - topic?: string; - type: number; - start_time?: string; - duration?: number; - timezone?: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; + participant: { + invitee_name: string; + phone_number: number; + }; }; }; }; -type MeetingParticipantFeedbackEvent = Event<"meeting.participant_feedback"> & { +type MeetingParticipantPhoneCalloutAcceptedEvent = Event<"meeting.participant_phone_callout_accepted"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id: string; + id: number; uuid: string; + host_id: string; participant: { - participant_uuid: string; - participant_user_id: string; - user_name: string; - feedback: { - satisfied: boolean; - feedback_details?: { - id: string; - name: string; - }[]; - comments?: string; - }; + invitee_name: string; + phone_number: number; }; }; }; }; -type MeetingParticipantJoinedEvent = Event<"meeting.participant_joined"> & { +type WebinarSharingStartedEvent = Event<"webinar.sharing_started"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id?: string; + id: string; uuid: string; host_id: string; - topic?: string; - type: number; - start_time?: string; + topic: string; + type: 5 | 6 | 9; + start_time: string; timezone?: string; duration: number; participant: { user_id: string; - user_name: string; + user_name?: string; id?: string; - participant_uuid?: string; - join_time: string; - email: string; - registrant_id?: string; - participant_user_id?: string; - customer_key?: string; - phone_number?: string; + sharing_details: { + content: "application" | "whiteboard" | "desktop" | "airplay" | "camera" | "unknown"; + link_source: "" | "deep_link" | "in_meeting"; + file_link: string; + date_time: string; + source: "" | "dropbox"; + }; }; }; }; }; -type UserTspDeletedEvent = Event<"user.tsp_deleted"> & { - event?: string; - event_ts?: number; - payload?: { - account_id?: string; - operator?: string; - operator_id?: string; - object?: { - id?: string; - email?: string; - tsp_credentials?: { - conference_code?: string; - leader_pin?: string; - tsp_bridge?: string; - dial_in_numbers?: { - code?: string; - number?: string; - type?: string; - country_label?: string; +type MeetingRegistrationCreatedEvent = Event<"meeting.registration_created"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + uuid: string; + id: number; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + duration: number; + timezone: string; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; + registrant: { + id: string; + first_name: string; + last_name?: string; + email: string; + address?: string; + city?: string; + country?: string; + zip?: string; + state?: string; + phone?: string; + industry?: string; + org?: string; + job_title?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-250" | "251-500" | "501-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; + comments?: string; + custom_questions?: { + title: string; + value: string; }[]; + status: "approved" | "pending"; + join_url: string; + participant_pin_code?: number; }; }; }; }; -type RecordingDeletedEvent = Event<"recording.deleted"> & { +type RecordingRecoveredEvent = Event<"recording.recovered"> & { event: string; event_ts: number; + download_token?: string; payload: { account_id: string; operator: string; @@ -6474,10 +7079,10 @@ type RecordingDeletedEvent = Event<"recording.deleted"> & { id: number; uuid: string; host_id: string; - account_id: string; topic: string; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; start_time: string; + account_id: string; timezone?: string; duration: number; share_url: string; @@ -6488,14 +7093,14 @@ type RecordingDeletedEvent = Event<"recording.deleted"> & { meeting_id: string; recording_start: string; recording_end: string; - file_type: string; + file_type: "MP4" | "M4A" | "CHAT" | "TRANSCRIPT" | "CSV" | "TB" | "CC" | "CHAT_MESSAGE" | "SUMMARY" | "TIMELINE"; file_size: number; - file_extension: string; + file_extension: "MP4" | "M4A" | "TXT" | "VTT" | "CSV" | "JSON" | "JPG"; file_name?: string; play_url?: string; download_url: string; - status: string; - recording_type: string; + status: "completed" | "processing"; + recording_type: "shared_screen_with_speaker_view(CC)" | "shared_screen_with_speaker_view" | "shared_screen_with_gallery_view" | "gallery_view" | "shared_screen" | "audio_only" | "audio_transcript" | "chat_file" | "active_speaker" | "host_video" | "audio_only_each_participant" | "cc_transcript" | "closed_caption" | "poll" | "timeline" | "thumbnail" | "audio_interpretation" | "summary" | "summary_next_steps" | "summary_smart_chapters" | "sign_interpretation" | "production_sutdio"; }[]; participant_audio_files?: { id: string; @@ -6508,140 +7113,130 @@ type RecordingDeletedEvent = Event<"recording.deleted"> & { play_url?: string; download_url: string; file_path?: string; - status: string; + status: "completed" | "processing"; }[]; }; }; }; -type RecordingPausedEvent = Event<"recording.paused"> & { +type MeetingSharingEndedEvent = Event<"meeting.sharing_ended"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id: number; + id: string; uuid: string; host_id: string; topic: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time: string; timezone?: string; duration: number; - recording_file: { - recording_start: string; - recording_end: string; + participant: { + user_id: string; + user_name?: string; + id?: string; + sharing_details: { + content: "application" | "whiteboard" | "desktop" | "airplay" | "camera" | "unknown"; + link_source: "" | "deep_link" | "in_meeting"; + file_link: string; + date_time: string; + source: "" | "dropbox"; + }; }; }; }; }; -type MeetingParticipantRoomSystemCalloutRingingEvent = Event<"meeting.participant_room_system_callout_ringing"> & { +type RecordingCloudStorageUsageUpdatedEvent = Event<"recording.cloud_storage_usage_updated"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id: number; - uuid: string; - host_id: string; - message_id: string; - inviter_name: string; - participant: { - call_type: string; - device_ip: string; - }; + free_storage: string; + plan_storage: string; + plan_type: string; + storage_used: string; + storage_used_percentage: number; + storage_exceed: string; + max_exceed_date?: string; }; }; }; -type RecordingStartedEvent = Event<"recording.started"> & { +type RecordingTranscriptCompletedEvent = Event<"recording.transcript_completed"> & { event: string; event_ts: number; + download_token?: string; payload: { account_id: string; object: { id: number; uuid: string; host_id: string; + account_id: string; topic: string; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; start_time: string; timezone?: string; + host_email: string; duration: number; - recording_file: { + password?: string; + share_url: string; + total_size: number; + recording_count: number; + recording_files: { + id: string; + meeting_id: string; recording_start: string; recording_end: string; - }; - }; - }; -}; -type UserTspUpdatedEvent = Event<"user.tsp_updated"> & { - event?: string; - event_ts?: number; - payload?: { - account_id?: string; - operator?: string; - operator_id?: string; - object?: { - id?: string; - email?: string; - tsp_credentials?: { - conference_code?: string; - leader_pin?: string; - tsp_bridge?: string; - dial_in_numbers?: { - code?: string; - number?: string; - type?: string; - country_label?: string; - }[]; - }; + file_type: string; + file_size: number; + file_extension: string; + file_name?: string; + play_url?: string; + download_url: string; + file_path?: string; + status: "completed" | "processing"; + recording_type: "shared_screen_with_speaker_view(CC)" | "shared_screen_with_speaker_view" | "shared_screen_with_gallery_view" | "gallery_view" | "shared_screen" | "audio_only" | "audio_transcript" | "chat_file" | "active_speaker" | "host_video" | "audio_only_each_participant" | "cc_transcript" | "closed_caption" | "poll" | "timeline" | "thumbnail"; + }[]; }; }; }; -type WebinarEndedEvent = Event<"webinar.ended"> & { +type RecordingStoppedEvent = Event<"recording.stopped"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id: string; + id: number; uuid: string; host_id: string; topic: string; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; start_time: string; - timezone: string; - end_time?: string; + timezone?: string; duration: number; + recording_file: { + recording_start: string; + recording_end: string; + }; }; }; }; -type WebinarRecoveredEvent = Event<"webinar.recovered"> & { +type RecordingBatchTrashedEvent = Event<"recording.batch_trashed"> & { event: string; event_ts: number; payload: { account_id: string; operator: string; operator_id: string; - operation?: string; - object: { - uuid: string; - id: number; - host_id: string; - topic: string; - type: number; - start_time: string; - duration: number; - timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; + operation: "trash_user_recordings" | "trash_account_recordings"; + object?: { + meeting_uuids: string[]; }; }; }; -type MeetingParticipantAdmittedEvent = Event<"meeting.participant_admitted"> & { +type WebinarStartedEvent = Event<"webinar.started"> & { event: string; event_ts: number; payload: { @@ -6651,64 +7246,35 @@ type MeetingParticipantAdmittedEvent = Event<"meeting.participant_admitted"> & { uuid: string; host_id: string; topic: string; - type: number; + type: 5 | 6 | 9; start_time: string; - timezone?: string; + timezone: string; duration: number; - participant: { - user_id: string; - user_name?: string; - id?: string; - participant_uuid?: string; - date_time: string; - email: string; - customer_key?: string; - registrant_id?: string; - }; }; }; }; -type MeetingParticipantRoomSystemCalloutFailedEvent = Event<"meeting.participant_room_system_callout_failed"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - id: number; - uuid: string; - host_id: string; - message_id: string; - inviter_name: string; - reason_type: number; - participant: { - call_type: string; - device_ip: string; - }; - }; - }; -}; -type RecordingResumedEvent = Event<"recording.resumed"> & { +type MeetingChatMessageFileDownloadedEvent = Event<"meeting.chat_message_file_downloaded"> & { event: string; event_ts: number; payload: { - account_id: string; + account_id?: string; + operator: string; + operator_id?: string; object: { id: number; uuid: string; - host_id: string; - topic: string; - type: number; - start_time: string; - timezone?: string; - duration: number; - recording_file: { - recording_start: string; - recording_end: string; + host_account_id: string; + chat_message_file: { + file_id: string; + file_name: string; + file_size: number; + file_type: string; + file_owner_id?: string; }; }; }; }; -type WebinarParticipantRoleChangedEvent = Event<"webinar.participant_role_changed"> & { +type WebinarSharingEndedEvent = Event<"webinar.sharing_ended"> & { event: string; event_ts: number; payload: { @@ -6718,41 +7284,26 @@ type WebinarParticipantRoleChangedEvent = Event<"webinar.participant_role_change uuid: string; host_id: string; topic: string; - type: number; + type: 5 | 6 | 9; start_time: string; - timezone: string; + timezone?: string; duration: number; participant: { user_id: string; - user_name: string; - email: string; - registrant_id?: string; - participant_user_id?: string; - participant_uuid?: string; - date_time: string; - old_role: string; - new_role: string; - }; - }; - }; -}; -type MeetingParticipantPhoneCalloutMissedEvent = Event<"meeting.participant_phone_callout_missed"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - id: number; - uuid: string; - host_id: string; - participant: { - invitee_name: string; - phone_number: number; + user_name?: string; + id?: string; + sharing_details: { + content: "application" | "whiteboard" | "desktop" | "airplay" | "camera" | "unknown"; + link_source: "" | "deep_link" | "in_meeting"; + file_link: string; + date_time: string; + source: "" | "dropbox"; + }; }; }; }; }; -type MeetingSummaryUpdatedEvent = Event<"meeting.summary_updated"> & { +type MeetingSummaryTrashedEvent = Event<"meeting.summary_trashed"> & { event: string; event_ts: number; payload: { @@ -6772,20 +7323,10 @@ type MeetingSummaryUpdatedEvent = Event<"meeting.summary_updated"> & { summary_created_time: string; summary_last_modified_time: string; summary_title: string; - summary_overview?: string; - summary_details: { - label: string; - summary: string; - }[]; - next_steps: string[]; - edited_summary: { - summary_details?: string; - next_steps?: string[]; - }; }; }; }; -type WebinarParticipantJoinedEvent = Event<"webinar.participant_joined"> & { +type MeetingSharingStartedEvent = Event<"meeting.sharing_started"> & { event: string; event_ts: number; payload: { @@ -6795,50 +7336,77 @@ type WebinarParticipantJoinedEvent = Event<"webinar.participant_joined"> & { uuid: string; host_id: string; topic: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time: string; - timezone: string; + timezone?: string; duration: number; participant: { user_id: string; - user_name: string; - id: string; - join_time: string; - email: string; - registrant_id?: string; - participant_user_id?: string; - participant_uuid?: string; - customer_key?: string; - phone_number?: string; + user_name?: string; + id?: string; + sharing_details: { + content: "application" | "whiteboard" | "desktop" | "airplay" | "camera" | "unknown"; + link_source: "" | "deep_link" | "in_meeting"; + file_link: string; + date_time: string; + source: "" | "dropbox"; + }; }; }; }; }; -type RecordingRenamedEvent = Event<"recording.renamed"> & { +type WebinarRegistrationCreatedEvent = Event<"webinar.registration_created"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id: string; - time_stamp: number; object: { uuid: string; id: number; - topic: string; - type: number; host_id: string; - }; - old_object: { - uuid: string; - id: number; topic: string; - type: number; - host_id: string; + type: 5 | 6 | 9; + start_time: string; + duration: number; + timezone: string; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; + registrant: { + id: string; + first_name: string; + last_name?: string; + email: string; + address?: string; + city?: string; + country?: string; + zip?: string; + state?: string; + phone?: string; + industry?: string; + org?: string; + job_title?: string; + purchasing_time_frame?: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process?: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; + no_of_employees?: "" | "1-20" | "21-50" | "51-100" | "101-250" | "251-500" | "501-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; + comments?: string; + custom_questions?: { + title: string; + value: string; + }[]; + status: "approved" | "pending"; + join_url: string; + tracking_source?: { + id: string; + source_name: string; + tracking_url: string; + }; + }; }; }; }; -type MeetingParticipantJbhWaitingLeftEvent = Event<"meeting.participant_jbh_waiting_left"> & { +type MeetingChatMessageSentEvent = Event<"meeting.chat_message_sent"> & { event: string; event_ts: number; payload: { @@ -6846,246 +7414,218 @@ type MeetingParticipantJbhWaitingLeftEvent = Event<"meeting.participant_jbh_wait object: { id: number; uuid: string; - host_id: string; - topic: string; - type: number; - start_time: string; - timezone?: string; - duration: number; - participant: { - id?: string; - user_name: string; - customer_key?: string; - registrant_id?: string; + chat_message: { + date_time: string; + sender_session_id: string; + sender_name: string; + sender_email?: string; + sender_type: "host" | "guest"; + recipient_session_id?: string; + recipient_name?: string; + recipient_email?: string; + recipient_type: "everyone" | "host" | "guest" | "group"; + message_id: string; + message_content: string; + file_ids?: string[]; }; }; }; }; -type UserTspCreatedEvent = Event<"user.tsp_created"> & { - event?: string; - event_ts?: number; - payload?: { - account_id?: string; - operator?: string; - operator_id?: string; - object?: { - id?: string; - email?: string; - tsp_credentials?: { - conference_code?: string; - leader_pin?: string; - tsp_bridge?: string; - dial_in_numbers?: { - code?: string; - number?: string; - type?: string; - country_label?: string; - }[]; +type MeetingInvitationRejectedEvent = Event<"meeting.invitation_rejected"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id: string; + object: { + id: string; + uuid: string; + host_id: string; + topic: string; + participant: { + participant_user_id: string; + email: string; }; }; }; }; -type MeetingBreakoutRoomSharingEndedEvent = Event<"meeting.breakout_room_sharing_ended"> & { +type MeetingParticipantRoleChangedEvent = Event<"meeting.participant_role_changed"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id: string; + id?: string; uuid: string; - breakout_room_uuid: string; host_id: string; - topic: string; - type: number; - start_time: string; + topic?: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time?: string; timezone?: string; duration: number; participant: { user_id: string; - parent_user_id?: string; - user_name?: string; - id?: string; - sharing_details: { - content: string; - link_source: string; - file_link: string; - date_time: string; - source: string; - }; + user_name: string; + email: string; + registrant_id?: string; + participant_user_id?: string; + participant_uuid?: string; + date_time: string; + old_role: "host" | "co-host" | "attendee"; + new_role: "host" | "co-host" | "attendee"; }; }; }; }; -type MeetingUpdatedEvent = Event<"meeting.updated"> & { +type MeetingPermanentlyDeletedEvent = Event<"meeting.permanently_deleted"> & { event: string; event_ts: number; payload: { account_id: string; operator: string; operator_id: string; - scope?: string; + operation?: "all" | "single"; object: { + uuid: string; id: number; - uuid?: string; - host_id?: string; - topic?: string; - type?: number; - start_time?: string; - duration?: number; - timezone?: string; - join_url?: string; - password?: string; - agenda?: string; - registration_url?: string; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + duration: number; + timezone: string; occurrences?: { occurrence_id: string; start_time: string; - duration?: number; - status?: string; }[]; - settings?: { - host_video?: boolean; - participant_video?: boolean; - join_before_host?: boolean; - jbh_time?: number; - mute_upon_entry?: boolean; - audio?: string; - auto_recording?: string; - use_pmi?: boolean; - waiting_room?: boolean; - watermark?: boolean; - enforce_login?: boolean; - enforce_login_domains?: string; - approval_type?: number; - registration_type?: number; - alternative_hosts?: string; - meeting_authentication?: boolean; - authentication_option?: string; - authentication_name?: string; - authentication_domains?: string; - meeting_invitees?: { - email?: string; + }; + }; +}; +type RecordingRegistrationCreatedEvent = Event<"recording.registration_created"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + id: number; + uuid: string; + host_id: string; + topic: string; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; + start_time: string; + timezone?: string; + duration: number; + registrant: { + id?: string; + email: string; + status: "approved" | "denied" | "pending" | "all"; + first_name: string; + last_name: string; + address: string; + city: string; + country: string; + zip: string; + state: string; + phone: string; + industry: string; + org: string; + job_title: string; + purchasing_time_frame: "" | "Within a month" | "1-3 months" | "4-6 months" | "More than 6 months" | "No timeframe"; + role_in_purchase_process: "" | "Decision Maker" | "Evaluator/Recommender" | "Influencer" | "Not involved"; + no_of_employees: "" | "1-20" | "21-50" | "51-100" | "101-250" | "251-500" | "501-1,000" | "1,001-5,000" | "5,001-10,000" | "More than 10,000"; + comments: string; + custom_questions?: { + title?: string; + value?: string; }[]; - language_interpretation?: { - enable: boolean; - interpreters?: { - email?: string; - languages?: string; - }[]; - }; - sign_language_interpretation?: { - enable: boolean; - interpreters?: { - email?: string; - sign_language?: string; - }[]; - }; - continuous_meeting_chat?: { - enable?: boolean; - auto_add_invited_external_users?: boolean; - auto_add_meeting_participants?: boolean; - }; - auto_start_meeting_summary?: boolean; - auto_start_ai_companion_questions?: boolean; - }; - recurrence?: { - type?: number; - repeat_interval?: number; - weekly_days?: string; - monthly_day?: number; - monthly_week_day?: number; - end_times?: number; - end_date_time?: string; - monthly_week?: number; }; - tracking_fields?: { - field?: string; - value?: string; - }[]; }; - time_stamp: number; - old_object: { + }; +}; +type MeetingCreatedEvent = Event<"meeting.created"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id: string; + operation?: "all" | "single"; + object: { + uuid: string; id: number; - uuid?: string; - host_id?: string; - topic?: string; - type?: number; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8 | 10; start_time?: string; - duration?: number; + duration: number; timezone?: string; - join_url?: string; + join_url: string; password?: string; - agenda?: string; - registration_url?: string; + pmi?: string; occurrences?: { occurrence_id: string; start_time: string; duration?: number; - status?: string; + status?: "available" | "deleted"; }[]; - settings?: { - host_video?: boolean; - participant_video?: boolean; - join_before_host?: boolean; - jbh_time?: number; - mute_upon_entry?: boolean; - audio?: string; - auto_recording?: string; - use_pmi?: boolean; - waiting_room?: boolean; - watermark?: boolean; - enforce_login?: boolean; - enforce_login_domains?: string; - approval_type?: number; - registration_type?: number; - alternative_hosts?: string; - meeting_authentication?: boolean; - authentication_option?: string; - authentication_name?: string; - authentication_domains?: string; + settings: { + use_pmi: boolean; + alternative_hosts: string; meeting_invitees?: { email?: string; }[]; - language_interpretation?: { - enable: boolean; - interpreters?: { - email?: string; - languages?: string; - }[]; - }; - sign_language_interpretation?: { - enable: boolean; - interpreters?: { - email?: string; - sign_language?: string; - }[]; - }; - continuous_meeting_chat?: { - enable?: boolean; - auto_add_invited_external_users?: boolean; - auto_add_meeting_participants?: boolean; - }; - auto_start_meeting_summary?: boolean; - auto_start_ai_companion_questions?: boolean; + join_before_host?: boolean; + jbh_time?: 0 | 5 | 10 | 15; }; recurrence?: { - type?: number; + type?: 1 | 2 | 3; repeat_interval?: number; weekly_days?: string; monthly_day?: number; - monthly_week_day?: number; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; end_times?: number; end_date_time?: string; - monthly_week?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; }; tracking_fields?: { field?: string; value?: string; + visible?: boolean; }[]; }; }; }; -type MeetingParticipantRoomSystemCalloutMissedEvent = Event<"meeting.participant_room_system_callout_missed"> & { +type MeetingLiveStreamingStartedEvent = Event<"meeting.live_streaming_started"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id: string; + object: { + id: number; + uuid: string; + host_id: string; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + timezone?: string; + duration: number; + live_streaming: { + service: "Facebook" | "Workplace_by_Facebook" | "YouTube" | "Twitch" | "Custom_Live_Streaming_Service"; + custom_live_streaming_settings?: { + stream_url: string; + stream_key: string; + page_url: string; + resolution?: string; + }; + date_time: string; + }; + }; + }; +}; +type MeetingParticipantRoomSystemCalloutAcceptedEvent = Event<"meeting.participant_room_system_callout_accepted"> & { event: string; event_ts: number; payload: { @@ -7103,7 +7643,7 @@ type MeetingParticipantRoomSystemCalloutMissedEvent = Event<"meeting.participant }; }; }; -type MeetingRegistrationDeniedEvent = Event<"meeting.registration_denied"> & { +type WebinarRegistrationApprovedEvent = Event<"webinar.registration_approved"> & { event: string; event_ts: number; payload: { @@ -7115,228 +7655,308 @@ type MeetingRegistrationDeniedEvent = Event<"meeting.registration_denied"> & { id: number; host_id: string; topic: string; - type: number; + type: 5 | 6 | 9; start_time: string; duration: number; timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; registrant: { id: string; first_name: string; last_name?: string; email: string; + join_url: string; + tracking_source?: { + id: string; + source_name: string; + tracking_url: string; + }; }; }; }; }; -type WebinarCreatedEvent = Event<"webinar.created"> & { +type MeetingBreakoutRoomSharingStartedEvent = Event<"meeting.breakout_room_sharing_started"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id: string; - operation?: string; object: { + id: string; uuid: string; - id: number; + breakout_room_uuid: string; host_id: string; topic: string; - type: number; - start_time?: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + timezone?: string; duration: number; - timezone: string; - join_url: string; - password?: string; - creation_source: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - duration?: number; - status?: string; - } - ]>[]; - settings: { - use_pmi: boolean; - alternative_hosts: string; + participant: { + user_id: string; + parent_user_id?: string; + user_name?: string; + id?: string; + sharing_details: { + content: "application" | "whiteboard" | "desktop" | "airplay" | "camera" | "unknown"; + link_source: "" | "deep_link" | "in_meeting"; + file_link: string; + date_time: string; + source: "" | "dropbox"; + }; }; - recurrence?: { - type?: number; - repeat_interval?: number; - weekly_days?: string; - monthly_day?: number; - monthly_week_day?: number; - end_times?: number; - end_date_time?: string; - monthly_week?: number; + }; + }; +}; +type MeetingParticipantBindEvent = Event<"meeting.participant_bind"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + id?: string; + uuid: string; + host_id: string; + topic?: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time?: string; + timezone?: string; + duration?: number; + participant: { + user_id: string; + bind_user_id: string; + user_name?: string; + id?: string; + participant_uuid?: string; + bind_participant_uuid?: string; + date_time?: string; + email?: string; + participant_user_id?: string; + registrant_id?: string; + phone_number: string; + }; + }; + }; +}; +type MeetingParticipantFeedbackEvent = Event<"meeting.participant_feedback"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + id: string; + uuid: string; + participant: { + participant_uuid: string; + participant_user_id: string; + user_name: string; + feedback: { + satisfied: boolean; + feedback_details?: { + id: string; + name: string; + }[]; + comments?: string; + }; }; }; }; }; -type WebinarRegistrationDeniedEvent = Event<"webinar.registration_denied"> & { +type RecordingDeletedEvent = Event<"recording.deleted"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id: string; + object: { + id: number; + uuid: string; + host_id: string; + account_id: string; + topic: string; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; + start_time: string; + timezone?: string; + duration: number; + share_url: string; + total_size: number; + recording_count: number; + recording_files: { + id: string; + meeting_id: string; + recording_start: string; + recording_end: string; + file_type: "MP4" | "M4A" | "CHAT" | "TRANSCRIPT" | "CSV" | "TB" | "CC" | "CHAT_MESSAGE" | "SUMMARY" | "TIMELINE"; + file_size: number; + file_extension: "MP4" | "M4A" | "TXT" | "VTT" | "CSV" | "JSON" | "JPG"; + file_name?: string; + play_url?: string; + download_url: string; + status: "completed" | "processing"; + recording_type: "shared_screen_with_speaker_view(CC)" | "shared_screen_with_speaker_view" | "shared_screen_with_gallery_view" | "gallery_view" | "shared_screen" | "audio_only" | "audio_transcript" | "chat_file" | "active_speaker" | "host_video" | "audio_only_each_participant" | "cc_transcript" | "closed_caption" | "poll" | "timeline" | "thumbnail" | "audio_interpretation" | "summary" | "summary_next_steps" | "summary_smart_chapters" | "sign_interpretation" | "production_sutdio"; + }[]; + participant_audio_files?: { + id: string; + recording_start: string; + recording_end: string; + file_type: string; + file_name: string; + file_size: number; + file_extension: string; + play_url?: string; + download_url: string; + file_path?: string; + status: "completed" | "processing"; + }[]; + }; + }; +}; +type RecordingPausedEvent = Event<"recording.paused"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id: string; object: { - uuid: string; id: number; + uuid: string; host_id: string; topic: string; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; start_time: string; + timezone?: string; duration: number; - timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; - registrant: { - id: string; - first_name: string; - last_name?: string; - email: string; - tracking_source?: { - id: string; - source_name: string; - tracking_url: string; - }; + recording_file: { + recording_start: string; + recording_end: string; }; }; }; }; -type MeetingRegistrationApprovedEvent = Event<"meeting.registration_approved"> & { +type MeetingParticipantRoomSystemCalloutRingingEvent = Event<"meeting.participant_room_system_callout_ringing"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id: string; object: { - uuid: string; id: number; + uuid: string; host_id: string; - topic: string; - type: number; - start_time: string; - duration: number; - timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; - registrant: { - id: string; - first_name: string; - last_name?: string; - email: string; - join_url: string; - participant_pin_code?: number; + message_id: string; + inviter_name: string; + participant: { + call_type: string; + device_ip: string; }; }; }; }; -type WebinarParticipantLeftEvent = Event<"webinar.participant_left"> & { +type RecordingStartedEvent = Event<"recording.started"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id: string; + id: number; uuid: string; host_id: string; topic: string; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; start_time: string; - timezone: string; + timezone?: string; duration: number; - participant: { - user_id: string; - user_name?: string; - id?: string; - leave_time?: string; - leave_reason?: string; - email: string; - registrant_id?: string; - participant_user_id?: string; - participant_uuid?: string; - customer_key?: string; - phone_number?: string; + recording_file: { + recording_start: string; + recording_end: string; }; }; }; }; -type RecordingBatchDeletedEvent = Event<"recording.batch_deleted"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - operator: string; - operator_id: string; +type UserTspUpdatedEvent = Event<"user.tsp_updated"> & { + event?: string; + event_ts?: number; + payload?: { + account_id?: string; + operator?: string; + operator_id?: string; object?: { - meetings: { - meeting_uuid?: string; - recording_file_ids?: any[]; + id?: string; + email?: string; + tsp_credentials?: { + conference_code?: string; + leader_pin?: string; + tsp_bridge?: string; + dial_in_numbers?: { + code?: string; + number?: string; + type?: "toll" | "tollfree" | "media_link"; + country_label?: "US_TSP_TB" | "EU_TSP_TB"; + }[]; + }; + }; + old_object?: { + conference_code?: string; + leader_pin?: string; + tsp_bridge?: string; + dial_in_numbers?: { + code?: string; + number?: string; + type?: "toll" | "tollfree" | "media_link"; + country_label?: "US_TSP_TB" | "EU_TSP_TB"; }[]; }; }; }; -type WebinarAlertEvent = Event<"webinar.alert"> & { +type MeetingInvitationTimeoutEvent = Event<"meeting.invitation_timeout"> & { event: string; event_ts: number; payload: { account_id: string; + operator: string; + operator_id: string; object: { id: string; uuid: string; host_id: string; topic: string; - type: number; - start_time: string; - timezone: string; - duration: number; - issues: string[]; + participant: { + participant_user_id: string; + email: string; + }; }; }; }; -type RecordingRegistrationDeniedEvent = Event<"recording.registration_denied"> & { +type MeetingParticipantAdmittedEvent = Event<"meeting.participant_admitted"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id: string; object: { - id: number; + id: string; uuid: string; host_id: string; topic: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time: string; timezone?: string; duration: number; - registrant: { + participant: { + user_id: string; + user_name?: string; id?: string; + participant_uuid?: string; + participant_user_id?: string; + phone_number?: string; + date_time: string; email: string; - first_name: string; - last_name: string; + customer_key?: string; + registrant_id?: string; }; }; }; }; -type WebinarChatMessageSentEvent = Event<"webinar.chat_message_sent"> & { +type MeetingParticipantRoomSystemCalloutFailedEvent = Event<"meeting.participant_room_system_callout_failed"> & { event: string; event_ts: number; payload: { @@ -7344,135 +7964,93 @@ type WebinarChatMessageSentEvent = Event<"webinar.chat_message_sent"> & { object: { id: number; uuid: string; - chat_message: { - date_time: string; - sender_session_id: string; - sender_name: string; - sender_email?: string; - sender_type: string; - recipient_session_id?: string; - recipient_name?: string; - recipient_email?: string; - recipient_type: string; - message_id: string; - message_content: string; - file_ids?: string[]; + host_id: string; + message_id: string; + inviter_name: string; + reason_type: 0 | 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14; + participant: { + call_type: string; + device_ip: string; }; }; }; }; -type MeetingLiveStreamingStoppedEvent = Event<"meeting.live_streaming_stopped"> & { +type RecordingResumedEvent = Event<"recording.resumed"> & { event: string; event_ts: number; payload: { account_id: string; - operator: string; - operator_id: string; object: { id: number; uuid: string; host_id: string; topic: string; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; start_time: string; timezone?: string; duration: number; - live_streaming: { - service: string; - custom_live_streaming_settings?: { - stream_url: string; - stream_key: string; - page_url: string; - resolution?: string; - }; - date_time: string; - }; - }; - }; -}; -type WebinarChatMessageFileSentEvent = Event<"webinar.chat_message_file_sent"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - webinar_id: number; - webinar_uuid: string; - chat_message_file: { - date_time: string; - sender_session_id: string; - sender_name: string; - sender_email?: string; - sender_type: string; - recipient_session_id?: string; - recipient_name?: string; - recipient_email?: string; - recipient_type: string; - message_id: string; - file_id: string; - file_name: string; - file_size: number; - file_type: string; - download_url: string; + recording_file: { + recording_start: string; + recording_end: string; }; }; }; }; -type MeetingEndedEvent = Event<"meeting.ended"> & { +type MeetingParticipantPhoneCalloutMissedEvent = Event<"meeting.participant_phone_callout_missed"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id: string; + id: number; uuid: string; host_id: string; - topic: string; - type: number; - start_time: string; - timezone?: string; - duration: number; - end_time: string; + participant: { + invitee_name: string; + phone_number: number; + }; }; }; }; -type WebinarRegistrationCancelledEvent = Event<"webinar.registration_cancelled"> & { +type MeetingSummaryUpdatedEvent = Event<"meeting.summary_updated"> & { event: string; event_ts: number; payload: { account_id: string; operator: string; - operator_id?: string; + operator_id: string; object: { - uuid: string; - id: number; - host_id: string; - topic: string; - type: number; - start_time: string; - duration: number; - timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; - registrant: { - id: string; - first_name: string; - last_name?: string; - email: string; - tracking_source?: { - id: string; - source_name: string; - tracking_url: string; - }; + meeting_host_id: string; + meeting_host_email: string; + meeting_uuid: string; + meeting_id: number; + meeting_topic: string; + meeting_start_time: string; + meeting_end_time: string; + summary_start_time: string; + summary_end_time: string; + summary_created_time: string; + summary_last_modified_time: string; + summary_last_modified_user_id: string; + summary_last_modified_user_email: string; + summary_title: string; + summary_overview?: string; + summary_details: { + label: string; + summary: string; + }[]; + next_steps: string[]; + edited_summary: { + summary_overview?: string; + summary_details?: string; + next_steps?: string[]; }; + summary_content?: string; + summary_doc_url?: string; }; }; }; -type MeetingParticipantJoinedBreakoutRoomEvent = Event<"meeting.participant_joined_breakout_room"> & { +type WebinarParticipantJoinedEvent = Event<"webinar.participant_joined"> & { event: string; event_ts: number; payload: { @@ -7480,202 +8058,221 @@ type MeetingParticipantJoinedBreakoutRoomEvent = Event<"meeting.participant_join object: { id: string; uuid: string; - breakout_room_uuid: string; host_id: string; topic: string; - type: number; + type: 5 | 6 | 9; start_time: string; - timezone?: string; + timezone: string; duration: number; participant: { user_id: string; - parent_user_id?: string; user_name: string; - id?: string; - participant_uuid?: string; + id: string; join_time: string; email: string; registrant_id?: string; participant_user_id?: string; - phone_number?: string; + participant_uuid?: string; customer_key?: string; + phone_number?: string; }; }; }; }; -type MeetingParticipantLeftWaitingRoomEvent = Event<"meeting.participant_left_waiting_room"> & { +type RecordingRenamedEvent = Event<"recording.renamed"> & { event: string; event_ts: number; payload: { account_id: string; + operator: string; + operator_id: string; + time_stamp: number; object: { - id: string; uuid: string; + id: number; + topic: string; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; host_id: string; + }; + old_object: { + uuid: string; + id: number; topic: string; - type: number; - start_time: string; - timezone?: string; - duration: number; - participant: { - user_id: string; - user_name?: string; - id?: string; - participant_uuid?: string; - date_time: string; - email: string; - phone_number?: string; - participant_user_id?: string; - customer_key?: string; - registrant_id?: string; - }; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; + host_id: string; }; }; }; -type MeetingStartedEvent = Event<"meeting.started"> & { +type MeetingParticipantRoomSystemCalloutMissedEvent = Event<"meeting.participant_room_system_callout_missed"> & { event: string; event_ts: number; payload: { account_id: string; object: { - id: string; + id: number; uuid: string; host_id: string; - topic: string; - type: number; - start_time: string; - timezone?: string; - duration: number; + message_id: string; + inviter_name: string; + participant: { + call_type: string; + device_ip: string; + }; }; }; }; -type MeetingRegistrationCancelledEvent = Event<"meeting.registration_cancelled"> & { +type WebinarCreatedEvent = Event<"webinar.created"> & { event: string; event_ts: number; payload: { account_id: string; operator: string; - operator_id?: string; + operator_id: string; + operation?: "all" | "single"; object: { uuid: string; id: number; host_id: string; topic: string; - type: number; - start_time: string; + type: 5 | 6 | 9; + start_time?: string; duration: number; timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; - registrant: { - id: string; - first_name: string; - last_name?: string; - email: string; + join_url: string; + password?: string; + creation_source: "other" | "open_api" | "web_portal"; + occurrences?: { + occurrence_id: string; + start_time: string; + duration?: number; + status?: "available" | "deleted"; + }[]; + settings: { + use_pmi: boolean; + alternative_hosts: string; + }; + recurrence?: { + type?: 1 | 2 | 3; + repeat_interval?: number; + weekly_days?: string; + monthly_day?: number; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; + end_times?: number; + end_date_time?: string; + monthly_week?: -1 | 1 | 2 | 3 | 4; }; }; }; }; -type MeetingSummaryCompletedEvent = Event<"meeting.summary_completed"> & { +type RecordingRegistrationDeniedEvent = Event<"recording.registration_denied"> & { event: string; event_ts: number; payload: { account_id: string; + operator: string; + operator_id: string; object: { - meeting_host_id: string; - meeting_host_email: string; - meeting_uuid: string; - meeting_id: number; - meeting_topic: string; - meeting_start_time: string; - meeting_end_time: string; - summary_start_time: string; - summary_end_time: string; - summary_created_time: string; - summary_last_modified_time: string; - summary_title: string; - summary_overview?: string; - summary_details: { - label: string; - summary: string; - }[]; - next_steps: string[]; + id: number; + uuid: string; + host_id: string; + topic: string; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; + start_time: string; + timezone?: string; + duration: number; + registrant: { + id?: string; + email: string; + first_name: string; + last_name: string; + }; }; }; }; -type MeetingParticipantLeftEvent = Event<"meeting.participant_left"> & { +type MeetingLiveStreamingStoppedEvent = Event<"meeting.live_streaming_stopped"> & { event: string; event_ts: number; payload: { account_id: string; + operator: string; + operator_id: string; object: { - id: string; + id: number; uuid: string; host_id: string; topic: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time: string; timezone?: string; duration: number; - participant: { - user_id: string; - user_name?: string; - id?: string; - participant_uuid?: string; - leave_time: string; - leave_reason?: string; - email: string; - registrant_id?: string; - participant_user_id?: string; - customer_key?: string; - phone_number?: string; + live_streaming: { + service: "Facebook" | "Workplace_by_Facebook" | "YouTube" | "Twitch" | "Custom_Live_Streaming_Service"; + custom_live_streaming_settings?: { + stream_url: string; + stream_key: string; + page_url: string; + resolution?: string; + }; + date_time: string; }; }; }; }; -type MeetingRecoveredEvent = Event<"meeting.recovered"> & { +type WebinarRegistrationCancelledEvent = Event<"webinar.registration_cancelled"> & { event: string; event_ts: number; payload: { account_id: string; operator: string; - operator_id: string; - operation?: string; + operator_id?: string; object: { uuid: string; id: number; host_id: string; topic: string; - type: number; + type: 5 | 6 | 9; start_time: string; duration: number; timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; + registrant: { + id: string; + first_name: string; + last_name?: string; + email: string; + tracking_source?: { + id: string; + source_name: string; + tracking_url: string; + }; + }; }; }; }; -type MeetingParticipantPhoneCalloutRejectedEvent = Event<"meeting.participant_phone_callout_rejected"> & { +type MeetingRecoveredEvent = Event<"meeting.recovered"> & { event: string; event_ts: number; payload: { account_id: string; + operator: string; + operator_id: string; + operation?: "all" | "single"; object: { - id: number; uuid: string; + id: number; host_id: string; - participant: { - invitee_name: string; - phone_number: number; - }; + topic: string; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; + start_time: string; + duration: number; + timezone: string; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; }; }; }; @@ -7686,22 +8283,20 @@ type WebinarPermanentlyDeletedEvent = Event<"webinar.permanently_deleted"> & { account_id: string; operator: string; operator_id: string; - operation?: string; + operation?: "all" | "single"; object: { uuid: string; id: number; host_id: string; topic: string; - type: number; + type: 5 | 6 | 9; start_time: string; duration: number; timezone: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - } - ]>[]; + occurrences?: { + occurrence_id: string; + start_time: string; + }[]; }; }; }; @@ -7717,7 +8312,7 @@ type RecordingCompletedEvent = Event<"recording.completed"> & { host_id: string; account_id: string; topic: string; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; start_time: string; password: string; timezone?: string; @@ -7735,15 +8330,15 @@ type RecordingCompletedEvent = Event<"recording.completed"> & { meeting_id: string; recording_start: string; recording_end: string; - file_type: string; + file_type: "MP4" | "M4A" | "CHAT" | "TRANSCRIPT" | "CSV" | "TB" | "CC" | "CHAT_MESSAGE" | "SUMMARY" | "TIMELINE"; file_size: number; - file_extension: string; + file_extension: "MP4" | "M4A" | "TXT" | "VTT" | "CSV" | "JSON" | "JPG"; file_name?: string; play_url?: string; download_url: string; file_path?: string; - status: string; - recording_type: string; + status: "completed" | "processing"; + recording_type: "shared_screen_with_speaker_view(CC)" | "shared_screen_with_speaker_view" | "shared_screen_with_gallery_view" | "gallery_view" | "shared_screen" | "audio_only" | "chat_file" | "active_speaker" | "host_video" | "audio_only_each_participant" | "cc_transcript" | "closed_caption" | "poll" | "timeline" | "thumbnail" | "audio_interpretation" | "summary" | "summary_next_steps" | "summary_smart_chapters" | "sign_interpretation" | "production_studio"; }[]; participant_audio_files?: { id: string; @@ -7756,27 +8351,11 @@ type RecordingCompletedEvent = Event<"recording.completed"> & { play_url?: string; download_url: string; file_path?: string; - status: string; + status: "completed" | "processing"; }[]; }; }; }; -type MeetingParticipantPhoneCalloutAcceptedEvent = Event<"meeting.participant_phone_callout_accepted"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - id: number; - uuid: string; - host_id: string; - participant: { - invitee_name: string; - phone_number: number; - }; - }; - }; -}; type MeetingParticipantPutInWaitingRoomEvent = Event<"meeting.participant_put_in_waiting_room"> & { event: string; event_ts: number; @@ -7787,7 +8366,7 @@ type MeetingParticipantPutInWaitingRoomEvent = Event<"meeting.participant_put_in uuid: string; host_id: string; topic: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time: string; timezone?: string; duration: number; @@ -7798,6 +8377,7 @@ type MeetingParticipantPutInWaitingRoomEvent = Event<"meeting.participant_put_in participant_uuid?: string; date_time: string; email: string; + phone_number?: string; participant_user_id?: string; customer_key?: string; registrant_id?: string; @@ -7818,7 +8398,7 @@ type RecordingTrashedEvent = Event<"recording.trashed"> & { uuid: string; host_id: string; topic: string; - type: number; + type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 99; start_time: string; timezone?: string; duration: number; @@ -7831,14 +8411,14 @@ type RecordingTrashedEvent = Event<"recording.trashed"> & { meeting_id: string; recording_start: string; recording_end: string; - file_type: string; - file_extension: string; + file_type: "MP4" | "M4A" | "CHAT" | "TRANSCRIPT" | "CSV" | "TB" | "CC" | "CHAT_MESSAGE" | "SUMMARY" | "TIMELINE"; + file_extension: "MP4" | "M4A" | "TXT" | "VTT" | "CSV" | "JSON" | "JPG"; file_name?: string; file_size: number; play_url?: string; download_url: string; - status: string; - recording_type: string; + status: "completed" | "processing"; + recording_type: "shared_screen_with_speaker_view(CC)" | "shared_screen_with_speaker_view" | "shared_screen_with_gallery_view" | "gallery_view" | "shared_screen" | "audio_only" | "audio_transcript" | "chat_file" | "active_speaker" | "host_video" | "audio_only_each_participant" | "cc_transcript" | "closed_caption" | "poll" | "timeline" | "thumbnail" | "audio_interpretation" | "summary" | "summary_next_steps" | "summary_smart_chapters" | "production_sutdio"; }[]; participant_audio_files?: { id: string; @@ -7851,7 +8431,7 @@ type RecordingTrashedEvent = Event<"recording.trashed"> & { play_url?: string; download_url: string; file_path?: string; - status: string; + status: "completed" | "processing"; }[]; }; }; @@ -7889,7 +8469,7 @@ type WebinarParticipantBindEvent = Event<"webinar.participant_bind"> & { uuid: string; host_id: string; topic?: string; - type: number; + type: 0 | 1 | 2 | 3 | 4 | 7 | 8; start_time?: string; timezone?: string; duration?: number; @@ -7914,50 +8494,64 @@ type WebinarUpdatedEvent = Event<"webinar.updated"> & { account_id: string; operator: string; operator_id: string; - scope?: string; + scope?: "single" | "all"; object: { id: number; uuid?: string; host_id?: string; topic?: string; - type?: number; + type?: 5 | 6 | 9; start_time?: string; duration?: number; timezone?: string; password?: string; agenda?: string; registration_url?: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - duration?: number; - status?: string; - } - ]>[]; + occurrences?: { + occurrence_id: string; + start_time: string; + duration?: number; + status?: "available" | "deleted"; + }[]; settings?: { host_video?: boolean; panelists_video?: boolean; practice_session?: boolean; - approval_type?: number; - registration_type?: number; - audio?: string; - auto_recording?: string; + approval_type?: 0 | 1 | 2; + registration_type?: 1 | 2 | 3; + audio?: "telephony" | "voip" | "both"; + auto_recording?: "local" | "cloud" | "none"; enforce_login?: boolean; meeting_authentication?: boolean; authentication_option?: string; authentication_name?: string; authentication_domains?: string; + language_interpretation?: { + enable?: boolean; + interpreters?: { + email?: string; + interpreter_languages?: string; + }[]; + }; + sign_language_interpretation?: { + enable?: boolean; + interpreters?: { + email?: string; + sign_language?: string; + }[]; + }; + allow_host_control_participant_mute_state?: boolean; + email_in_attendee_report?: boolean; }; recurrence?: { - type?: number; + type?: 1 | 2 | 3; repeat_interval?: number; weekly_days?: string; monthly_day?: number; - monthly_week_day?: number; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; end_times?: number; end_date_time?: string; - monthly_week?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; }; }; time_stamp: number; @@ -7966,44 +8560,58 @@ type WebinarUpdatedEvent = Event<"webinar.updated"> & { uuid?: string; host_id?: string; topic?: string; - type?: number; + type?: 5 | 6 | 9; start_time?: string; duration?: number; timezone?: string; password?: string; agenda?: string; registration_url?: string; - occurrences?: ExactlyOneOf<[ - { - occurrence_id: string; - start_time: string; - duration?: number; - status?: string; - } - ]>[]; + occurrences?: { + occurrence_id: string; + start_time: string; + duration?: number; + status?: "available" | "deleted"; + }[]; settings?: { host_video?: boolean; panelists_video?: boolean; practice_session?: boolean; - approval_type?: number; - registration_type?: number; - audio?: string; - auto_recording?: string; + approval_type?: 0 | 1 | 2; + registration_type?: 1 | 2 | 3; + audio?: "telephony" | "voip" | "both"; + auto_recording?: "local" | "cloud" | "none"; enforce_login?: boolean; meeting_authentication?: boolean; authentication_option?: string; authentication_name?: string; authentication_domains?: string; + language_interpretation?: { + enable?: boolean; + interpreters?: { + email?: string; + interpreter_languages?: string; + }[]; + }; + sign_language_interpretation?: { + enable?: boolean; + interpreters?: { + email?: string; + sign_language?: string; + }[]; + }; + allow_host_control_participant_mute_state?: boolean; + email_in_attendee_report?: boolean; }; recurrence?: { - type?: number; + type?: 1 | 2 | 3; repeat_interval?: number; weekly_days?: string; monthly_day?: number; - monthly_week_day?: number; + monthly_week_day?: 1 | 2 | 3 | 4 | 5 | 6 | 7; end_times?: number; end_date_time?: string; - monthly_week?: number; + monthly_week?: -1 | 1 | 2 | 3 | 4; }; }; }; @@ -8018,7 +8626,7 @@ type RecordingBatchRecoveredEvent = Event<"recording.batch_recovered"> & { object?: { meetings: { meeting_uuid?: string; - recording_file_ids?: any[]; + recording_file_ids?: string[]; }[]; }; }; @@ -8041,37 +8649,10 @@ type MeetingParticipantRoomSystemCalloutRejectedEvent = Event<"meeting.participa }; }; }; -type MeetingsEvents = WebinarSharingStartedEvent | MeetingParticipantJbhWaitingEvent | MeetingRegistrationCreatedEvent | MeetingSummaryRecoveredEvent | RecordingRecoveredEvent | MeetingSharingEndedEvent | RecordingCloudStorageUsageUpdatedEvent | MeetingParticipantLeftBreakoutRoomEvent | MeetingDeviceTestedEvent | MeetingSummarySharedEvent | RecordingTranscriptCompletedEvent | RecordingStoppedEvent | RecordingBatchTrashedEvent | WebinarStartedEvent | WebinarSharingEndedEvent | MeetingSummaryTrashedEvent | WebinarDeletedEvent | RecordingRegistrationApprovedEvent | MeetingSharingStartedEvent | WebinarRegistrationCreatedEvent | MeetingChatMessageSentEvent | MeetingParticipantRoleChangedEvent | MeetingRiskAlertEvent | MeetingPermanentlyDeletedEvent | WebinarParticipantFeedbackEvent | RecordingRegistrationCreatedEvent | MeetingParticipantJoinedWaitingRoomEvent | MeetingParticipantPhoneCalloutRingingEvent | MeetingCreatedEvent | MeetingLiveStreamingStartedEvent | MeetingParticipantRoomSystemCalloutAcceptedEvent | WebinarRegistrationApprovedEvent | MeetingParticipantJbhJoinedEvent | RecordingArchiveFilesCompletedEvent | MeetingBreakoutRoomSharingStartedEvent | MeetingAlertEvent | MeetingChatMessageFileSentEvent | MeetingParticipantBindEvent | MeetingDeletedEvent | MeetingParticipantFeedbackEvent | MeetingParticipantJoinedEvent | UserTspDeletedEvent | RecordingDeletedEvent | RecordingPausedEvent | MeetingParticipantRoomSystemCalloutRingingEvent | RecordingStartedEvent | UserTspUpdatedEvent | WebinarEndedEvent | WebinarRecoveredEvent | MeetingParticipantAdmittedEvent | MeetingParticipantRoomSystemCalloutFailedEvent | RecordingResumedEvent | WebinarParticipantRoleChangedEvent | MeetingParticipantPhoneCalloutMissedEvent | MeetingSummaryUpdatedEvent | WebinarParticipantJoinedEvent | RecordingRenamedEvent | MeetingParticipantJbhWaitingLeftEvent | UserTspCreatedEvent | MeetingBreakoutRoomSharingEndedEvent | MeetingUpdatedEvent | MeetingParticipantRoomSystemCalloutMissedEvent | MeetingRegistrationDeniedEvent | WebinarCreatedEvent | WebinarRegistrationDeniedEvent | MeetingRegistrationApprovedEvent | WebinarParticipantLeftEvent | RecordingBatchDeletedEvent | WebinarAlertEvent | RecordingRegistrationDeniedEvent | WebinarChatMessageSentEvent | MeetingLiveStreamingStoppedEvent | WebinarChatMessageFileSentEvent | MeetingEndedEvent | WebinarRegistrationCancelledEvent | MeetingParticipantJoinedBreakoutRoomEvent | MeetingParticipantLeftWaitingRoomEvent | MeetingStartedEvent | MeetingRegistrationCancelledEvent | MeetingSummaryCompletedEvent | MeetingParticipantLeftEvent | MeetingRecoveredEvent | MeetingParticipantPhoneCalloutRejectedEvent | WebinarPermanentlyDeletedEvent | RecordingCompletedEvent | MeetingParticipantPhoneCalloutAcceptedEvent | MeetingParticipantPutInWaitingRoomEvent | RecordingTrashedEvent | MeetingSummaryDeletedEvent | WebinarParticipantBindEvent | WebinarUpdatedEvent | RecordingBatchRecoveredEvent | MeetingParticipantRoomSystemCalloutRejectedEvent; +type MeetingsEvents = MeetingParticipantJbhWaitingEvent | MeetingSummaryRecoveredEvent | MeetingParticipantLeftBreakoutRoomEvent | MeetingDeviceTestedEvent | MeetingSummarySharedEvent | WebinarChatMessageFileDownloadedEvent | WebinarDeletedEvent | RecordingRegistrationApprovedEvent | MeetingRiskAlertEvent | WebinarParticipantFeedbackEvent | MeetingParticipantJoinedWaitingRoomEvent | WebinarConvertedToMeetingEvent | MeetingParticipantPhoneCalloutRingingEvent | MeetingParticipantJbhJoinedEvent | MeetingInvitationAcceptedEvent | RecordingArchiveFilesCompletedEvent | MeetingAlertEvent | MeetingChatMessageFileSentEvent | MeetingDeletedEvent | MeetingParticipantJoinedEvent | UserTspDeletedEvent | MeetingInvitationDispatchedEvent | WebinarEndedEvent | MeetingConvertedToWebinarEvent | WebinarRecoveredEvent | WebinarParticipantRoleChangedEvent | MeetingParticipantJbhWaitingLeftEvent | UserTspCreatedEvent | MeetingBreakoutRoomSharingEndedEvent | MeetingUpdatedEvent | MeetingRegistrationDeniedEvent | WebinarRegistrationDeniedEvent | MeetingRegistrationApprovedEvent | WebinarParticipantLeftEvent | RecordingBatchDeletedEvent | WebinarAlertEvent | WebinarChatMessageSentEvent | WebinarChatMessageFileSentEvent | MeetingEndedEvent | MeetingParticipantJoinedBreakoutRoomEvent | MeetingParticipantLeftWaitingRoomEvent | MeetingStartedEvent | MeetingRegistrationCancelledEvent | MeetingSummaryCompletedEvent | MeetingParticipantLeftEvent | MeetingParticipantPhoneCalloutRejectedEvent | MeetingParticipantPhoneCalloutAcceptedEvent | WebinarSharingStartedEvent | MeetingRegistrationCreatedEvent | RecordingRecoveredEvent | MeetingSharingEndedEvent | RecordingCloudStorageUsageUpdatedEvent | RecordingTranscriptCompletedEvent | RecordingStoppedEvent | RecordingBatchTrashedEvent | WebinarStartedEvent | MeetingChatMessageFileDownloadedEvent | WebinarSharingEndedEvent | MeetingSummaryTrashedEvent | MeetingSharingStartedEvent | WebinarRegistrationCreatedEvent | MeetingChatMessageSentEvent | MeetingInvitationRejectedEvent | MeetingParticipantRoleChangedEvent | MeetingPermanentlyDeletedEvent | RecordingRegistrationCreatedEvent | MeetingCreatedEvent | MeetingLiveStreamingStartedEvent | MeetingParticipantRoomSystemCalloutAcceptedEvent | WebinarRegistrationApprovedEvent | MeetingBreakoutRoomSharingStartedEvent | MeetingParticipantBindEvent | MeetingParticipantFeedbackEvent | RecordingDeletedEvent | RecordingPausedEvent | MeetingParticipantRoomSystemCalloutRingingEvent | RecordingStartedEvent | UserTspUpdatedEvent | MeetingInvitationTimeoutEvent | MeetingParticipantAdmittedEvent | MeetingParticipantRoomSystemCalloutFailedEvent | RecordingResumedEvent | MeetingParticipantPhoneCalloutMissedEvent | MeetingSummaryUpdatedEvent | WebinarParticipantJoinedEvent | RecordingRenamedEvent | MeetingParticipantRoomSystemCalloutMissedEvent | WebinarCreatedEvent | RecordingRegistrationDeniedEvent | MeetingLiveStreamingStoppedEvent | WebinarRegistrationCancelledEvent | MeetingRecoveredEvent | WebinarPermanentlyDeletedEvent | RecordingCompletedEvent | MeetingParticipantPutInWaitingRoomEvent | RecordingTrashedEvent | MeetingSummaryDeletedEvent | WebinarParticipantBindEvent | WebinarUpdatedEvent | RecordingBatchRecoveredEvent | MeetingParticipantRoomSystemCalloutRejectedEvent; declare class MeetingsEventProcessor extends EventManager { } -/** - * Credentials for access token & refresh token, which are used to access Zoom's APIs. - * - * As access token is short-lived (usually a single hour), its expiration time is checked - * first. If it's possible to use the access token, it's used; however, if it has expired - * or is close to expiring, the refresh token should be used to generate a new access token - * before the API call is made. Refresh tokens are generally valid for 90 days. - * - * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} - * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. - * It's likely that this error will be rare, but it _can_ be thrown. - */ -interface OAuthToken { - accessToken: string; - expirationTimeIso: string; - refreshToken: string; - scopes: string[]; -} -declare class OAuth extends InteractiveAuth { - private assertResponseAccessToken; - private fetchAccessToken; - getToken(): Promise; - initRedirectCode(code: string): Promise; - private mapOAuthToken; - private refreshAccessToken; -} - type MeetingsOptions = CommonClientOptions; declare class MeetingsOAuthClient = MeetingsOptions> extends ProductClient { protected initAuth({ clientId, clientSecret, tokenStore, ...restOptions }: OptionsType): OAuth; @@ -8086,4 +8667,5 @@ declare class MeetingsS2SAuthClient typeof obj.installerOptions.redirectUri !== "undefined" && typeof obj.installerOptions.stateStore !== "undefined"; @@ -450,7 +453,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -460,14 +466,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -579,9 +591,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -595,6 +604,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? createServer : createServer$1; } @@ -609,8 +631,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -627,69 +661,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -723,18 +772,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -743,87 +798,13 @@ class HttpReceiver { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -852,10 +833,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -948,6 +942,8 @@ class MeetingsEndpoints extends WebEndpoints { method: "PUT", urlPathBuilder: ({ meetingId, recordingId }) => `/meetings/${meetingId}/recordings/${recordingId}/status` }), + getMeetingTranscript: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/transcript` }), + deleteMeetingOrWebinarTranscript: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/transcript` }), recoverMeetingRecordings: this.buildEndpoint({ method: "PUT", urlPathBuilder: ({ meetingUUID }) => `/meetings/${meetingUUID}/recordings/status` }), listAllRecordings: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/users/${userId}/recordings` }) }; @@ -962,7 +958,8 @@ class MeetingsEndpoints extends WebEndpoints { }), getZDMGroupInfo: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/devices/groups` }), assignDeviceToUserOrCommonarea: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/devices/zpa/assignment` }), - upgradeZpaOsApp: this.buildEndpoint({ + getZoomPhoneApplianceSettingsByUserID: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/devices/zpa/settings` }), + upgradeZPAFirmwareOrApp: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/devices/zpa/upgrade` }), @@ -980,6 +977,7 @@ class MeetingsEndpoints extends WebEndpoints { method: "PATCH", urlPathBuilder: ({ deviceId }) => `/devices/${deviceId}` }), + assignDeviceToGroup: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ deviceId }) => `/devices/${deviceId}/assign_group` }), changeDeviceAssociation: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ deviceId }) => `/devices/${deviceId}/assignment` }) }; h323Devices = { @@ -1001,7 +999,8 @@ class MeetingsEndpoints extends WebEndpoints { urlPathBuilder: ({ meetingId, messageId }) => `/live_meetings/${meetingId}/chat/messages/${messageId}` }), useInMeetingControls: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ meetingId }) => `/live_meetings/${meetingId}/events` }), - listMeetingSummariesOfAccount: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/meetings/meeting_summaries` }), + updateParticipantRealTimeMediaStreamsRTMSAppStatus: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ meetingId }) => `/live_meetings/${meetingId}/rtms_app/status` }), + listAccountsMeetingOrWebinarSummaries: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/meetings/meeting_summaries` }), getMeeting: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}` }), deleteMeeting: this.buildEndpoint({ method: "DELETE", @@ -1018,7 +1017,8 @@ class MeetingsEndpoints extends WebEndpoints { getLivestreamDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/livestream` }), updateLivestream: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/livestream` }), updateLivestreamStatus: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/livestream/status` }), - getMeetingSummary: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/meeting_summary` }), + getMeetingOrWebinarSummary: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/meeting_summary` }), + deleteMeetingOrWebinarSummary: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/meeting_summary` }), addMeetingApp: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ meetingId }) => `/meetings/${meetingId}/open_apps` }), deleteMeetingApp: this.buildEndpoint({ method: "DELETE", @@ -1077,6 +1077,7 @@ class MeetingsEndpoints extends WebEndpoints { getBillingInvoiceReports: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/billing/invoices` }), getCloudRecordingUsageReport: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/cloud_recording` }), getDailyUsageReport: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/daily` }), + getHistoryMeetingAndWebinarList: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/history_meetings` }), getMeetingActivitiesReport: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/meeting_activities` }), getMeetingDetailReports: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/report/meetings/${meetingId}` }), getMeetingParticipantReports: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ meetingId }) => `/report/meetings/${meetingId}/participants` }), @@ -1095,15 +1096,15 @@ class MeetingsEndpoints extends WebEndpoints { getWebinarSurveyReport: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ webinarId }) => `/report/webinars/${webinarId}/survey` }) }; sIPPhone = { - listSIPPhones: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/sip_phones` }), - enableSIPPhone: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/sip_phones` }), + listSIPPhones: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/sip_phones/phones` }), + enableSIPPhone: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/sip_phones/phones` }), deleteSIPPhone: this.buildEndpoint({ method: "DELETE", - urlPathBuilder: ({ phoneId }) => `/sip_phones/${phoneId}` + urlPathBuilder: ({ phoneId }) => `/sip_phones/phones/${phoneId}` }), updateSIPPhone: this.buildEndpoint({ method: "PATCH", - urlPathBuilder: ({ phoneId }) => `/sip_phones/${phoneId}` + urlPathBuilder: ({ phoneId }) => `/sip_phones/phones/${phoneId}` }) }; tSP = { @@ -1320,7 +1321,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -1341,7 +1344,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. diff --git a/package.json b/package.json index 406517f..c79f5d4 100644 --- a/package.json +++ b/package.json @@ -1,138 +1,100 @@ { - "type": "module", - "name": "@zoom/rivet", - "author": "Zoom Communications, Inc.", - "contributors": [ - { - "name": "James Coon", - "email": "james.coon@zoom.us", - "url": "https://www.npmjs.com/~jcoon97" - }, - { - "name": "Will Ezrine", - "email": "will.ezrine@zoom.us", - "url": "https://www.npmjs.com/~wezrine" - }, - { - "name": "Tommy Gaessler", - "email": "tommy.gaessler@zoom.us", - "url": "https://www.npmjs.com/~tommygaessler" - } - ], - "packageManager": "pnpm@9.9.0", - "version": "0.2.2", - "scripts": { - "test": "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - "prepare": "husky", - "lint": "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" + "type": "module", + "name": "@zoom/rivet", + "author": "Zoom Communications, Inc.", + "contributors": [ + { + "name": "James Coon", + "email": "james.coon@zoom.us", + "url": "https://www.npmjs.com/~jcoon97" }, - "devDependencies": { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - "dotenv": "^16.4.5", - "eslint": "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - "husky": "^9.1.6", - "lint-staged": "^15.2.10", - "nock": "^13.5.5", - "prettier": "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - "rollup": "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - "semver": "^7.6.3", - "supertest": "^7.0.0", - "ts-node": "^10.9.2", - "tslib": "^2.7.0", - "typescript": "^5.6.3", - "typescript-eslint": "^8.8.1", - "vitest": "2.1.3" + { + "name": "Will Ezrine", + "email": "will.ezrine@zoom.us", + "url": "https://www.npmjs.com/~wezrine" }, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] + { + "name": "Tommy Gaessler", + "email": "tommy.gaessler@zoom.us", + "url": "https://www.npmjs.com/~tommygaessler" + } + ], + "packageManager": "pnpm@10.24.0", + "version": "0.4.0", + "license": "SEE LICENSE IN LICENSE.md", + "repository": { + "type": "git", + "url": "git+https://github.com/zoom/rivet-javascript.git" + }, + "keywords": [ + "zoom", + "rivet", + "api", + "endpoint", + "webhook", + "event", + "sdk" + ], + "bugs": { + "url": "https://github.com/zoom/rivet-javascript/issues" + }, + "homepage": "https://developers.zoom.us/docs/rivet/javascript/", + "exports": { + ".": { + "import": "./index.mjs", + "require": "./index.cjs", + "types": "./index.d.ts" + }, + "./chatbot": { + "import": "./chatbot/chatbot.mjs", + "require": "./chatbot/chatbot.cjs", + "types": "./chatbot/chatbot.d.ts" + }, + "./commerce": { + "import": "./commerce/commerce.mjs", + "require": "./commerce/commerce.cjs", + "types": "./commerce/commerce.d.ts" + }, + "./teamchat": { + "import": "./teamchat/teamchat.mjs", + "require": "./teamchat/teamchat.cjs", + "types": "./teamchat/teamchat.d.ts" + }, + "./users": { + "import": "./users/users.mjs", + "require": "./users/users.cjs", + "types": "./users/users.d.ts" + }, + "./marketplace": { + "import": "./marketplace/marketplace.mjs", + "require": "./marketplace/marketplace.cjs", + "types": "./marketplace/marketplace.d.ts" }, - "license": "SEE LICENSE IN LICENSE.md", - "repository": { - "type": "git", - "url": "git+https://github.com/zoom/rivet-javascript.git" + "./phone": { + "import": "./phone/phone.mjs", + "require": "./phone/phone.cjs", + "types": "./phone/phone.d.ts" }, - "keywords": [ - "zoom", - "rivet", - "api", - "endpoint", - "webhook", - "event", - "sdk" - ], - "bugs": { - "url": "https://github.com/zoom/rivet-javascript/issues" + "./accounts": { + "import": "./accounts/accounts.mjs", + "require": "./accounts/accounts.cjs", + "types": "./accounts/accounts.d.ts" }, - "homepage": "https://developers.zoom.us/docs/rivet/javascript/", - "exports": { - ".": { - "import": "./index.mjs", - "require": "./index.cjs", - "types": "./index.d.ts" - }, - "./chatbot": { - "import": "./chatbot/chatbot.mjs", - "require": "./chatbot/chatbot.cjs", - "types": "./chatbot/chatbot.d.ts" - }, - "./teamchat": { - "import": "./teamchat/teamchat.mjs", - "require": "./teamchat/teamchat.cjs", - "types": "./teamchat/teamchat.d.ts" - }, - "./users": { - "import": "./users/users.mjs", - "require": "./users/users.cjs", - "types": "./users/users.d.ts" - }, - "./phone": { - "import": "./phone/phone.mjs", - "require": "./phone/phone.cjs", - "types": "./phone/phone.d.ts" - }, - "./accounts": { - "import": "./accounts/accounts.mjs", - "require": "./accounts/accounts.cjs", - "types": "./accounts/accounts.d.ts" - }, - "./meetings": { - "import": "./meetings/meetings.mjs", - "require": "./meetings/meetings.cjs", - "types": "./meetings/meetings.d.ts" - }, - "./videosdk": { - "import": "./videosdk/videosdk.mjs", - "require": "./videosdk/videosdk.cjs", - "types": "./videosdk/videosdk.d.ts" - } + "./meetings": { + "import": "./meetings/meetings.mjs", + "require": "./meetings/meetings.cjs", + "types": "./meetings/meetings.d.ts" }, - "dependencies": { - "axios": "^1.7.9", - "dayjs": "^1.11.13", - "form-data": "^4.0.1", - "jose": "^5.9.4" + "./videosdk": { + "import": "./videosdk/videosdk.mjs", + "require": "./videosdk/videosdk.cjs", + "types": "./videosdk/videosdk.d.ts" } -} \ No newline at end of file + }, + "dependencies": { + "axios": "^1.13.2", + "dayjs": "^1.11.19", + "form-data": "^4.0.5", + "jose": "^5.9.4" + } +} diff --git a/phone/phone.cjs b/phone/phone.cjs index 91ea27e..cbde199 100644 --- a/phone/phone.cjs +++ b/phone/phone.cjs @@ -426,6 +426,9 @@ class JwtStateStore { const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; const hasInstallerOptions = (obj) => typeof obj.installerOptions.redirectUri !== "undefined" && typeof obj.installerOptions.stateStore !== "undefined"; @@ -452,7 +455,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -462,14 +468,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -581,9 +593,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -597,6 +606,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? node_https.createServer : node_http.createServer; } @@ -611,8 +633,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -629,69 +663,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, exports.StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, exports.StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -725,18 +774,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(exports.StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -745,87 +800,13 @@ class HttpReceiver { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -854,10 +835,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${node_path.basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -1028,9 +1022,14 @@ class PhoneEndpoints extends WebEndpoints { urlPathBuilder: ({ callLogId }) => `/phone/call_history/${callLogId}` }), addClientCodeToCallHistory: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ callLogId }) => `/phone/call_history/${callLogId}/client_code` }), + getCallHistoryDetail: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ callHistoryId }) => `/phone/call_history_detail/${callHistoryId}` }), getAccountsCallLogs: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/call_logs` }), getCallLogDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ callLogId }) => `/phone/call_logs/${callLogId}` }), addClientCodeToCallLog: this.buildEndpoint({ method: "PUT", urlPathBuilder: ({ callLogId }) => `/phone/call_logs/${callLogId}/client_code` }), + getUserAICallSummaryDetail: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ userId, aiCallSummaryId }) => `/phone/user/${userId}/ai_call_summary/${aiCallSummaryId}` + }), getUsersCallHistory: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/phone/users/${userId}/call_history` }), syncUsersCallHistory: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/phone/users/${userId}/call_history/sync` }), deleteUsersCallHistory: this.buildEndpoint({ @@ -1045,6 +1044,7 @@ class PhoneEndpoints extends WebEndpoints { }) }; callQueues = { + listCallQueueAnalytics: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/call_queue_analytics` }), listCallQueues: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/call_queues` }), createCallQueue: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/phone/call_queues` }), getCallQueueDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ callQueueId }) => `/phone/call_queues/${callQueueId}` }), @@ -1072,7 +1072,7 @@ class PhoneEndpoints extends WebEndpoints { method: "DELETE", urlPathBuilder: ({ callQueueId, phoneNumberId }) => `/phone/call_queues/${callQueueId}/phone_numbers/${phoneNumberId}` }), - addPolicySettingToCallQueue: this.buildEndpoint({ + addPolicySubsettingToCallQueue: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ callQueueId, policyType }) => `/phone/call_queues/${callQueueId}/policies/${policyType}` }), @@ -1104,6 +1104,7 @@ class PhoneEndpoints extends WebEndpoints { commonAreas = { listCommonAreas: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/common_areas` }), addCommonArea: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/phone/common_areas` }), + generateActivationCodesForCommonAreas: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/phone/common_areas/activation_code` }), listActivationCodes: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/common_areas/activation_codes` }), applyTemplateToCommonAreas: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ templateId }) => `/phone/common_areas/template_id/${templateId}` }), getCommonAreaDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ commonAreaId }) => `/phone/common_areas/${commonAreaId}` }), @@ -1124,7 +1125,7 @@ class PhoneEndpoints extends WebEndpoints { }), updateCommonAreaPinCode: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ commonAreaId }) => `/phone/common_areas/${commonAreaId}/pin_code` }), getCommonAreaSettings: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ commonAreaId }) => `/phone/common_areas/${commonAreaId}/settings` }), - addCommonAreaSettings: this.buildEndpoint({ + addCommonAreaSetting: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ commonAreaId, settingType }) => `/phone/common_areas/${commonAreaId}/settings/${settingType}` }), @@ -1132,7 +1133,7 @@ class PhoneEndpoints extends WebEndpoints { method: "DELETE", urlPathBuilder: ({ commonAreaId, settingType }) => `/phone/common_areas/${commonAreaId}/settings/${settingType}` }), - updateCommonAreaSettings: this.buildEndpoint({ + updateCommonAreaSetting: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ commonAreaId, settingType }) => `/phone/common_areas/${commonAreaId}/settings/${settingType}` }) @@ -1144,6 +1145,12 @@ class PhoneEndpoints extends WebEndpoints { urlPathBuilder: ({ callId }) => `/phone/metrics/call_logs/${callId}/qos` }), getCallDetailsFromCallLog: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ call_id }) => `/phone/metrics/call_logs/${call_id}` }), + listDefaultEmergencyAddressUsers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/default_emergency_address/users` }), + listDetectablePersonalLocationUsers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/detectable_personal_location/users` }), + listUsersPermissionForLocationSharing: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/location_sharing_permission/users` }), + listNomadicEmergencyServicesUsers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/nomadic_emergency_services/users` }), + listRealTimeLocationForIPPhones: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/realtime_location/devices` }), + listRealTimeLocationForUsers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/realtime_location/users` }), listTrackedLocations: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/location_tracking` }), listPastCallMetrics: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/past_calls` }) }; @@ -1218,6 +1225,14 @@ class PhoneEndpoints extends WebEndpoints { }) }; groups = { + getGroupPolicyDetails: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ groupId, policyType }) => `/phone/groups/${groupId}/policies/${policyType}` + }), + updateGroupPolicy: this.buildEndpoint({ + method: "PATCH", + urlPathBuilder: ({ groupId, policyType }) => `/phone/groups/${groupId}/policies/${policyType}` + }), getGroupPhoneSettings: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ groupId }) => `/phone/groups/${groupId}/settings` }) }; iVR = { @@ -1366,7 +1381,8 @@ class PhoneEndpoints extends WebEndpoints { rebootDeskPhone: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ deviceId }) => `/phone/devices/${deviceId}/reboot` - }) + }), + listSmartphones: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/smartphones` }) }; phoneNumbers = { addBYOCPhoneNumbers: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/phone/byoc_numbers` }), @@ -1405,7 +1421,10 @@ class PhoneEndpoints extends WebEndpoints { updatePhoneRole: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}` }), listMembersInRole: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/members` }), addMembersToRoles: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/members` }), - deleteMembersInRole: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/members` }) + deleteMembersInRole: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/members` }), + listPhoneRoleTargets: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/targets` }), + addPhoneRoleTargets: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/targets` }), + deletePhoneRoleTargets: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/targets` }) }; privateDirectory = { listPrivateDirectoryMembers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/private_directory/members` }), @@ -1456,6 +1475,10 @@ class PhoneEndpoints extends WebEndpoints { updateDirectoryBackupRoutingRule: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ routingRuleId }) => `/phone/routing_rules/${routingRuleId}` }) }; sMS = { + postSMSMessage: this.buildEndpoint({ + method: "POST", + urlPathBuilder: () => `/phone/sms/messages` + }), getAccountsSMSSessions: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/sms/sessions` }), getSMSSessionDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/phone/sms/sessions/${sessionId}` }), getSMSByMessageID: this.buildEndpoint({ @@ -1481,7 +1504,8 @@ class PhoneEndpoints extends WebEndpoints { unassignPhoneNumber: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ smsCampaignId, phoneNumberId }) => `/phone/sms_campaigns/${smsCampaignId}/phone_numbers/${phoneNumberId}` - }) + }), + listUsersOptStatusesOfPhoneNumbers: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/phone/user/${userId}/sms_campaigns/phone_numbers/opt_status` }) }; settingTemplates = { listSettingTemplates: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/setting_templates` }), @@ -1490,6 +1514,8 @@ class PhoneEndpoints extends WebEndpoints { updateSettingTemplate: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ templateId }) => `/phone/setting_templates/${templateId}` }) }; settings = { + getAccountPolicyDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ policyType }) => `/phone/policies/${policyType}` }), + updateAccountPolicy: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ policyType }) => `/phone/policies/${policyType}` }), listPortedNumbers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/ported_numbers/orders` }), getPortedNumberDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ orderId }) => `/phone/ported_numbers/orders/${orderId}` }), getPhoneAccountSettings: this.buildEndpoint({ @@ -1613,6 +1639,11 @@ class PhoneEndpoints extends WebEndpoints { method: "DELETE", urlPathBuilder: ({ userId }) => `/phone/users/${userId}/outbound_caller_id/customized_numbers` }), + getUserPolicyDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId, policyType }) => `/phone/users/${userId}/policies/${policyType}` }), + updateUserPolicy: this.buildEndpoint({ + method: "PATCH", + urlPathBuilder: ({ userId, policyType }) => `/phone/users/${userId}/policies/${policyType}` + }), getUsersProfileSettings: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/phone/users/${userId}/settings` }), updateUsersProfileSettings: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ userId }) => `/phone/users/${userId}/settings` }), addUsersSharedAccessSetting: this.buildEndpoint({ @@ -1637,6 +1668,10 @@ class PhoneEndpoints extends WebEndpoints { urlPathBuilder: ({ fileId }) => `/phone/voice_mails/download/${fileId}` }), getVoicemailDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ voicemailId }) => `/phone/voice_mails/${voicemailId}` }), + deleteVoicemail: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ voicemailId }) => `/phone/voice_mails/${voicemailId}` + }), updateVoicemailReadStatus: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ voicemailId }) => `/phone/voice_mails/${voicemailId}` }) }; zoomRooms = { @@ -1650,10 +1685,7 @@ class PhoneEndpoints extends WebEndpoints { removeZoomRoomFromZPAccount: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ roomId }) => `/phone/rooms/${roomId}` }), updateZoomRoomUnderZoomPhoneLicense: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ roomId }) => `/phone/rooms/${roomId}` }), assignCallingPlansToZoomRoom: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ roomId }) => `/phone/rooms/${roomId}/calling_plans` }), - removeCallingPlanFromZoomRoom: this.buildEndpoint({ - method: "DELETE", - urlPathBuilder: ({ roomId, type }) => `/phone/rooms/${roomId}/calling_plans/${type.toString()}` - }), + removeCallingPlanFromZoomRoom: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ roomId, type }) => `/phone/rooms/${roomId}/calling_plans/${type}` }), assignPhoneNumbersToZoomRoom: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ roomId }) => `/phone/rooms/${roomId}/phone_numbers` }), removePhoneNumberFromZoomRoom: this.buildEndpoint({ method: "DELETE", @@ -1749,7 +1781,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -1770,7 +1804,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. diff --git a/phone/phone.d.ts b/phone/phone.d.ts index 5441f91..316ed87 100644 --- a/phone/phone.d.ts +++ b/phone/phone.d.ts @@ -1,88 +1,8 @@ -import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { AxiosResponse } from 'axios'; +import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { Server } from 'node:http'; import { ServerOptions } from 'node:https'; -type AllPropsOptional = Exclude<{ - [P in keyof T]: undefined extends T[P] ? True : False; -}[keyof T], undefined> extends True ? True : False; -type Constructor = new (...args: any[]) => T; -type MaybeArray = T | T[]; -type MaybePromise = T | Promise; -type StringIndexed = Record; - -/** - * {@link StateStore} defines methods for generating and verifying OAuth state. - * - * This interface is implemented internally for the default state store; however, - * it can also be implemented and passed to an OAuth client as well. - */ -interface StateStore { - /** - * Generate a new state string, which is directly appended to the OAuth `state` parameter. - */ - generateState(): MaybePromise; - /** - * Verify that the state received during OAuth callback is valid and not forged. - * - * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. - * - * @param state The state parameter that was received during OAuth callback - */ - verifyState(state: string): MaybePromise; -} -/** - * Guard if an object implements the {@link StateStore} interface — most notably, - * `generateState()` and `verifyState(state: string)`. - */ -declare const isStateStore: (obj: unknown) => obj is StateStore; - -interface TokenStore { - getLatestToken(): MaybePromise; - storeToken(token: Token): MaybePromise; -} - -interface RivetError extends Error { - readonly errorCode: ErrorCode; -} - -declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ - readonly ApiResponseError: "zoom_rivet_api_response_error"; - readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; - readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; - readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; - readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; - readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; - readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; - readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; - readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; - readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; - readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; - readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; - readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; - readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; - readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; - readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; - readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; -}[K]>; -declare const ApiResponseError: Constructor; -declare const AwsReceiverRequestError: Constructor; -declare const ClientCredentialsRawResponseError: Constructor; -declare const S2SRawResponseError: Constructor; -declare const CommonHttpRequestError: Constructor; -declare const ReceiverInconsistentStateError: Constructor; -declare const ReceiverOAuthFlowError: Constructor; -declare const HTTPReceiverConstructionError: Constructor; -declare const HTTPReceiverPortNotNumberError: Constructor; -declare const HTTPReceiverRequestError: Constructor; -declare const OAuthInstallerNotInitializedError: Constructor; -declare const OAuthTokenDoesNotExistError: Constructor; -declare const OAuthTokenFetchFailedError: Constructor; -declare const OAuthTokenRawResponseError: Constructor; -declare const OAuthTokenRefreshFailedError: Constructor; -declare const OAuthStateVerificationFailedError: Constructor; -declare const ProductClientConstructionError: Constructor; - declare enum LogLevel { ERROR = "error", WARN = "warn", @@ -142,6 +62,19 @@ declare class ConsoleLogger implements Logger { private static isMoreOrEqualSevere; } +type AllPropsOptional = Exclude<{ + [P in keyof T]: undefined extends T[P] ? True : False; +}[keyof T], undefined> extends True ? True : False; +type Constructor = new (...args: any[]) => T; +type MaybeArray = T | T[]; +type MaybePromise = T | Promise; +type StringIndexed = Record; + +interface TokenStore { + getLatestToken(): MaybePromise; + storeToken(token: Token): MaybePromise; +} + interface AuthOptions { clientId: string; clientSecret: string; @@ -187,6 +120,17 @@ declare abstract class Auth { }>, "grant_type">): Promise; } +interface ClientCredentialsToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} + +interface JwtToken { + token: string; + expirationTimeIso: string; +} + interface S2SAuthToken { accessToken: string; expirationTimeIso: string; @@ -225,12 +169,31 @@ declare class EventManager { protected withContext, Context>(): ContextListener; } +declare enum StatusCode { + OK = 200, + TEMPORARY_REDIRECT = 302, + BAD_REQUEST = 400, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + INTERNAL_SERVER_ERROR = 500 +} +interface ReceiverInitOptions { + eventEmitter?: GenericEventManager | undefined; + interactiveAuth?: InteractiveAuth | undefined; +} +interface Receiver { + canInstall(): true | false; + init(options: ReceiverInitOptions): void; + start(...args: any[]): MaybePromise; + stop(...args: any[]): MaybePromise; +} + interface HttpReceiverOptions extends Partial { endpoints?: MaybeArray | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; port?: number | string | undefined; - webhooksSecretToken: string; - logger?: Logger; - logLevel?: LogLevel; + webhooksSecretToken?: string | undefined; } type SecureServerOptions = { [K in (typeof secureServerOptionKeys)[number]]: ServerOptions[K]; @@ -243,10 +206,15 @@ declare class HttpReceiver implements Receiver { private logger; constructor(options: HttpReceiverOptions); canInstall(): true; + private buildDeletedStateCookieHeader; + private buildStateCookieHeader; + private getRequestCookie; private getServerCreator; private hasEndpoint; private hasSecureOptions; init({ eventEmitter, interactiveAuth }: ReceiverInitOptions): void; + private setResponseCookie; + private areNormalizedUrlsEqual; start(port?: number | string): Promise; stop(): Promise; private writeTemporaryRedirect; @@ -269,6 +237,7 @@ interface WebEndpointOptions { baseUrl?: string | undefined; doubleEncodeUrl?: boolean | undefined; timeout?: number | undefined; + userAgentName?: string | undefined; } type EndpointArguments = (PathSchema extends NoParams ? object : AllPropsOptional extends "t" ? { path?: PathSchema; @@ -290,6 +259,7 @@ declare class WebEndpoints { constructor(options: WebEndpointOptions); protected buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }: BuildEndpointOptions): (_: EndpointArguments) => Promise>; private buildUserAgent; + private getCustomUserAgentName; private getHeaders; private getRequestBody; private isOk; @@ -297,7 +267,7 @@ declare class WebEndpoints { private makeRequest; } -type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & { +type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & Pick & { disableReceiver?: boolean | undefined; logger?: Logger | undefined; logLevel?: LogLevel | undefined; @@ -305,38 +275,68 @@ type CommonClientOptions = GetAuthOptions interface ClientReceiverOptions { receiver: R; } -type ClientConstructorOptions, R extends Receiver> = IsReceiverDisabled extends true ? O : O & (ClientReceiverOptions | HttpReceiverOptions); +type ClientConstructorOptions, R extends Receiver> = (O & { + disableReceiver: true; +}) | (O & (ClientReceiverOptions | HttpReceiverOptions)); type ExtractInstallerOptions = A extends InteractiveAuth ? [ ReturnType ] extends [true] ? WideInstallerOptions : object : object; type ExtractAuthTokenType = A extends Auth ? T : never; -type GenericClientOptions = CommonClientOptions; type GetAuthOptions = AuthOptions> & (A extends S2SAuth ? S2SAuthOptions : object); -type IsReceiverDisabled> = [ - O["disableReceiver"] -] extends [true] ? true : false; type WideInstallerOptions = { installerOptions: InstallerOptions; }; declare abstract class ProductClient, ReceiverType extends Receiver> { private readonly auth; readonly endpoints: EndpointsType; - readonly webEventConsumer: EventProcessorType; + readonly webEventConsumer?: EventProcessorType | undefined; private readonly receiver?; constructor(options: ClientConstructorOptions); protected abstract initAuth(options: OptionsType): AuthType; protected abstract initEndpoints(auth: AuthType, options: OptionsType): EndpointsType; - protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType; + protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType | undefined; private initDefaultReceiver; - start(this: IsReceiverDisabled extends true ? never : this): Promise>; + start(): Promise>; } +/** + * {@link StateStore} defines methods for generating and verifying OAuth state. + * + * This interface is implemented internally for the default state store; however, + * it can also be implemented and passed to an OAuth client as well. + */ +interface StateStore { + /** + * Generate a new state string, which is directly appended to the OAuth `state` parameter. + */ + generateState(): MaybePromise; + /** + * Verify that the state received during OAuth callback is valid and not forged. + * + * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. + * + * @param state The state parameter that was received during OAuth callback + */ + verifyState(state: string): MaybePromise; +} +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +declare const isStateStore: (obj: unknown) => obj is StateStore; + +interface AuthorizationUrlResult { + fullUrl: string; + generatedState: string; +} interface InstallerOptions { directInstall?: boolean | undefined; installPath?: string | undefined; redirectUri: string; redirectUriPath?: string | undefined; stateStore: StateStore | string; + stateCookieName?: string | undefined; + stateCookieMaxAge?: number | undefined; } /** * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication @@ -350,36 +350,87 @@ interface InstallerOptions { */ declare abstract class InteractiveAuth extends Auth { installerOptions?: ReturnType; - getAuthorizationUrl(): Promise; + getAuthorizationUrl(): Promise; getFullRedirectUri(): string; - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }: InstallerOptions): { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }: InstallerOptions): { directInstall: boolean; installPath: string; redirectUri: string; redirectUriPath: string; stateStore: StateStore; + stateCookieName: string; + stateCookieMaxAge: number; }; } -declare enum StatusCode { - OK = 200, - TEMPORARY_REDIRECT = 302, - BAD_REQUEST = 400, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - INTERNAL_SERVER_ERROR = 500 +/** + * Credentials for access token & refresh token, which are used to access Zoom's APIs. + * + * As access token is short-lived (usually a single hour), its expiration time is checked + * first. If it's possible to use the access token, it's used; however, if it has expired + * or is close to expiring, the refresh token should be used to generate a new access token + * before the API call is made. Refresh tokens are generally valid for 90 days. + * + * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} + * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. + * It's likely that this error will be rare, but it _can_ be thrown. + */ +interface OAuthToken { + accessToken: string; + expirationTimeIso: string; + refreshToken: string; + scopes: string[]; } -interface ReceiverInitOptions { - eventEmitter: GenericEventManager; - interactiveAuth?: InteractiveAuth | undefined; +declare class OAuth extends InteractiveAuth { + private assertResponseAccessToken; + private fetchAccessToken; + getToken(): Promise; + initRedirectCode(code: string): Promise; + private mapOAuthToken; + private refreshAccessToken; } -interface Receiver { - canInstall(): true | false; - init(options: ReceiverInitOptions): void; - start(...args: any[]): MaybePromise; - stop(...args: any[]): MaybePromise; + +interface RivetError extends Error { + readonly errorCode: ErrorCode; } +declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ + readonly ApiResponseError: "zoom_rivet_api_response_error"; + readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; + readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; + readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; + readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; + readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; + readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; + readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; + readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; + readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; + readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; + readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; + readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; + readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; + readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; + readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; + readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; +}[K]>; +declare const ApiResponseError: Constructor; +declare const AwsReceiverRequestError: Constructor; +declare const ClientCredentialsRawResponseError: Constructor; +declare const S2SRawResponseError: Constructor; +declare const CommonHttpRequestError: Constructor; +declare const ReceiverInconsistentStateError: Constructor; +declare const ReceiverOAuthFlowError: Constructor; +declare const HTTPReceiverConstructionError: Constructor; +declare const HTTPReceiverPortNotNumberError: Constructor; +declare const HTTPReceiverRequestError: Constructor; +declare const OAuthInstallerNotInitializedError: Constructor; +declare const OAuthTokenDoesNotExistError: Constructor; +declare const OAuthTokenFetchFailedError: Constructor; +declare const OAuthTokenRawResponseError: Constructor; +declare const OAuthTokenRefreshFailedError: Constructor; +declare const OAuthStateVerificationFailedError: Constructor; +declare const ProductClientConstructionError: Constructor; + interface AwsLambdaReceiverOptions { webhooksSecretToken: string; } @@ -401,7 +452,7 @@ type AccountsListAccountsZoomPhoneSettingsResponse = { call_live_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { transcription_start_prompt?: { enable?: boolean; @@ -412,24 +463,24 @@ type AccountsListAccountsZoomPhoneSettingsResponse = { local_survivability_mode?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; external_calling_on_zoom_room_common_area?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; select_outbound_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { allow_hide_outbound_caller_id?: boolean; }; personal_audio_library?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { allow_music_on_hold_customization?: boolean; allow_voicemail_and_message_greeting_customization?: boolean; @@ -437,7 +488,7 @@ type AccountsListAccountsZoomPhoneSettingsResponse = { voicemail?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { allow_videomail?: boolean; allow_download?: boolean; @@ -448,12 +499,12 @@ type AccountsListAccountsZoomPhoneSettingsResponse = { voicemail_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { include_voicemail_file?: boolean; include_voicemail_transcription?: boolean; @@ -462,12 +513,12 @@ type AccountsListAccountsZoomPhoneSettingsResponse = { shared_voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; restricted_call_hours?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { time_zone?: { id?: string; @@ -480,7 +531,7 @@ type AccountsListAccountsZoomPhoneSettingsResponse = { allowed_call_locations?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { locations_applied?: boolean; allow_internal_calls?: boolean; @@ -488,14 +539,14 @@ type AccountsListAccountsZoomPhoneSettingsResponse = { check_voicemails_over_phone?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; auto_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { - recording_calls?: string; + recording_calls?: "inbound" | "outbound" | "both"; recording_transcription?: boolean; recording_start_prompt?: boolean; recording_start_prompt_audio_id?: string; @@ -504,9 +555,9 @@ type AccountsListAccountsZoomPhoneSettingsResponse = { disconnect_on_recording_failure?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_member?: string; - play_beep_volume?: number; - play_beep_time_interval?: number; + play_beep_member?: "allMembers" | "recordingUser"; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; }; inbound_audio_notification?: { recording_start_prompt?: boolean; @@ -522,152 +573,152 @@ type AccountsListAccountsZoomPhoneSettingsResponse = { ad_hoc_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; international_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; outbound_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; outbound_sms?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; sms?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { international_sms?: boolean; }; sms_etiquette_tool?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; zoom_phone_on_mobile?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { allow_calling_sms_mms?: boolean; }; zoom_phone_on_pwa?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; e2e_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; call_handling_forwarding_to_other_users?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { - call_forwarding_type?: number; + call_forwarding_type?: 1 | 2 | 3 | 4; }; call_overflow?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { - call_overflow_type?: number; + call_overflow_type?: 1 | 2 | 3 | 4; }; call_transferring?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; } & { - call_transferring_type?: number; + call_transferring_type?: 1 | 2 | 3 | 4; }; elevate_to_meeting?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; call_park?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; hand_off_to_room?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; mobile_switch_to_carrier?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; delegation?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; audio_intercom?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; block_calls_without_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; block_external_calls?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; call_queue_opt_out_reason?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; auto_delete_data_after_retention_duration?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; auto_call_from_third_party_apps?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; override_default_port?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; peer_to_peer_media?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; advanced_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; display_call_feedback_survey?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; block_list_for_inbound_calls_and_messaging?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; block_calls_as_threat?: { enable?: boolean; @@ -678,7 +729,7 @@ type AccountsListAccountsZoomPhoneSettingsResponse = { type AccountsListAccountsCustomizedOutboundCallerIDPhoneNumbersQueryParams = { selected?: boolean; site_id?: string; - extension_type?: string; + extension_type?: "autoReceptionist" | "callQueue" | "sharedLineGroup"; keyword?: string; page_size?: number; next_page_token?: string; @@ -707,15 +758,16 @@ type AccountsListAccountsCustomizedOutboundCallerIDPhoneNumbersResponse = { type AccountsAddPhoneNumbersForAccountsCustomizedOutboundCallerIDRequestBody = { phone_number_ids?: string[]; }; +type AccountsAddPhoneNumbersForAccountsCustomizedOutboundCallerIDResponse = never; type AccountsDeletePhoneNumbersForAccountsCustomizedOutboundCallerIDQueryParams = { customize_ids?: string[]; }; type AlertsListAlertSettingsWithPagingQueryQueryParams = { page_size?: number; next_page_token?: string; - module?: number; - rule?: number; - status?: number; + module?: 1 | 2 | 3 | 4 | 5; + rule?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14; + status?: 0 | 1; }; type AlertsListAlertSettingsWithPagingQueryResponse = { next_page_token?: string; @@ -726,46 +778,46 @@ type AlertsListAlertSettingsWithPagingQueryResponse = { module?: number; rule?: number; rule_conditions?: { - rule_condition_type?: number; + rule_condition_type?: 1 | 2 | 3 | 4 | 5; rule_condition_value?: string; }[]; targets?: { target_name?: string; }[]; - time_frame_type?: string; + time_frame_type?: "all_day" | "specific_time"; time_frame_from?: string; time_frame_to?: string; - frequency?: number; + frequency?: 5 | 10 | 15 | 30 | 60; email_recipients?: string[]; chat_channels?: { chat_channel_name?: string; token?: string; end_point?: string; }[]; - status?: number; + status?: 0 | 1; }[]; }; type AlertsAddAlertSettingRequestBody = { alert_setting_name: string; module: number; rule: number; - target_type: number; + target_type: 1 | 2 | 3 | 4 | 5; target_ids?: string[]; rule_conditions: { - rule_condition_type?: number; + rule_condition_type?: 1 | 2 | 3 | 4 | 5; rule_condition_value?: string; }[]; - time_frame_type: string; + time_frame_type: "all_day" | "specific_time"; time_frame_from: string; time_frame_to: string; - frequency?: number; + frequency?: 5 | 10 | 15 | 30 | 60; email_recipients?: string[]; chat_channels?: { chat_channel_name?: string; token?: string; end_point?: string; }[]; - status?: number; + status?: 0 | 1; }; type AlertsAddAlertSettingResponse = { alert_setting_id?: string; @@ -780,13 +832,13 @@ type AlertsGetAlertSettingDetailsResponse = { module?: number; rule?: number; rule_conditions?: { - rule_condition_type?: number; + rule_condition_type?: 1 | 2 | 3 | 4 | 5; rule_condition_value?: string; }[]; targets?: { target_id?: string; target_name?: string; - target_type?: number; + target_type?: 1 | 2 | 3 | 4 | 5; target_extension_number?: number; site?: { id?: string; @@ -795,21 +847,21 @@ type AlertsGetAlertSettingDetailsResponse = { assignees?: { extension_number?: number; name?: string; - extension_type?: string; + extension_type?: "user" | "commonArea"; extension_id?: string; }[]; }[]; - time_frame_type?: string; + time_frame_type?: "all_day" | "specific_time"; time_frame_from?: string; time_frame_to?: string; - frequency?: number; + frequency?: 5 | 10 | 15 | 30 | 60; email_recipients?: string[]; chat_channels?: { chat_channel_name?: string; token?: string; end_point?: string; }[]; - status?: number; + status?: 0 | 1; }; type AlertsDeleteAlertSettingPathParams = { alertSettingId: string; @@ -820,21 +872,21 @@ type AlertsUpdateAlertSettingPathParams = { type AlertsUpdateAlertSettingRequestBody = { alert_setting_name?: string; rule_conditions?: { - rule_condition_type?: number; + rule_condition_type?: 1 | 2 | 3 | 4 | 5; rule_condition_value?: string; }[]; target_ids?: string[]; - time_frame_type?: string; + time_frame_type?: "all_day" | "specific_time"; time_frame_from?: string; time_frame_to?: string; - frequency?: number; + frequency?: 5 | 10 | 15 | 30 | 60; email_recipients?: string[]; chat_channels?: { chat_channel_name?: string; token?: string; end_point?: string; }[]; - status?: number; + status?: 0 | 1; }; type AudioLibraryGetAudioItemPathParams = { audioId: string; @@ -844,7 +896,7 @@ type AudioLibraryGetAudioItemResponse = { name?: string; play_url?: string; text?: string; - voice_language?: string; + voice_language?: "en-US" | "en-GB" | "en-GB-WLS" | "en-AU" | "en-IN" | "en-ZA" | "en-NZ" | "es-ES" | "es-US" | "es-MX" | "fr-CA" | "da-DK" | "de-DE" | "fr-FR" | "it-IT" | "is-IS" | "nl-NL" | "pt-PT" | "ja-JP" | "ko-KO" | "ko-KR" | "pt-BR" | "pl-PL" | "zh-CN" | "zh-TW" | "cmn-CN" | "tr-TR" | "nb-NO" | "ro-RO" | "ru-RU" | "sv-SE" | "cy-GB" | "ca-ES" | "de-AT" | "arb"; voice_accent?: string; }; type AudioLibraryDeleteAudioItemPathParams = { @@ -946,7 +998,7 @@ type AutoReceptionistsGetAutoReceptionistResponse = { extension_number?: number; name?: string; timezone?: string; - audio_prompt_language?: string; + audio_prompt_language?: "en-US" | "en-GB" | "es-US" | "fr-CA" | "da-DK" | "de-DE" | "es-ES" | "fr-FR" | "it-IT" | "nl-NL" | "pt-PT" | "ja" | "ko-KR" | "pt-BR" | "zh-CN"; holiday_hours?: { id?: string; name?: string; @@ -961,7 +1013,7 @@ type AutoReceptionistsGetAutoReceptionistResponse = { id?: string; name?: string; }; - recording_storage_location?: string; + recording_storage_location?: "US" | "AU" | "CA" | "DE" | "IN" | "JP" | "SG" | "BR" | "CN" | "MX"; own_storage_name?: string; }; type AutoReceptionistsDeleteNonPrimaryAutoReceptionistPathParams = { @@ -975,9 +1027,9 @@ type AutoReceptionistsUpdateAutoReceptionistRequestBody = { department?: string; extension_number?: number; name?: string; - audio_prompt_language?: string; + audio_prompt_language?: "en-US" | "en-GB" | "es-US" | "fr-CA" | "da-DK" | "de-DE" | "es-ES" | "fr-FR" | "it-IT" | "nl-NL" | "pt-PT" | "ja" | "ko-KR" | "pt-BR" | "zh-CN"; timezone?: string; - recording_storage_location?: string; + recording_storage_location?: "US" | "AU" | "CA" | "DE" | "IN" | "JP" | "SG" | "BR" | "CN" | "MX"; }; type AutoReceptionistsAssignPhoneNumbersPathParams = { autoReceptionistId: string; @@ -1008,7 +1060,7 @@ type AutoReceptionistsGetAutoReceptionistPolicyResponse = { voicemail_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; voicemail_notification_by_email?: { @@ -1017,7 +1069,7 @@ type AutoReceptionistsGetAutoReceptionistPolicyResponse = { forward_voicemail_to_email?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; sms?: { @@ -1025,7 +1077,7 @@ type AutoReceptionistsGetAutoReceptionistPolicyResponse = { international_sms?: boolean; international_sms_countries?: string[]; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; }; @@ -1111,24 +1163,24 @@ type BlockedListListBlockedListsQueryParams = { }; type BlockedListListBlockedListsResponse = { blocked_list?: { - block_type?: string; + block_type?: "inbound" | "outbound" | "threat"; comment?: string; id?: string; - match_type?: string; + match_type?: "phoneNumber" | "prefix"; phone_number?: string; - status?: string; + status?: "active" | "inactive"; }[]; next_page_token?: string; page_size?: number; total_records?: number; }; type BlockedListCreateBlockedListRequestBody = { - block_type?: string; + block_type?: "inbound" | "outbound" | "threat"; comment?: string; country?: string; - match_type?: string; + match_type?: "phoneNumber" | "prefix"; phone_number?: string; - status?: string; + status?: "active" | "inactive"; }; type BlockedListCreateBlockedListResponse = { id?: string; @@ -1137,12 +1189,12 @@ type BlockedListGetBlockedListDetailsPathParams = { blockedListId: string; }; type BlockedListGetBlockedListDetailsResponse = { - block_type?: string; + block_type?: "inbound" | "outbound" | "threat"; comment?: string; id?: string; - match_type?: string; + match_type?: "phoneNumber" | "prefix"; phone_number?: string; - status?: string; + status?: "active" | "inactive"; }; type BlockedListDeleteBlockedListPathParams = { blockedListId: string; @@ -1151,12 +1203,12 @@ type BlockedListUpdateBlockedListPathParams = { blockedListId: string; }; type BlockedListUpdateBlockedListRequestBody = { - block_type?: string; + block_type?: "inbound" | "outbound"; comment?: string; country?: string; - match_type?: string; + match_type?: "phoneNumber" | "prefix"; phone_number?: string; - status?: string; + status?: "active" | "inactive"; }; type CallHandlingGetCallHandlingSettingsPathParams = { extensionId: string; @@ -1172,8 +1224,8 @@ type CallHandlingGetCallHandlingSettingsResponse = { }; call_distribution?: { handle_multiple_calls?: boolean; - ring_duration?: number; - ring_mode?: string; + ring_duration?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; + ring_mode?: "simultaneous" | "sequential" | "rotating" | "longest_idle"; skip_offline_device_phone_number?: boolean; }; call_forwarding_settings?: { @@ -1188,34 +1240,34 @@ type CallHandlingGetCallHandlingSettingsResponse = { phone_numbers?: string[]; }; }[]; - call_not_answer_action?: number; + call_not_answer_action?: 1 | 2 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14; connect_to_operator?: boolean; custom_hours_settings?: { from?: string; to?: string; - type?: number; - weekday?: number; + type?: 0 | 1 | 2; + weekday?: 1 | 2 | 3 | 4 | 5 | 6 | 7; }[]; greeting_prompt?: { id?: string; name?: string; }; max_call_in_queue?: number; - max_wait_time?: number; + max_wait_time?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 120 | 180 | 240 | 300 | 600 | 900 | 1200 | 1500 | 1800; music_on_hold?: { id?: string; name?: string; }; receive_call?: boolean; require_press_1_before_connecting?: boolean; - ring_mode?: string; + ring_mode?: "simultaneous" | "sequential"; routing?: { - action?: number; + action?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 18 | 19; forward_to?: { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "autoReceptionist" | "callQueue" | "commonArea"; id?: string; phone_number?: string; description?: string; @@ -1234,7 +1286,7 @@ type CallHandlingGetCallHandlingSettingsResponse = { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "commonArea" | "sharedLineGroup" | "callQueue"; id?: string; }; connect_to_operator?: boolean; @@ -1262,7 +1314,7 @@ type CallHandlingGetCallHandlingSettingsResponse = { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "autoReceptionist" | "callQueue" | "commonArea"; id?: string; phone_number?: string; description?: string; @@ -1275,7 +1327,7 @@ type CallHandlingGetCallHandlingSettingsResponse = { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "commonArea" | "sharedLineGroup" | "callQueue"; id?: string; }; connect_to_operator?: boolean; @@ -1297,10 +1349,10 @@ type CallHandlingGetCallHandlingSettingsResponse = { play_callee_voicemail_greeting?: boolean; busy_play_callee_voicemail_greeting?: boolean; }; - type?: number; - wrap_up_time?: number; + type?: 1 | 2; + wrap_up_time?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 120 | 180 | 240 | 300; }; - sub_setting_type?: string; + sub_setting_type?: "call_forwarding" | "custom_hours" | "call_handling"; }[]; closed_hours?: { settings?: { @@ -1317,18 +1369,18 @@ type CallHandlingGetCallHandlingSettingsResponse = { phone_numbers?: string[]; }; }[]; - call_not_answer_action?: number; + call_not_answer_action?: 1 | 2 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14; connect_to_operator?: boolean; - max_wait_time?: number; + max_wait_time?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; require_press_1_before_connecting?: boolean; - ring_mode?: string; + ring_mode?: "simultaneous" | "sequential"; routing?: { - action?: number; + action?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 18 | 19; forward_to?: { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "autoReceptionist" | "callQueue" | "commonArea"; id?: string; phone_number?: string; description?: string; @@ -1347,7 +1399,7 @@ type CallHandlingGetCallHandlingSettingsResponse = { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "commonArea" | "sharedLineGroup" | "callQueue"; id?: string; }; connect_to_operator?: boolean; @@ -1375,7 +1427,7 @@ type CallHandlingGetCallHandlingSettingsResponse = { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "autoReceptionist" | "callQueue" | "commonArea"; id?: string; phone_number?: string; description?: string; @@ -1388,7 +1440,7 @@ type CallHandlingGetCallHandlingSettingsResponse = { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "commonArea" | "sharedLineGroup" | "callQueue"; id?: string; }; connect_to_operator?: boolean; @@ -1411,7 +1463,7 @@ type CallHandlingGetCallHandlingSettingsResponse = { busy_play_callee_voicemail_greeting?: boolean; }; }; - sub_setting_type?: string; + sub_setting_type?: "call_forwarding" | "call_handling"; }[]; holiday_hours?: { details?: { @@ -1429,20 +1481,20 @@ type CallHandlingGetCallHandlingSettingsResponse = { phone_numbers?: string[]; }; }[]; - call_not_answer_action?: number; + call_not_answer_action?: 1 | 2 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14; connect_to_operator?: boolean; from?: string; - max_wait_time?: number; + max_wait_time?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; name?: string; require_press_1_before_connecting?: boolean; - ring_mode?: string; + ring_mode?: "simultaneous" | "sequential"; routing?: { action?: number; forward_to?: { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "autoReceptionist" | "callQueue" | "commonArea"; id?: string; phone_number?: string; description?: string; @@ -1452,7 +1504,7 @@ type CallHandlingGetCallHandlingSettingsResponse = { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "commonArea" | "sharedLineGroup" | "callQueue"; id?: string; }; connect_to_operator?: boolean; @@ -1468,14 +1520,14 @@ type CallHandlingGetCallHandlingSettingsResponse = { }; to?: string; }; - sub_setting_type?: string; + sub_setting_type?: "call_forwarding" | "call_handling" | "holiday"; }[]; holiday_id?: string; }[]; }; type CallHandlingAddCallHandlingSettingPathParams = { extensionId: string; - settingType: string; + settingType: "business_hours" | "closed_hours" | "holiday_hours"; }; type CallHandlingAddCallHandlingSettingRequestBody = { settings?: { @@ -1483,14 +1535,14 @@ type CallHandlingAddCallHandlingSettingRequestBody = { description?: string; phone_number?: string; }; - sub_setting_type?: string; + sub_setting_type?: "call_forwarding"; } | { settings?: { name?: string; from?: string; to?: string; }; - sub_setting_type?: string; + sub_setting_type?: "holiday"; }; type CallHandlingAddCallHandlingSettingResponse = { call_forwarding_id?: string; @@ -1499,7 +1551,7 @@ type CallHandlingAddCallHandlingSettingResponse = { }; type CallHandlingDeleteCallHandlingSettingPathParams = { extensionId: string; - settingType: string; + settingType: "business_hours" | "closed_hours" | "holiday_hours"; }; type CallHandlingDeleteCallHandlingSettingQueryParams = { call_forwarding_id?: string; @@ -1507,7 +1559,7 @@ type CallHandlingDeleteCallHandlingSettingQueryParams = { }; type CallHandlingUpdateCallHandlingSettingPathParams = { extensionId: string; - settingType: string; + settingType: "business_hours" | "closed_hours" | "holiday_hours"; }; type CallHandlingUpdateCallHandlingSettingRequestBody = { settings?: { @@ -1522,7 +1574,7 @@ type CallHandlingUpdateCallHandlingSettingRequestBody = { }[]; require_press_1_before_connecting?: boolean; }; - sub_setting_type?: string; + sub_setting_type?: "call_forwarding"; } | { settings?: { from?: string; @@ -1530,19 +1582,19 @@ type CallHandlingUpdateCallHandlingSettingRequestBody = { name?: string; to?: string; }; - sub_setting_type?: string; + sub_setting_type?: "holiday"; } | { settings?: { allow_members_to_reset?: boolean; custom_hours_settings?: { from?: string; to?: string; - type?: number; - weekday?: number; + type?: 0 | 1 | 2; + weekday?: 1 | 2 | 3 | 4 | 5 | 6 | 7; }[]; - type?: number; + type?: 1 | 2; }; - sub_setting_type?: string; + sub_setting_type?: "custom_hours"; } | { settings?: { allow_callers_check_voicemail?: boolean; @@ -1550,12 +1602,12 @@ type CallHandlingUpdateCallHandlingSettingRequestBody = { audio_while_connecting_id?: string; call_distribution?: { handle_multiple_calls?: boolean; - ring_duration?: number; - ring_mode?: string; + ring_duration?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; + ring_mode?: "simultaneous" | "sequential" | "rotating" | "longest_idle"; skip_offline_device_phone_number?: boolean; }; - call_not_answer_action?: number; - busy_on_another_call_action?: number; + call_not_answer_action?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 18 | 19; + busy_on_another_call_action?: 1 | 2 | 4 | 6 | 7 | 8 | 9 | 10 | 12 | 21 | 22; busy_require_press_1_before_connecting?: boolean; un_answered_require_press_1_before_connecting?: boolean; overflow_play_callee_voicemail_greeting?: boolean; @@ -1570,20 +1622,20 @@ type CallHandlingUpdateCallHandlingSettingRequestBody = { busy_forward_to_extension_id?: string; greeting_prompt_id?: string; max_call_in_queue?: number; - max_wait_time?: number; + max_wait_time?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 120 | 180 | 240 | 300 | 600 | 900 | 1200 | 1500 | 1800; music_on_hold_id?: string; operator_extension_id?: string; receive_call?: boolean; - ring_mode?: string; + ring_mode?: "simultaneous" | "sequential"; voicemail_greeting_id?: string; voicemail_leaving_instruction_id?: string; message_greeting_id?: string; forward_to_zcc_phone_number?: string; forward_to_partner_contact_center_id?: string; forward_to_teams_id?: string; - wrap_up_time?: number; + wrap_up_time?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 120 | 180 | 240 | 300; }; - sub_setting_type?: string; + sub_setting_type?: "call_handling"; }; type CallLogsGetAccountsCallHistoryQueryParams = { page_size?: number; @@ -1591,43 +1643,46 @@ type CallLogsGetAccountsCallHistoryQueryParams = { to?: string; next_page_token?: string; keyword?: string; - directions?: string[]; - connect_types?: string[]; - number_types?: string[]; - call_types?: string[]; - extension_types?: string[]; - call_results?: string[]; + directions?: ("inbound" | "outbound")[]; + connect_types?: ("internal" | "external")[]; + number_types?: ("zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zoom_revenue_accelerator")[]; + call_types?: ("general" | "emergency")[]; + extension_types?: ("user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact")[]; + call_results?: ("answered" | "accepted" | "picked_up" | "connected" | "succeeded" | "voicemail" | "hang_up" | "canceled" | "call_failed" | "unconnected" | "rejected" | "busy" | "ring_timeout" | "overflowed" | "no_answer" | "invalid_key" | "invalid_operation" | "abandoned" | "system_blocked" | "service_unavailable")[]; group_ids?: string[]; site_ids?: string[]; department?: string; cost_center?: string; - time_type?: string; - recording_status?: string; + time_type?: "start_time" | "end_time"; + recording_status?: "recorded" | "non_recorded"; + with_voicemail?: boolean; }; type CallLogsGetAccountsCallHistoryResponse = { call_logs?: { id?: string; call_id?: string; - direction?: string; + direction?: "inbound" | "outbound"; international?: boolean; start_time?: string; answer_time?: string; end_time?: string; duration?: number; - connect_type?: string; + connect_type?: "internal" | "external"; sbc_id?: string; sbc_name?: string; sip_group_id?: string; sip_group_name?: string; - call_type?: string; - call_result?: string; + call_type?: "general" | "emergency"; + call_result?: "answered" | "accepted" | "picked_up" | "connected" | "succeeded" | "voicemail" | "hang_up" | "canceled" | "call_failed" | "unconnected" | "rejected" | "busy" | "ring_timeout" | "overflowed" | "no_answer" | "invalid_key" | "invalid_operation" | "abandoned" | "system_blocked" | "service_unavailable"; + hide_caller_id?: boolean; + end_to_end?: boolean; caller_ext_id?: string; caller_did_number?: string; caller_ext_number?: string; caller_name?: string; caller_email?: string; - caller_ext_type?: string; - caller_number_type?: string; + caller_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + caller_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zoom_revenue_accelerator"; caller_device_type?: string; caller_country_iso_code?: string; caller_country_code?: string; @@ -1636,19 +1691,18 @@ type CallLogsGetAccountsCallHistoryResponse = { callee_ext_number?: string; callee_name?: string; callee_email?: string; - callee_ext_type?: string; - callee_number_type?: string; + callee_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + callee_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zoom_revenue_accelerator"; callee_device_type?: string; callee_country_iso_code?: string; callee_country_code?: string; - client_code?: string; department?: string; cost_center?: string; site_id?: string; group_id?: string; site_name?: string; spam?: string; - recording_status?: string; + recording_status?: "recorded" | "non_recorded"; }[]; from?: string; to?: string; @@ -1663,22 +1717,24 @@ type CallLogsGetCallPathPathParams = { type CallLogsGetCallPathResponse = { id?: string; call_id?: string; - connect_type?: string; - call_type?: string; - direction?: string; + connect_type?: "internal" | "external"; + call_type?: "general" | "emergency"; + direction?: "inbound" | "outbound"; international?: boolean; + hide_caller_id?: boolean; + end_to_end?: boolean; caller_ext_id?: string; caller_name?: string; caller_did_number?: string; caller_ext_number?: string; caller_email?: string; - caller_ext_type?: string; + caller_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; callee_ext_id?: string; callee_name?: string; callee_email?: string; callee_did_number?: string; callee_ext_number?: string; - callee_ext_type?: string; + callee_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; department?: string; cost_center?: string; site_id?: string; @@ -1690,16 +1746,18 @@ type CallLogsGetCallPathResponse = { call_path?: { id?: string; call_id?: string; - connect_type?: string; - call_type?: string; - direction?: string; + connect_type?: "internal" | "external"; + call_type?: "general" | "emergency"; + direction?: "inbound" | "outbound"; + hide_caller_id?: boolean; + end_to_end?: boolean; caller_ext_id?: string; caller_name?: string; caller_email?: string; caller_did_number?: string; caller_ext_number?: string; - caller_ext_type?: string; - caller_number_type?: string; + caller_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + caller_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zra_phone_number"; caller_device_type?: string; caller_country_iso_code?: string; caller_country_code?: string; @@ -1708,8 +1766,8 @@ type CallLogsGetCallPathResponse = { callee_did_number?: string; callee_ext_number?: string; callee_email?: string; - callee_ext_type?: string; - callee_number_type?: string; + callee_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + callee_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zra_phone_number"; callee_device_type?: string; callee_country_iso_code?: string; callee_country_code?: string; @@ -1722,21 +1780,21 @@ type CallLogsGetCallPathResponse = { start_time?: string; answer_time?: string; end_time?: string; - event?: string; - result?: string; - result_reason?: string; + event?: "incoming" | "transfer_from_zoom_contact_center" | "shared_line_incoming" | "outgoing" | "call_me_on" | "outgoing_to_zoom_contact_center" | "warm_transfer" | "forward" | "ring_to_member" | "overflow" | "direct_transfer" | "barge" | "monitor" | "whisper" | "listen" | "takeover" | "conference_barge" | "park" | "timeout" | "park_pick_up" | "merge" | "shared"; + result?: "answered" | "accepted" | "picked_up" | "connected" | "succeeded" | "voicemail" | "hang_up" | "canceled" | "call_failed" | "unconnected" | "rejected" | "busy" | "ring_timeout" | "overflowed" | "no_answer" | "invalid_key" | "invalid_operation" | "abandoned" | "system_blocked" | "service_unavailable"; + result_reason?: "answered_by_other" | "pickup_by_other" | "call_out_by_other"; device_private_ip?: string; device_public_ip?: string; operator_ext_number?: string; operator_ext_id?: string; - operator_ext_type?: string; + operator_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; operator_name?: string; press_key?: string; segment?: number; node?: number; is_node?: number; recording_id?: string; - recording_type?: string; + recording_type?: "automatic" | "ad-hoc"; hold_time?: number; waiting_time?: number; voicemail_id?: string; @@ -1748,6 +1806,66 @@ type CallLogsAddClientCodeToCallHistoryPathParams = { type CallLogsAddClientCodeToCallHistoryRequestBody = { client_code: string; }; +type CallLogsGetCallHistoryDetailPathParams = { + callHistoryId: string; +}; +type CallLogsGetCallHistoryDetailResponse = { + id?: string; + call_path_id?: string; + call_id?: string; + connect_type?: "internal" | "external"; + call_type?: "general" | "emergency"; + direction?: "inbound" | "outbound"; + hide_caller_id?: boolean; + end_to_end?: boolean; + caller_ext_id?: string; + caller_name?: string; + caller_email?: string; + caller_did_number?: string; + caller_ext_number?: string; + caller_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + caller_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zra_phone_number"; + caller_device_type?: string; + caller_country_iso_code?: string; + caller_country_code?: string; + callee_ext_id?: string; + callee_name?: string; + callee_did_number?: string; + callee_ext_number?: string; + callee_email?: string; + callee_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + callee_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zra_phone_number"; + callee_device_type?: string; + callee_country_iso_code?: string; + callee_country_code?: string; + client_code?: string; + department?: string; + cost_center?: string; + site_id?: string; + group_id?: string; + site_name?: string; + start_time?: string; + answer_time?: string; + end_time?: string; + event?: "incoming" | "transfer_from_zoom_contact_center" | "shared_line_incoming" | "outgoing" | "call_me_on" | "outgoing_to_zoom_contact_center" | "warm_transfer" | "forward" | "ring_to_member" | "overflow" | "direct_transfer" | "barge" | "monitor" | "whisper" | "listen" | "takeover" | "conference_barge" | "park" | "timeout" | "park_pick_up" | "merge" | "shared"; + result?: "answered" | "accepted" | "picked_up" | "connected" | "succeeded" | "voicemail" | "hang_up" | "canceled" | "call_failed" | "unconnected" | "rejected" | "busy" | "ring_timeout" | "overflowed" | "no_answer" | "invalid_key" | "invalid_operation" | "abandoned" | "system_blocked" | "service_unavailable"; + result_reason?: "answered_by_other" | "pickup_by_other" | "call_out_by_other"; + device_private_ip?: string; + device_public_ip?: string; + operator_ext_number?: string; + operator_ext_id?: string; + operator_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + operator_name?: string; + press_key?: string; + segment?: number; + node?: number; + is_node?: number; + recording_id?: string; + recording_type?: "ad-hoc" | "automatic"; + hold_time?: number; + waiting_time?: number; + voicemail_id?: string; +}; type CallLogsGetAccountsCallLogsQueryParams = { page_size?: number; from?: string; @@ -1755,7 +1873,7 @@ type CallLogsGetAccountsCallLogsQueryParams = { type?: string; next_page_token?: string; path?: string; - time_type?: string; + time_type?: "startTime" | "endTime"; site_id?: string; charged_call_logs?: boolean; }; @@ -1764,21 +1882,21 @@ type CallLogsGetAccountsCallLogsResponse = { answer_start_time?: string; call_end_time?: string; call_id?: string; - call_type?: string; + call_type?: "voip" | "pstn" | "tollfree" | "international" | "contactCenter"; callee_country_code?: string; callee_country_iso_code?: string; callee_did_number?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; - callee_number_source?: string; + callee_number_type?: 1 | 2 | 3; + callee_number_source?: "internal" | "external" | "byop"; caller_country_code?: string; caller_country_iso_code?: string; caller_did_number?: string; caller_name?: string; caller_number?: string; - caller_number_type?: number; - caller_number_source?: string; + caller_number_type?: 1 | 2; + caller_number_source?: "internal" | "external" | "byop"; caller_billing_reference_id?: string; charge?: string; client_code?: string; @@ -1792,12 +1910,12 @@ type CallLogsGetAccountsCallLogsResponse = { extension_number?: number; id?: string; name?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "sharedLineGroup"; }; path?: string; rate?: string; recording_id?: string; - recording_type?: string; + recording_type?: "OnDemand" | "Automatic"; result?: string; site?: { id?: string; @@ -1821,30 +1939,30 @@ type CallLogsGetCallLogDetailsPathParams = { }; type CallLogsGetCallLogDetailsResponse = { call_id?: string; - call_type?: string; + call_type?: "voip" | "pstn" | "tollfree" | "international" | "contactCenter"; callee_country_code?: string; callee_country_iso_code?: string; callee_did_number?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; - callee_number_source?: string; - callee_status?: string; + callee_number_type?: 1 | 2 | 3; + callee_number_source?: "internal" | "external" | "byop"; + callee_status?: "inactive" | "deleted"; callee_deleted_time?: string; caller_country_code?: string; caller_country_iso_code?: string; caller_did_number?: string; caller_name?: string; caller_number?: string; - caller_number_type?: number; - caller_number_source?: string; + caller_number_type?: 1 | 2; + caller_number_source?: "internal" | "external" | "byop"; caller_billing_reference_id?: string; - caller_status?: string; + caller_status?: "inactive" | "deleted"; caller_deleted_time?: string; date_time?: string; device_private_ip?: string; device_public_ip?: string; - direction?: string; + direction?: "inbound" | "outbound"; duration?: number; has_recording?: boolean; has_voicemail?: boolean; @@ -1859,8 +1977,8 @@ type CallLogsGetCallLogDetailsResponse = { extension_number?: string; id?: string; name?: string; - type?: string; - extension_status?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; + extension_status?: "inactive" | "deleted"; extension_deleted_time?: string; }; id?: string; @@ -1882,6 +2000,24 @@ type CallLogsAddClientCodeToCallLogPathParams = { type CallLogsAddClientCodeToCallLogRequestBody = { client_code: string; }; +type CallLogsGetUserAICallSummaryDetailPathParams = { + userId: string; + aiCallSummaryId: string; +}; +type CallLogsGetUserAICallSummaryDetailResponse = { + ai_call_summary_id?: string; + account_id?: string; + call_id?: string; + user_id?: string; + call_summary_rate?: "thumb_up" | "thumb_down"; + transcript_language?: string; + call_summary?: string; + next_steps?: string; + detailed_summary?: string; + created_time?: string; + modified_time?: string; + edited?: boolean; +}; type CallLogsGetUsersCallHistoryPathParams = { userId: string; }; @@ -1891,35 +2027,39 @@ type CallLogsGetUsersCallHistoryQueryParams = { to?: string; next_page_token?: string; keyword?: string; - directions?: string[]; - connect_types?: string[]; - number_types?: string[]; - call_types?: string[]; - extension_types?: string[]; - call_results?: string[]; + directions?: ("inbound" | "outbound")[]; + connect_types?: ("internal" | "external")[]; + number_types?: ("zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zoom_revenue_accelerator")[]; + call_types?: ("general" | "emergency")[]; + extension_types?: ("user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact")[]; + call_results?: ("answered" | "connected" | "voicemail" | "hang_up" | "no_answer" | "invalid_operation" | "abandoned" | "blocked" | "service_unavailable")[]; group_ids?: string[]; site_ids?: string[]; department?: string; cost_center?: string; - time_type?: string; - recording_status?: string; + time_type?: "start_time" | "end_time"; + recording_status?: "recorded" | "non_recorded"; + with_voicemail?: boolean; }; type CallLogsGetUsersCallHistoryResponse = { call_logs?: { id?: string; + call_path_id?: string; call_id?: string; group_id?: string; - connect_type?: string; - call_type?: string; - direction?: string; + connect_type?: "internal" | "external"; + call_type?: "general" | "emergency"; + direction?: "inbound" | "outbound"; + hide_caller_id?: boolean; + end_to_end?: boolean; caller_ext_id?: string; caller_name?: string; caller_email?: string; caller_employee_id?: string; caller_did_number?: string; caller_ext_number?: string; - caller_ext_type?: string; - caller_number_type?: string; + caller_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + caller_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zoom_revenue_accelerator"; caller_device_private_ip?: string; caller_device_public_ip?: string; caller_device_type?: string; @@ -1934,8 +2074,8 @@ type CallLogsGetUsersCallHistoryResponse = { callee_ext_number?: string; callee_email?: string; callee_employee_id?: string; - callee_ext_type?: string; - callee_number_type?: string; + callee_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + callee_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zoom_revenue_accelerator"; callee_device_private_ip?: string; callee_device_public_ip?: string; callee_device_type?: string; @@ -1947,15 +2087,15 @@ type CallLogsGetUsersCallHistoryResponse = { start_time?: string; answer_time?: string; end_time?: string; - event?: string; - result?: string; - result_reason?: string; + event?: "incoming" | "transfer_from_zoom_contact_center" | "shared_line_incoming" | "outgoing" | "call_me_on" | "outgoing_to_zoom_contact_center" | "warm_transfer" | "forward" | "ring_to_member" | "overflow" | "direct_transfer" | "barge" | "monitor" | "whisper" | "listen" | "takeover" | "conference_barge" | "park" | "timeout" | "park_pick_up" | "merge" | "shared"; + result?: "answered" | "accepted" | "picked_up" | "connected" | "succeeded" | "voicemail" | "hang_up" | "canceled" | "call_failed" | "unconnected" | "rejected" | "busy" | "ring_timeout" | "overflowed" | "no_answer" | "invalid_key" | "invalid_operation" | "abandoned" | "system_blocked" | "service_unavailable"; + result_reason?: "answered_by_other" | "pickup_by_other" | "call_out_by_other"; operator_ext_number?: string; operator_ext_id?: string; - operator_ext_type?: string; + operator_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; operator_name?: string; recording_id?: string; - recording_type?: string; + recording_type?: "ad-hoc" | "automatic"; voicemail_id?: string; talk_time?: number; hold_time?: number; @@ -1979,19 +2119,22 @@ type CallLogsSyncUsersCallHistoryQueryParams = { type CallLogsSyncUsersCallHistoryResponse = { call_logs?: { id?: string; + call_path_id?: string; call_id?: string; group_id?: string; - connect_type?: string; - call_type?: string; - direction?: string; + connect_type?: "internal" | "external"; + call_type?: "general" | "emergency"; + direction?: "inbound" | "outbound"; + hide_caller_id?: boolean; + end_to_end?: boolean; caller_ext_id?: string; caller_name?: string; caller_email?: string; caller_employee_id?: string; caller_did_number?: string; caller_ext_number?: string; - caller_ext_type?: string; - caller_number_type?: string; + caller_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + caller_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zoom_revenue_accelerator"; caller_device_private_ip?: string; caller_device_public_ip?: string; caller_device_type?: string; @@ -2006,8 +2149,8 @@ type CallLogsSyncUsersCallHistoryResponse = { callee_ext_number?: string; callee_email?: string; callee_employee_id?: string; - callee_ext_type?: string; - callee_number_type?: string; + callee_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + callee_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number" | "zoom_revenue_accelerator"; callee_device_private_ip?: string; callee_device_public_ip?: string; callee_device_type?: string; @@ -2019,15 +2162,15 @@ type CallLogsSyncUsersCallHistoryResponse = { start_time?: string; answer_time?: string; end_time?: string; - event?: string; - result?: string; - result_reason?: string; + event?: "incoming" | "transfer_from_zoom_contact_center" | "shared_line_incoming" | "outgoing" | "call_me_on" | "outgoing_to_zoom_contact_center" | "warm_transfer" | "forward" | "ring_to_member" | "overflow" | "direct_transfer" | "barge" | "monitor" | "whisper" | "listen" | "takeover" | "conference_barge" | "park" | "timeout" | "park_pick_up" | "merge" | "shared"; + result?: "answered" | "accepted" | "picked_up" | "connected" | "succeeded" | "voicemail" | "hang_up" | "canceled" | "call_failed" | "unconnected" | "rejected" | "busy" | "ring_timeout" | "overflowed" | "no_answer" | "invalid_key" | "invalid_operation" | "abandoned" | "system_blocked" | "service_unavailable"; + result_reason?: "answered_by_other" | "pickup_by_other" | "call_out_by_other"; operator_ext_number?: string; operator_ext_id?: string; - operator_ext_type?: string; + operator_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; operator_name?: string; recording_id?: string; - recording_type?: string; + recording_type?: "ad-hoc" | "automatic"; voicemail_id?: string; talk_time?: number; hold_time?: number; @@ -2047,10 +2190,10 @@ type CallLogsGetUsersCallLogsQueryParams = { page_size?: number; from?: string; to?: string; - type?: string; + type?: "all" | "missed"; next_page_token?: string; phone_number?: string; - time_type?: string; + time_type?: "startTime" | "endTime"; }; type CallLogsGetUsersCallLogsResponse = { call_logs?: { @@ -2069,15 +2212,15 @@ type CallLogsGetUsersCallLogsResponse = { callee_did_number?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; - callee_number_source?: string; + callee_number_type?: 1 | 2 | 3; + callee_number_source?: "internal" | "external" | "byop"; caller_country_code?: string; caller_country_iso_code?: string; caller_did_number?: string; caller_name?: string; caller_number?: string; - caller_number_type?: number; - caller_number_source?: string; + caller_number_type?: 1 | 2; + caller_number_source?: "internal" | "external" | "byop"; caller_billing_reference_id?: string; charge?: string; client_code?: string; @@ -2086,7 +2229,7 @@ type CallLogsGetUsersCallLogsResponse = { duration?: number; forwarded_by?: { extension_number?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "commonAreaPhone" | "autoReceptionist" | "sharedLineGroup"; location?: string; name?: string; number_type?: number; @@ -2111,7 +2254,7 @@ type CallLogsGetUsersCallLogsResponse = { }; path?: string; rate?: string; - recording_type?: string; + recording_type?: "OnDemand" | "Automatic"; result?: string; site?: { id?: string; @@ -2134,7 +2277,7 @@ type CallLogsSyncUsersCallLogsPathParams = { userId: string; }; type CallLogsSyncUsersCallLogsQueryParams = { - sync_type?: string; + sync_type?: "FSync" | "ISync" | "BSync"; count?: number; sync_token?: string; }; @@ -2156,16 +2299,16 @@ type CallLogsSyncUsersCallLogsResponse = { callee_did_number?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; - callee_number_source?: string; + callee_number_type?: 1 | 2 | 3; + callee_number_source?: "internal" | "external" | "byop"; caller_user_id?: string; caller_country_code?: string; caller_country_iso_code?: string; caller_did_number?: string; caller_name?: string; caller_number?: string; - caller_number_type?: number; - caller_number_source?: string; + caller_number_type?: 1 | 2; + caller_number_source?: "internal" | "external" | "byop"; caller_billing_reference_id?: string; charge?: string; client_code?: string; @@ -2174,7 +2317,7 @@ type CallLogsSyncUsersCallLogsResponse = { duration?: number; forwarded_by?: { extension_number?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "commonAreaPhone" | "autoReceptionist" | "sharedLineGroup"; location?: string; name?: string; number_type?: number; @@ -2199,7 +2342,7 @@ type CallLogsSyncUsersCallLogsResponse = { }; path?: string; rate?: string; - recording_type?: string; + recording_type?: "OnDemand" | "Automatic"; result?: string; site?: { id?: string; @@ -2215,6 +2358,40 @@ type CallLogsDeleteUsersCallLogPathParams = { userId: string; callLogId: string; }; +type CallQueuesListCallQueueAnalyticsQueryParams = { + page_size?: number; + from?: string; + to?: string; + next_page_token?: string; + site_id?: string; + call_queue_ext_ids?: string[]; + department?: string; + cost_center?: string; +}; +type CallQueuesListCallQueueAnalyticsResponse = { + call_queues?: { + call_queue_id?: string; + call_queue_name?: string; + call_queue_ext_id?: string; + inbound_calls?: number; + completed_calls?: number; + abandoned_calls?: number; + overflowed_calls?: number; + avg_handle_time?: number; + avg_wrap_up_time?: number; + avg_in_queue_wait_time?: number; + max_in_queue_wait_time?: number; + outbound_calls?: number; + outbound_connected_calls?: number; + outbound_unconnected_calls?: number; + site_name?: string; + site_id?: string; + }[]; + from?: string; + to?: string; + page_size?: number; + next_page_token?: string; +}; type CallQueuesListCallQueuesQueryParams = { next_page_token?: string; page_size?: number; @@ -2231,13 +2408,13 @@ type CallQueuesListCallQueuesResponse = { phone_numbers?: { id?: string; number?: string; - source?: string; + source?: "internal" | "external"; }[]; site?: { id?: string; name?: string; }; - status?: string; + status?: "active" | "inactive"; }[]; next_page_token?: string; page_size?: number; @@ -2276,7 +2453,7 @@ type CallQueuesGetCallQueueDetailsResponse = { members?: { users?: { id?: string; - level?: string; + level?: "manager" | "user"; name?: string; receive_call?: boolean; extension_id?: string; @@ -2291,16 +2468,17 @@ type CallQueuesGetCallQueueDetailsResponse = { phone_numbers?: { id?: string; number?: string; - source?: string; + source?: "internal" | "external"; }[]; site?: { id?: string; name?: string; }; - status?: string; + status?: "active" | "inactive"; policy?: { voicemail_access_members?: ({ access_user_id?: string; + access_user_type?: "commonArea" | "user"; allow_download?: boolean; allow_delete?: boolean; allow_sharing?: boolean; @@ -2309,8 +2487,8 @@ type CallQueuesGetCallQueueDetailsResponse = { })[]; }; timezone?: string; - audio_prompt_language?: string; - recording_storage_location?: string; + audio_prompt_language?: "en-US" | "en-GB" | "es-US" | "fr-CA" | "da-DK" | "de-DE" | "es-ES" | "fr-FR" | "it-IT" | "nl-NL" | "pt-PT" | "ja" | "ko-KR" | "pt-BR" | "zh-CN"; + recording_storage_location?: "US" | "AU" | "CA" | "DE" | "IN" | "JP" | "SG" | "BR" | "CN" | "MX"; own_storage_name?: string; }; type CallQueuesDeleteCallQueuePathParams = { @@ -2326,10 +2504,10 @@ type CallQueuesUpdateCallQueueDetailsRequestBody = { extension_number?: number; name?: string; site_id?: string; - status?: string; + status?: "active" | "inactive"; timezone?: string; - audio_prompt_language?: string; - recording_storage_location?: string; + audio_prompt_language?: "en-US" | "en-GB" | "es-US" | "fr-CA" | "da-DK" | "de-DE" | "es-ES" | "fr-FR" | "it-IT" | "nl-NL" | "pt-PT" | "ja" | "ko-KR" | "pt-BR" | "zh-CN"; + recording_storage_location?: "US" | "AU" | "CA" | "DE" | "IN" | "JP" | "SG" | "BR" | "CN" | "MX"; }; type CallQueuesListCallQueueMembersPathParams = { callQueueId: string; @@ -2337,7 +2515,7 @@ type CallQueuesListCallQueueMembersPathParams = { type CallQueuesListCallQueueMembersResponse = { call_queue_members?: { id?: string; - level?: string; + level?: "commonArea" | "user"; name?: string; receive_call?: boolean; extension_id?: string; @@ -2381,21 +2559,23 @@ type CallQueuesUnassignPhoneNumberPathParams = { callQueueId: string; phoneNumberId: string; }; -type CallQueuesAddPolicySettingToCallQueuePathParams = { +type CallQueuesAddPolicySubsettingToCallQueuePathParams = { callQueueId: string; policyType: string; }; -type CallQueuesAddPolicySettingToCallQueueRequestBody = { +type CallQueuesAddPolicySubsettingToCallQueueRequestBody = { voicemail_access_members?: { access_user_id?: string; + access_user_type?: "commonArea" | "user"; allow_download?: boolean; allow_delete?: boolean; allow_sharing?: boolean; }[]; }; -type CallQueuesAddPolicySettingToCallQueueResponse = { +type CallQueuesAddPolicySubsettingToCallQueueResponse = { voicemail_access_members?: ({ access_user_id?: string; + access_user_type?: "commonArea" | "user"; allow_download?: boolean; allow_delete?: boolean; allow_sharing?: boolean; @@ -2417,6 +2597,7 @@ type CallQueuesUpdateCallQueuesPolicySubsettingPathParams = { type CallQueuesUpdateCallQueuesPolicySubsettingRequestBody = { voicemail_access_members?: ({ access_user_id?: string; + access_user_type?: "commonArea" | "user"; allow_download?: boolean; allow_delete?: boolean; allow_sharing?: boolean; @@ -2440,10 +2621,10 @@ type CallQueuesGetCallQueueRecordingsResponse = { recordings?: { callee_name?: string; callee_number?: string; - callee_number_type?: string; + callee_number_type?: "1" | "2" | "3"; caller_name?: string; caller_number?: string; - caller_number_type?: number; + caller_number_type?: 1 | 2; date_time?: string; direction?: string; download_url?: string; @@ -2456,17 +2637,17 @@ type CallQueuesGetCallQueueRecordingsResponse = { type CarrierResellerListPhoneNumbersQueryParams = { page_size?: number; next_page_token?: string; - assigned_status?: string; + assigned_status?: "assigned" | "unassigned" | "returned"; sub_account_id?: string; keyword?: string; }; type CarrierResellerListPhoneNumbersResponse = { carrier_reseller_numbers?: { - assigned_status?: string; + assigned_status?: "assigned" | "unassigned" | "returned"; carrier_code?: number; country_iso_code?: string; phone_number?: string; - status?: string; + status?: "inactive" | "active"; sub_account_id?: string; sub_account_name?: string; }[]; @@ -2476,7 +2657,7 @@ type CarrierResellerListPhoneNumbersResponse = { }; type CarrierResellerCreatePhoneNumbersRequestBody = { phone_number?: string; - status?: string; + status?: "active" | "inactive"; }[]; type CarrierResellerActivatePhoneNumbersRequestBody = string[]; type CarrierResellerDeletePhoneNumberPathParams = { @@ -2493,6 +2674,8 @@ type CommonAreasListCommonAreasResponse = { type?: number; billing_account_id?: string; billing_account_name?: string; + billing_subscription_id?: string; + billing_subscription_name?: string; }[]; display_name?: string; extension_number?: number; @@ -2501,18 +2684,18 @@ type CommonAreasListCommonAreasResponse = { display_name?: string; id?: string; number?: string; - source?: string; + source?: "internal" | "external"; }[]; site?: { id?: string; name?: string; }; - status?: string; + status?: "online" | "offline"; desk_phones?: { id?: string; display_name?: string; device_type?: string; - status?: string; + status?: "online" | "offline"; }[]; }[]; next_page_token?: string; @@ -2521,6 +2704,7 @@ type CommonAreasListCommonAreasResponse = { type CommonAreasAddCommonAreaRequestBody = { calling_plans?: { type?: number; + billing_subscription_id?: string; }[]; country_iso_code?: string; display_name: string; @@ -2533,6 +2717,18 @@ type CommonAreasAddCommonAreaResponse = { display_name?: string; id?: string; }; +type CommonAreasGenerateActivationCodesForCommonAreasRequestBody = { + common_area_ids: string[]; +}; +type CommonAreasGenerateActivationCodesForCommonAreasResponse = { + common_areas_activation_codes?: { + common_area_id?: string; + display_name?: string; + extension_number?: number; + activation_code?: string; + activation_code_expiration?: string; + }[]; +}; type CommonAreasListActivationCodesQueryParams = { page_size?: number; next_page_token?: string; @@ -2544,7 +2740,7 @@ type CommonAreasListActivationCodesResponse = { extension_number?: number; activation_code?: string; activation_code_expiration?: string; - status?: string; + status?: "used" | "not_used"; site?: { site_id?: string; name?: string; @@ -2569,6 +2765,8 @@ type CommonAreasGetCommonAreaDetailsResponse = { type?: number; billing_account_id?: string; billing_account_name?: string; + billing_subscription_id?: string; + billing_subscription_name?: string; }[]; cost_center?: string; country?: { @@ -2586,7 +2784,7 @@ type CommonAreasGetCommonAreaDetailsResponse = { country?: string; id?: string; state_code?: string; - status?: number; + status?: 1 | 2 | 3 | 4 | 5 | 6; zip?: string; }; id?: string; @@ -2599,13 +2797,13 @@ type CommonAreasGetCommonAreaDetailsResponse = { display_name?: string; id?: string; number?: string; - source?: string; + source?: "internal" | "external"; }[]; policy?: { international_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "site"; modified?: boolean; }; outbound_calling?: { @@ -2618,7 +2816,7 @@ type CommonAreasGetCommonAreaDetailsResponse = { id?: string; name?: string; }; - status?: string; + status?: "online" | "offline"; }; type CommonAreasDeleteCommonAreaPathParams = { commonAreaId: string; @@ -2651,6 +2849,7 @@ type CommonAreasAssignCallingPlansToCommonAreaRequestBody = { calling_plans: { type: number; billing_account_id?: string; + billing_subscription_id?: string; }[]; }; type CommonAreasAssignCallingPlansToCommonAreaResponse = { @@ -2701,23 +2900,23 @@ type CommonAreasGetCommonAreaSettingsResponse = { id?: string; display_name?: string; device_type?: string; - status?: string; + status?: "online" | "offline"; mac_address?: string; hot_desking?: { - status?: string; + status?: "unsupported" | "on" | "off"; }; private_ip?: string; public_ip?: string; }[]; }; -type CommonAreasAddCommonAreaSettingsPathParams = { +type CommonAreasAddCommonAreaSettingPathParams = { commonAreaId: string; settingType: string; }; -type CommonAreasAddCommonAreaSettingsRequestBody = { +type CommonAreasAddCommonAreaSettingRequestBody = { device_id?: string; }; -type CommonAreasAddCommonAreaSettingsResponse = { +type CommonAreasAddCommonAreaSettingResponse = { desk_phones?: { id?: string; display_name?: string; @@ -2730,15 +2929,15 @@ type CommonAreasDeleteCommonAreaSettingPathParams = { type CommonAreasDeleteCommonAreaSettingQueryParams = { device_id: string; }; -type CommonAreasUpdateCommonAreaSettingsPathParams = { +type CommonAreasUpdateCommonAreaSettingPathParams = { commonAreaId: string; settingType: string; }; -type CommonAreasUpdateCommonAreaSettingsRequestBody = { +type CommonAreasUpdateCommonAreaSettingRequestBody = { desk_phones?: { id?: string; hot_desking?: { - status?: string; + status?: "on" | "off"; }; }[]; }; @@ -2863,7 +3062,7 @@ type DashboardGetCallDetailsFromCallLogResponse = { phone_number?: string; site_id?: string; id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; display_name?: string; }; caller?: { @@ -2877,19 +3076,172 @@ type DashboardGetCallDetailsFromCallLogResponse = { microphone?: string; phone_number?: string; site_id?: string; - id?: string; - extension_type?: string; - display_name?: string; - }; - date_time?: string; - direction?: string; - duration?: number; - mos?: string; + id?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; + display_name?: string; + }; + date_time?: string; + direction?: string; + duration?: number; + mos?: string; +}; +type DashboardListDefaultEmergencyAddressUsersQueryParams = { + status: "set" | "not_set"; + site_id?: string; + keyword?: string; + page_size?: number; + next_page_token?: string; +}; +type DashboardListDefaultEmergencyAddressUsersResponse = { + next_page_token?: string; + page_size?: number; + total_records?: number; + records?: { + email?: string; + user_display_name?: string; + extension_id?: string; + extension_number?: number; + site_name?: string; + site_id?: string; + status?: "set" | "not_set"; + }[]; +}; +type DashboardListDetectablePersonalLocationUsersQueryParams = { + status: "set" | "not_set"; + site_id?: string; + keyword?: string; + page_size?: number; + next_page_token?: string; +}; +type DashboardListDetectablePersonalLocationUsersResponse = { + next_page_token?: string; + page_size?: number; + total_records?: number; + records?: { + email?: string; + user_display_name?: string; + extension_id?: string; + extension_number?: number; + site_name?: string; + site_id?: string; + status?: "set" | "not_set"; + }[]; +}; +type DashboardListUsersPermissionForLocationSharingQueryParams = { + site_id?: string; + keyword?: string; + page_size?: number; + next_page_token?: string; +}; +type DashboardListUsersPermissionForLocationSharingResponse = { + next_page_token?: string; + page_size?: number; + total_records?: number; + user_permissions?: { + email?: string; + user_display_name?: string; + extension_id?: string; + extension_number?: number; + site_name?: string; + site_id?: string; + device_permissions?: { + last_seen_time?: number; + location_sharing?: "allowed" | "disallowed"; + platform?: string; + }[]; + }[]; +}; +type DashboardListNomadicEmergencyServicesUsersQueryParams = { + status: "enabled" | "disabled"; + site_id?: string; + keyword?: string; + page_size?: number; + next_page_token?: string; +}; +type DashboardListNomadicEmergencyServicesUsersResponse = { + next_page_token?: string; + page_size?: number; + total_records?: number; + records?: { + email?: string; + user_display_name?: string; + extension_id?: string; + extension_number?: number; + site_name?: string; + site_id?: string; + status?: "enabled" | "disabled"; + reason_for_disabling?: 1 | 2 | 3 | 4 | 5; + }[]; +}; +type DashboardListRealTimeLocationForIPPhonesQueryParams = { + location_type: "company" | "personal" | "unknown"; + site_id?: string; + keyword?: string; + page_size?: number; + next_page_token?: string; +}; +type DashboardListRealTimeLocationForIPPhonesResponse = { + records?: { + device_id?: string; + device_name?: string; + site_id?: string; + site_name?: string; + public_ip?: string; + private_ip?: string; + bssid?: string; + emergency_address?: string; + mac_address?: string; + location_name?: string; + network_switch?: { + port?: string; + mac_address?: string; + }; + location_type?: "company" | "personal" | "unknown"; + assigned_info?: { + extension_id?: string; + extension_number?: number; + user_email?: string; + user_display_name?: string; + }[]; + }[]; + total_records?: number; + page_size?: number; + next_page_token?: string; +}; +type DashboardListRealTimeLocationForUsersQueryParams = { + location_type: "company" | "personal" | "unknown"; + site_id?: string; + keyword?: string; + page_size?: number; + next_page_token?: string; +}; +type DashboardListRealTimeLocationForUsersResponse = { + next_page_token?: string; + page_size?: number; + total_records?: number; + records?: { + email?: string; + bssid?: string; + user_display_name?: string; + extension_id?: string; + extension_number?: number; + site_name?: string; + site_id?: string; + public_ip?: string; + private_ip?: string; + emergency_address?: string; + location_name?: string; + network_switch?: { + port?: string; + mac_address?: string; + }; + location_type?: "company" | "personal" | "unknown"; + }[]; }; type DashboardListTrackedLocationsQueryParams = { - type?: number; + type?: 1 | 2 | 3 | 4 | 5 | 6; site_id?: string; - location_type?: string; + location_type?: "company" | "personal" | "unknown"; keyword?: string; }; type DashboardListTrackedLocationsResponse = { @@ -2919,7 +3271,7 @@ type DashboardListTrackedLocationsResponse = { id?: string; name?: string; }; - type?: string; + type?: "company" | "personal" | "unknown"; zip?: string; }[]; next_page_token?: string; @@ -2931,11 +3283,11 @@ type DashboardListPastCallMetricsQueryParams = { to?: string; phone_number?: string; extension_number?: string; - quality_type?: string; + quality_type?: "good" | "bad"; department?: string; cost_center?: string; - directions?: string[]; - durations?: number[]; + directions?: ("inbound" | "outbound" | "internal")[]; + durations?: (0 | 1 | 2 | 3)[]; site_id?: string; page_size?: number; next_page_token?: string; @@ -2968,7 +3320,7 @@ type DashboardListPastCallMetricsResponse = { site_id?: string; }; date_time?: string; - direction?: string; + direction?: "inbound" | "outbound" | "internal"; duration?: number; mos?: string; }[]; @@ -2989,14 +3341,14 @@ type DeviceLineKeysGetDeviceLineKeysInformationResponse = { owner_extension_name?: string; owner_extension_number?: number; extension_number?: number; - extension_type?: string; + extension_type?: "User" | "CommonArea"; extension_id?: string; display_name?: string; phone_number?: string; outbound_caller_ids?: { extension_id?: string; phone_number?: string; - usage_type?: string; + usage_type?: "Main Company Number" | "Additional Company Number" | "Direct Number" | "Phone Number"; }[]; }[]; }; @@ -3077,8 +3429,8 @@ type DialByNameDirectoryDeleteUsersFromDirectoryOfSiteQueryParams = { type EmergencyAddressesListEmergencyAddressesQueryParams = { site_id?: string; user_id?: string; - level?: number; - status?: number; + level?: 0 | 1 | 2; + status?: 1 | 2 | 3 | 4 | 5 | 6; address_keyword?: string; next_page_token?: string; page_size?: number; @@ -3091,7 +3443,7 @@ type EmergencyAddressesListEmergencyAddressesResponse = { country?: string; id?: string; is_default?: boolean; - level?: number; + level?: 0 | 1 | 2; owner?: { extension_number?: number; id?: string; @@ -3102,7 +3454,7 @@ type EmergencyAddressesListEmergencyAddressesResponse = { name?: string; }; state_code?: string; - status?: number; + status?: 1 | 2 | 3 | 4 | 5 | 6; zip?: string; }[]; next_page_token?: string; @@ -3127,7 +3479,7 @@ type EmergencyAddressesAddEmergencyAddressResponse = { country?: string; id?: string; is_default?: boolean; - level?: number; + level?: 0 | 1 | 2; owner?: { extension_number?: string; id?: string; @@ -3138,7 +3490,7 @@ type EmergencyAddressesAddEmergencyAddressResponse = { name?: string; }; state_code?: string; - status?: number; + status?: 1 | 2 | 3 | 4 | 5 | 6; zip?: string; }; type EmergencyAddressesGetEmergencyAddressDetailsPathParams = { @@ -3151,7 +3503,7 @@ type EmergencyAddressesGetEmergencyAddressDetailsResponse = { country?: string; id?: string; is_default?: boolean; - level?: number; + level?: 0 | 1 | 2; owner?: { extension_number?: number; id?: string; @@ -3162,7 +3514,7 @@ type EmergencyAddressesGetEmergencyAddressDetailsResponse = { name?: string; }; state_code?: string; - status?: number; + status?: 1 | 2 | 3 | 4 | 5 | 6; zip?: string; }; type EmergencyAddressesDeleteEmergencyAddressPathParams = { @@ -3187,7 +3539,7 @@ type EmergencyAddressesUpdateEmergencyAddressResponse = { country?: string; id?: string; is_default?: boolean; - level?: number; + level?: 0 | 1 | 2; owner?: { extension_number?: number; id?: string; @@ -3198,7 +3550,7 @@ type EmergencyAddressesUpdateEmergencyAddressResponse = { name?: string; }; state_code?: string; - status?: number; + status?: 1 | 2 | 3 | 4 | 5 | 6; zip?: string; }; type EmergencyServiceLocationsBatchAddEmergencyServiceLocationsRequestBody = { @@ -3271,6 +3623,7 @@ type EmergencyServiceLocationsListEmergencyServiceLocationsResponse = { name?: string; }; emergency_address?: { + id?: string; address_line1?: string; address_line2?: string; city?: string; @@ -3283,6 +3636,7 @@ type EmergencyServiceLocationsListEmergencyServiceLocationsResponse = { }[]; next_page_token?: string; page_size?: number; + total_records?: number; }; type EmergencyServiceLocationsAddEmergencyServiceLocationRequestBody = { bssid?: string; @@ -3377,6 +3731,8 @@ type ExternalContactsListExternalContactsResponse = { name?: string; phone_numbers?: string[]; auto_call_recorded?: boolean; + profile_picture_download_url?: string; + enable_internal_extension?: boolean; }[]; next_page_token?: string; page_size?: number; @@ -3390,6 +3746,11 @@ type ExternalContactsAddExternalContactRequestBody = { phone_numbers?: string[]; routing_path?: string; auto_call_recorded?: boolean; + profile_picture?: { + type?: "JPG" | "JPEG" | "PNG" | "GIF"; + base64_encoding?: string; + }; + enable_internal_extension?: boolean; }; type ExternalContactsAddExternalContactResponse = { name?: string; @@ -3407,6 +3768,8 @@ type ExternalContactsGetExternalContactDetailsResponse = { name?: string; phone_numbers?: string[]; auto_call_recorded?: boolean; + profile_picture_download_url?: string; + enable_internal_extension?: boolean; }; type ExternalContactsDeleteExternalContactPathParams = { externalContactId: string; @@ -3423,6 +3786,11 @@ type ExternalContactsUpdateExternalContactRequestBody = { phone_numbers?: string[]; routing_path?: string; auto_call_recorded?: boolean; + profile_picture?: { + type?: "JPG" | "JPEG" | "PNG" | "GIF"; + base64_encoding?: string; + }; + enable_internal_extension?: boolean; }; type FirmwareUpdateRulesListFirmwareUpdateRulesQueryParams = { site_id?: string; @@ -3444,7 +3812,7 @@ type FirmwareUpdateRulesAddFirmwareUpdateRuleRequestBody = { version: string; device_type: string; device_model: string; - restart_type?: number; + restart_type?: 1 | 2; }; type FirmwareUpdateRulesAddFirmwareUpdateRuleResponse = { rule_Id?: string; @@ -3462,7 +3830,7 @@ type FirmwareUpdateRulesDeleteFirmwareUpdateRulePathParams = { ruleId: string; }; type FirmwareUpdateRulesDeleteFirmwareUpdateRuleQueryParams = { - restart_type?: number; + restart_type?: 1 | 2; }; type FirmwareUpdateRulesUpdateFirmwareUpdateRulePathParams = { ruleId: string; @@ -3471,7 +3839,7 @@ type FirmwareUpdateRulesUpdateFirmwareUpdateRuleRequestBody = { version: string; device_type: string; device_model: string; - restart_type?: number; + restart_type?: 1 | 2; }; type FirmwareUpdateRulesListUpdatableFirmwaresQueryParams = { is_update?: boolean; @@ -3485,7 +3853,7 @@ type FirmwareUpdateRulesListUpdatableFirmwaresResponse = { version?: string; update_log?: string; expire_time?: string; - status?: number; + status?: 1 | 2 | 3; }[]; }[]; }; @@ -3502,7 +3870,7 @@ type GroupCallPickupListGroupCallPickupObjectsResponse = { extension_number?: number; member_count?: number; description?: string; - delay?: number; + delay?: 0 | 5 | 10 | 15; cost_center?: string; department?: string; site?: { @@ -3520,11 +3888,11 @@ type GroupCallPickupAddGroupCallPickupObjectRequestBody = { site_id: string; description?: string; extension_number?: number; - delay?: number; + delay?: 0 | 5 | 10 | 15; play_incoming_calls_sound?: { enable?: boolean; - ring_tone?: string; - duration?: number; + ring_tone?: "ringtone_1" | "ringtone_2" | "ringtone_3"; + duration?: 0 | 1 | 3 | 5; }; directed_call_pickup?: boolean; member_extension_ids?: string[]; @@ -3542,7 +3910,7 @@ type GroupCallPickupGetCallPickupGroupByIDResponse = { extension_id?: string; extension_number?: number; description?: string; - delay?: number; + delay?: 0 | 5 | 10 | 15; member_count?: number; cost_center?: string; department?: string; @@ -3552,8 +3920,8 @@ type GroupCallPickupGetCallPickupGroupByIDResponse = { }; play_incoming_calls_sound?: { enable?: boolean; - ring_tone?: string; - duration?: number; + ring_tone?: "ringtone_1" | "ringtone_2" | "ringtone_3"; + duration?: 0 | 1 | 3 | 5; }; directed_call_pickup?: boolean; }; @@ -3567,13 +3935,13 @@ type GroupCallPickupUpdateGroupCallPickupInformationRequestBody = { display_name?: string; extension_number?: number; description?: string; - delay?: number; + delay?: 0 | 5 | 10 | 15; cost_center?: string; department?: string; play_incoming_calls_sound?: { enable?: boolean; - ring_tone?: string; - duration?: number; + ring_tone?: "ringtone_1" | "ringtone_2" | "ringtone_3"; + duration?: 0 | 1 | 3 | 5; }; directed_call_pickup?: boolean; }; @@ -3584,14 +3952,14 @@ type GroupCallPickupListCallPickupGroupMembersQueryParams = { page_size?: number; next_page_token?: string; site_id?: string; - extension_type?: string; + extension_type?: "user" | "commonArea"; }; type GroupCallPickupListCallPickupGroupMembersResponse = { group_call_pickup_member?: { id?: string; display_name?: string; extension_id?: string; - extension_type?: string; + extension_type?: "user" | "commonArea"; extension_number?: number; }[]; next_page_token?: string; @@ -3608,6 +3976,33 @@ type GroupCallPickupRemoveMembersFromCallPickupGroupPathParams = { groupId: string; extensionId: string; }; +type GroupsGetGroupPolicyDetailsPathParams = { + groupId: string; + policyType: "allow_emergency_calls"; +}; +type GroupsGetGroupPolicyDetailsResponse = { + allow_emergency_calls?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "user_group"; + modified?: boolean; + allow_emergency_calls_from_clients?: boolean; + allow_emergency_calls_from_deskphones?: boolean; + }; +}; +type GroupsUpdateGroupPolicyPathParams = { + groupId: string; + policyType: "allow_emergency_calls"; +}; +type GroupsUpdateGroupPolicyRequestBody = { + allow_emergency_calls?: { + enable?: boolean; + locked?: boolean; + reset?: boolean; + allow_emergency_calls_from_clients?: boolean; + allow_emergency_calls_from_deskphones?: boolean; + }; +}; type GroupsGetGroupPhoneSettingsPathParams = { groupId: string; }; @@ -3618,7 +4013,7 @@ type GroupsGetGroupPhoneSettingsResponse = { call_live_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; transcription_start_prompt?: { enable?: boolean; @@ -3629,20 +4024,20 @@ type GroupsGetGroupPhoneSettingsResponse = { local_survivability_mode?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; select_outbound_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_hide_outbound_caller_id?: boolean; }; personal_audio_library?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_music_on_hold_customization?: boolean; allow_voicemail_and_message_greeting_customization?: boolean; @@ -3650,7 +4045,7 @@ type GroupsGetGroupPhoneSettingsResponse = { voicemail?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_delete?: boolean; allow_download?: boolean; @@ -3661,7 +4056,7 @@ type GroupsGetGroupPhoneSettingsResponse = { voicemail_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; voicemail_notification_by_email?: { @@ -3670,19 +4065,19 @@ type GroupsGetGroupPhoneSettingsResponse = { forward_voicemail_to_email?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; shared_voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; restricted_call_hours?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; time_zone?: { id?: string; @@ -3695,7 +4090,7 @@ type GroupsGetGroupPhoneSettingsResponse = { allowed_call_locations?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; locations_applied?: boolean; allow_internal_calls?: boolean; @@ -3703,14 +4098,14 @@ type GroupsGetGroupPhoneSettingsResponse = { check_voicemails_over_phone?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; auto_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; - recording_calls?: string; + locked_by?: "invalid" | "account" | "user_group"; + recording_calls?: "inbound" | "outbound" | "both"; recording_transcription?: boolean; recording_start_prompt?: boolean; recording_start_prompt_audio_id?: string; @@ -3719,9 +4114,9 @@ type GroupsGetGroupPhoneSettingsResponse = { disconnect_on_recording_failure?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; inbound_audio_notification?: { recording_start_prompt?: boolean; @@ -3737,7 +4132,7 @@ type GroupsGetGroupPhoneSettingsResponse = { ad_hoc_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; recording_transcription?: boolean; allow_download?: boolean; @@ -3746,22 +4141,26 @@ type GroupsGetGroupPhoneSettingsResponse = { recording_explicit_consent?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; }; zoom_phone_on_mobile?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_calling_sms_mms?: boolean; + allow_calling_clients?: ("ios" | "android" | "intune" | "blackberry")[]; + allow_sms_mms_clients?: ("ios" | "android" | "intune" | "blackberry")[]; }; zoom_phone_on_pwa?: { + allow_calling?: boolean; + allow_sms_mms?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; sms_etiquette_tool?: { @@ -3771,151 +4170,155 @@ type GroupsGetGroupPhoneSettingsResponse = { id?: string; name?: string; description?: string; - rule?: number; + rule?: 1 | 2; content?: string; - action?: number; + action?: 1 | 2; active?: boolean; }[]; }; outbound_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; outbound_sms?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; + allow_copy?: boolean; + allow_paste?: boolean; }; international_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; sms?: { enable?: boolean; international_sms?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; + allow_copy?: boolean; + allow_paste?: boolean; }; e2e_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; call_handling_forwarding?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - call_forwarding_type?: number; + call_forwarding_type?: 1 | 2 | 3 | 4; }; call_overflow?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - call_overflow_type?: number; + call_overflow_type?: 1 | 2 | 3 | 4; }; call_transferring?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - call_transferring_type?: number; + call_transferring_type?: 1 | 2 | 3 | 4; }; elevate_to_meeting?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; call_park?: { enable?: boolean; - expiration_period?: number; + expiration_period?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; call_not_picked_up_action?: number; forward_to?: { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "zoomRoom" | "commonArea" | "ciscoRoom/polycomRoom" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; id?: string; }; - sequence?: number; + sequence?: 0 | 1; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; hand_off_to_room?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; mobile_switch_to_carrier?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; delegation?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; audio_intercom?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; block_list_for_inbound_calls_and_messaging?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; block_calls_without_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; block_external_calls?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; block_business_hours?: boolean; block_closed_hours?: boolean; block_holiday_hours?: boolean; - block_call_action?: number; + block_call_action?: 0 | 9; }; peer_to_peer_media?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; advanced_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; disable_incoming_unencrypted_voicemail?: boolean; }; display_call_feedback_survey?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - feedback_type?: number; + feedback_type?: 1 | 2; feedback_mos?: { enable?: boolean; min?: string; @@ -3927,6 +4330,22 @@ type GroupsGetGroupPhoneSettingsResponse = { max?: number; }; }; + zoom_phone_on_desktop?: { + allow_calling_clients?: ("mac_os" | "windows" | "vdi_client" | "linux")[]; + allow_sms_mms_clients?: ("mac_os" | "windows" | "vdi_client" | "linux")[]; + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + allow_emergency_calls?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "user_group"; + modified?: boolean; + allow_emergency_calls_from_clients?: boolean; + allow_emergency_calls_from_deskphones?: boolean; + }; }; type IVRGetAutoReceptionistIVRPathParams = { autoReceptionistId: string; @@ -3942,7 +4361,7 @@ type IVRGetAutoReceptionistIVRResponse = { }; caller_enters_no_action?: { action?: number; - audio_prompt_repeat?: number; + audio_prompt_repeat?: 1 | 2 | 3; forward_to?: { display_name?: string; extension_id?: string; @@ -3973,7 +4392,7 @@ type IVRUpdateAutoReceptionistIVRRequestBody = { audio_prompt_id?: string; caller_enters_no_action?: { action?: number; - audio_prompt_repeat?: number; + audio_prompt_repeat?: 1 | 2 | 3; forward_to_extension_id?: string; }; holiday_id?: string; @@ -3993,17 +4412,17 @@ type InboundBlockedListListExtensionsInboundBlockRulesPathParams = { }; type InboundBlockedListListExtensionsInboundBlockRulesQueryParams = { keyword?: string; - match_type?: string; - type?: string; + match_type?: "prefix" | "phoneNumber" | "SMS-shortCodes"; + type?: "block_for_other_reasons" | "block_as_threat"; next_page_token?: string; page_size?: number; }; type InboundBlockedListListExtensionsInboundBlockRulesResponse = { extension_blocked_rules?: { id?: string; - match_type?: string; + match_type?: "prefix" | "phoneNumber" | "SMS-shortCodes"; phone_number?: string; - type?: string; + type?: "block_for_other_reasons" | "block_as_threat"; blocked_number?: string; country?: string; }[]; @@ -4014,9 +4433,9 @@ type InboundBlockedListAddExtensionsInboundBlockRulePathParams = { extensionId: string; }; type InboundBlockedListAddExtensionsInboundBlockRuleRequestBody = { - match_type: string; + match_type: "prefix" | "phoneNumber" | "SMS-shortCodes"; blocked_number: string; - type: string; + type: "block_for_other_reasons" | "block_as_threat"; country?: string; }; type InboundBlockedListAddExtensionsInboundBlockRuleResponse = { @@ -4030,7 +4449,7 @@ type InboundBlockedListDeleteExtensionsInboundBlockRuleQueryParams = { }; type InboundBlockedListListAccountsInboundBlockedStatisticsQueryParams = { keyword?: string; - match_type?: string; + match_type?: "prefix" | "phoneNumber" | "SMS-shortCodes"; type?: string; next_page_token?: string; page_size?: number; @@ -4038,9 +4457,9 @@ type InboundBlockedListListAccountsInboundBlockedStatisticsQueryParams = { type InboundBlockedListListAccountsInboundBlockedStatisticsResponse = { blocked_statistic?: { id?: string; - match_type?: string; + match_type?: "prefix" | "phoneNumber" | "SMS-shortCodes"; phone_number?: string; - type?: string; + type?: "block_for_other_reasons" | "block_as_threat"; block_count?: number; threat_count?: number; blocked_number?: string; @@ -4057,19 +4476,19 @@ type InboundBlockedListMarkPhoneNumberAsBlockedForAllExtensionsRequestBody = { }; type InboundBlockedListListAccountsInboundBlockRulesQueryParams = { keyword?: string; - match_type?: string; - type?: string; - status?: string; + match_type?: "prefix" | "phoneNumber" | "SMS-shortCodes"; + type?: "block_for_other_reasons" | "block_as_threat"; + status?: "active" | "inactive"; next_page_token?: string; page_size?: number; }; type InboundBlockedListListAccountsInboundBlockRulesResponse = { account_blocked_rules?: { id?: string; - match_type?: string; + match_type?: "prefix" | "phoneNumber" | "SMS-shortCodes"; phone_number?: string; - type?: string; - status?: string; + type?: "block_for_other_reasons" | "block_as_threat"; + status?: "active" | "inactive"; comment?: string; blocked_number?: string; country?: string; @@ -4078,11 +4497,11 @@ type InboundBlockedListListAccountsInboundBlockRulesResponse = { page_size?: number; }; type InboundBlockedListAddAccountsInboundBlockRuleRequestBody = { - match_type: string; + match_type: "prefix" | "phoneNumber" | "SMS-shortCodes"; blocked_number: string; - type: string; + type: "block_for_other_reasons" | "block_as_threat"; comment?: string; - status: string; + status: "active" | "inactive"; country?: string; }; type InboundBlockedListAddAccountsInboundBlockRuleResponse = { @@ -4095,11 +4514,11 @@ type InboundBlockedListUpdateAccountsInboundBlockRulePathParams = { blockedRuleId: string; }; type InboundBlockedListUpdateAccountsInboundBlockRuleRequestBody = { - match_type: string; + match_type: "prefix" | "phoneNumber" | "SMS-shortCodes"; blocked_number: string; - type: string; + type: "block_for_other_reasons" | "block_as_threat"; comment?: string; - status?: string; + status?: "active" | "inactive"; country?: string; }; type LineKeysGetLineKeyPositionAndSettingsInformationPathParams = { @@ -4119,7 +4538,7 @@ type LineKeysGetLineKeyPositionAndSettingsInformationResponse = { }; line_key_id?: string; outbound_caller_id?: string; - type?: string; + type?: "line" | "blf" | "speed_dial" | "zoom_meeting" | "call_park" | "group_call_pickup"; }[]; }; type LineKeysBatchUpdateLineKeyPositionAndSettingsInformationPathParams = { @@ -4129,7 +4548,7 @@ type LineKeysBatchUpdateLineKeyPositionAndSettingsInformationRequestBody = { line_keys?: { line_key_id?: string; index?: number; - type?: string; + type?: "line" | "blf" | "speed_dial" | "zoom_meeting" | "call_park" | "group_call_pickup"; key_assignment?: { extension_id?: string; speed_dial_number?: string; @@ -4144,7 +4563,7 @@ type LineKeysDeleteLineKeySettingPathParams = { lineKeyId: string; }; type MonitoringGroupsGetListOfMonitoringGroupsOnAccountQueryParams = { - type?: number; + type?: 1 | 2 | 3 | 4; site_id?: string; page_size?: number; next_page_token?: string; @@ -4154,25 +4573,25 @@ type MonitoringGroupsGetListOfMonitoringGroupsOnAccountResponse = { id?: string; monitor_members_count?: number; monitored_members_count?: number; - monitoring_privileges?: string[]; + monitoring_privileges?: ("listen" | "whisper" | "barge" | "take_over")[]; name?: string; prompt?: boolean; site?: { id?: string; name?: string; }; - type?: number; + type?: 1 | 2 | 3 | 4; }[]; next_page_token?: string; page_size?: number; total_records?: number; }; type MonitoringGroupsCreateMonitoringGroupRequestBody = { - monitoring_privileges?: string[]; + monitoring_privileges?: ("listen" | "whisper" | "barge" | "take_over")[]; name?: string; prompt?: boolean; site_id?: string; - type?: number; + type?: 1 | 2 | 3 | 4; }; type MonitoringGroupsCreateMonitoringGroupResponse = { id?: string; @@ -4185,14 +4604,14 @@ type MonitoringGroupsGetMonitoringGroupByIDResponse = { id?: string; monitor_members_count?: number; monitored_members_count?: number; - monitoring_privileges?: string[]; + monitoring_privileges?: ("listen" | "whisper" | "barge" | "take_over")[]; name?: string; prompt?: boolean; site?: { id?: string; name?: string; }; - type?: number; + type?: 1 | 2 | 3 | 4; }; type MonitoringGroupsDeleteMonitoringGroupPathParams = { monitoringGroupId: string; @@ -4201,7 +4620,7 @@ type MonitoringGroupsUpdateMonitoringGroupPathParams = { monitoringGroupId: string; }; type MonitoringGroupsUpdateMonitoringGroupRequestBody = { - monitoring_privileges?: string[]; + monitoring_privileges?: ("listen" | "whisper" | "barge" | "take_over")[]; name?: string; prompt?: boolean; site_id?: string; @@ -4210,7 +4629,7 @@ type MonitoringGroupsGetMembersOfMonitoringGroupPathParams = { monitoringGroupId: string; }; type MonitoringGroupsGetMembersOfMonitoringGroupQueryParams = { - member_type: string; + member_type: "monitor" | "monitored"; page_size?: number; next_page_token?: string; }; @@ -4219,7 +4638,7 @@ type MonitoringGroupsGetMembersOfMonitoringGroupResponse = { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "call_queue" | "shared_line_group" | "common_area_phone"; }[]; next_page_token?: string; page_size?: number; @@ -4229,21 +4648,22 @@ type MonitoringGroupsAddMembersToMonitoringGroupPathParams = { monitoringGroupId: string; }; type MonitoringGroupsAddMembersToMonitoringGroupQueryParams = { - member_type: string; + member_type: "monitor" | "monitored"; }; type MonitoringGroupsAddMembersToMonitoringGroupRequestBody = string[]; +type MonitoringGroupsAddMembersToMonitoringGroupResponse = never; type MonitoringGroupsRemoveAllMonitorsOrMonitoredMembersFromMonitoringGroupPathParams = { monitoringGroupId: string; }; type MonitoringGroupsRemoveAllMonitorsOrMonitoredMembersFromMonitoringGroupQueryParams = { - member_type: string; + member_type: "monitor" | "monitored"; }; type MonitoringGroupsRemoveMemberFromMonitoringGroupPathParams = { monitoringGroupId: string; memberExtensionId: string; }; type MonitoringGroupsRemoveMemberFromMonitoringGroupQueryParams = { - member_type?: string; + member_type?: "monitor" | "monitored"; }; type OutboundCallingGetCommonAreaLevelOutboundCallingCountriesAndRegionsPathParams = { commonAreaId: string; @@ -4257,7 +4677,7 @@ type OutboundCallingGetCommonAreaLevelOutboundCallingCountriesAndRegionsResponse name?: string; code?: number; iso_code?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; enabled_carrier?: string[]; }[]; next_page_token?: string; @@ -4269,7 +4689,7 @@ type OutboundCallingUpdateCommonAreaLevelOutboundCallingCountriesOrRegionsPathPa type OutboundCallingUpdateCommonAreaLevelOutboundCallingCountriesOrRegionsRequestBody = { country_regions?: { iso_code?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; delete_existing_exception_rules?: boolean; }[]; }; @@ -4279,19 +4699,19 @@ type OutboundCallingListCommonAreaLevelOutboundCallingExceptionRulesPathParams = type OutboundCallingListCommonAreaLevelOutboundCallingExceptionRulesQueryParams = { country?: string; keyword?: string; - match_type?: string; - status?: string; + match_type?: "phoneNumber" | "prefix"; + status?: "active" | "inactive"; next_page_token?: string; page_size?: number; }; type OutboundCallingListCommonAreaLevelOutboundCallingExceptionRulesResponse = { exception_rules?: { id?: string; - match_type?: string; + match_type?: "phoneNumber" | "prefix"; prefix_number?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; comment?: string; - status?: string; + status?: "active" | "inactive"; }[]; next_page_token?: string; page_size?: number; @@ -4301,10 +4721,10 @@ type OutboundCallingAddCommonAreaLevelOutboundCallingExceptionRulePathParams = { }; type OutboundCallingAddCommonAreaLevelOutboundCallingExceptionRuleRequestBody = { exception_rule?: { - match_type: string; + match_type: "phoneNumber" | "prefix"; prefix_number: string; comment?: string; - status: string; + status: "active" | "inactive"; country: string; }; }; @@ -4321,10 +4741,10 @@ type OutboundCallingUpdateCommonAreaLevelOutboundCallingExceptionRulePathParams }; type OutboundCallingUpdateCommonAreaLevelOutboundCallingExceptionRuleRequestBody = { exception_rule?: { - match_type: string; + match_type: "phoneNumber" | "prefix"; prefix_number: string; comment?: string; - status: string; + status: "active" | "inactive"; country: string; }; }; @@ -4337,7 +4757,7 @@ type OutboundCallingGetAccountLevelOutboundCallingCountriesAndRegionsResponse = name?: string; code?: number; iso_code?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; enabled_carrier?: string[]; }[]; next_page_token?: string; @@ -4346,36 +4766,36 @@ type OutboundCallingGetAccountLevelOutboundCallingCountriesAndRegionsResponse = type OutboundCallingUpdateAccountLevelOutboundCallingCountriesOrRegionsRequestBody = { country_regions?: { iso_code?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; delete_existing_exception_rules?: boolean; }[]; }; type OutboundCallingListAccountLevelOutboundCallingExceptionRulesQueryParams = { country?: string; keyword?: string; - match_type?: string; - status?: string; + match_type?: "phoneNumber" | "prefix"; + status?: "active" | "inactive"; next_page_token?: string; page_size?: number; }; type OutboundCallingListAccountLevelOutboundCallingExceptionRulesResponse = { exception_rules?: { id?: string; - match_type?: string; + match_type?: "phoneNumber" | "prefix"; prefix_number?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; comment?: string; - status?: string; + status?: "active" | "inactive"; }[]; next_page_token?: string; page_size?: number; }; type OutboundCallingAddAccountLevelOutboundCallingExceptionRuleRequestBody = { exception_rule?: { - match_type: string; + match_type: "phoneNumber" | "prefix"; prefix_number: string; comment?: string; - status: string; + status: "active" | "inactive"; country: string; }; }; @@ -4390,10 +4810,10 @@ type OutboundCallingUpdateAccountLevelOutboundCallingExceptionRulePathParams = { }; type OutboundCallingUpdateAccountLevelOutboundCallingExceptionRuleRequestBody = { exception_rule?: { - match_type: string; + match_type: "phoneNumber" | "prefix"; prefix_number: string; comment?: string; - status: string; + status: "active" | "inactive"; country: string; }; }; @@ -4409,7 +4829,7 @@ type OutboundCallingGetSiteLevelOutboundCallingCountriesAndRegionsResponse = { name?: string; code?: number; iso_code?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; enabled_carrier?: string[]; }[]; next_page_token?: string; @@ -4421,7 +4841,7 @@ type OutboundCallingUpdateSiteLevelOutboundCallingCountriesOrRegionsPathParams = type OutboundCallingUpdateSiteLevelOutboundCallingCountriesOrRegionsRequestBody = { country_regions?: { iso_code?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; delete_existing_exception_rules?: boolean; }[]; }; @@ -4431,19 +4851,19 @@ type OutboundCallingListSiteLevelOutboundCallingExceptionRulesPathParams = { type OutboundCallingListSiteLevelOutboundCallingExceptionRulesQueryParams = { country?: string; keyword?: string; - match_type?: string; - status?: string; + match_type?: "phoneNumber" | "prefix"; + status?: "active" | "inactive"; next_page_token?: string; page_size?: number; }; type OutboundCallingListSiteLevelOutboundCallingExceptionRulesResponse = { exception_rules?: { id?: string; - match_type?: string; + match_type?: "phoneNumber" | "prefix"; prefix_number?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; comment?: string; - status?: string; + status?: "active" | "inactive"; }[]; next_page_token?: string; page_size?: number; @@ -4453,10 +4873,10 @@ type OutboundCallingAddSiteLevelOutboundCallingExceptionRulePathParams = { }; type OutboundCallingAddSiteLevelOutboundCallingExceptionRuleRequestBody = { exception_rule?: { - match_type: string; + match_type: "phoneNumber" | "prefix"; prefix_number: string; comment?: string; - status: string; + status: "active" | "inactive"; country: string; }; }; @@ -4473,10 +4893,10 @@ type OutboundCallingUpdateSiteLevelOutboundCallingExceptionRulePathParams = { }; type OutboundCallingUpdateSiteLevelOutboundCallingExceptionRuleRequestBody = { exception_rule?: { - match_type: string; + match_type: "phoneNumber" | "prefix"; prefix_number: string; comment?: string; - status: string; + status: "active" | "inactive"; country: string; }; }; @@ -4492,7 +4912,7 @@ type OutboundCallingGetUserLevelOutboundCallingCountriesAndRegionsResponse = { name?: string; code?: number; iso_code?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; enabled_carrier?: string[]; }[]; next_page_token?: string; @@ -4504,7 +4924,7 @@ type OutboundCallingUpdateUserLevelOutboundCallingCountriesOrRegionsPathParams = type OutboundCallingUpdateUserLevelOutboundCallingCountriesOrRegionsRequestBody = { country_regions?: { iso_code?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; delete_existing_exception_rules?: boolean; }[]; }; @@ -4514,19 +4934,19 @@ type OutboundCallingListUserLevelOutboundCallingExceptionRulesPathParams = { type OutboundCallingListUserLevelOutboundCallingExceptionRulesQueryParams = { country?: string; keyword?: string; - match_type?: string; - status?: string; + match_type?: "phoneNumber" | "prefix"; + status?: "active" | "inactive"; next_page_token?: string; page_size?: number; }; type OutboundCallingListUserLevelOutboundCallingExceptionRulesResponse = { exception_rules?: { id?: string; - match_type?: string; + match_type?: "phoneNumber" | "prefix"; prefix_number?: string; - rule?: number; + rule?: 1 | 2 | 3 | 4; comment?: string; - status?: string; + status?: "active" | "inactive"; }[]; next_page_token?: string; page_size?: number; @@ -4536,10 +4956,10 @@ type OutboundCallingAddUserLevelOutboundCallingExceptionRulePathParams = { }; type OutboundCallingAddUserLevelOutboundCallingExceptionRuleRequestBody = { exception_rule?: { - match_type: string; + match_type: "phoneNumber" | "prefix"; prefix_number: string; comment?: string; - status: string; + status: "active" | "inactive"; country: string; }; }; @@ -4556,20 +4976,20 @@ type OutboundCallingUpdateUserLevelOutboundCallingExceptionRulePathParams = { }; type OutboundCallingUpdateUserLevelOutboundCallingExceptionRuleRequestBody = { exception_rule?: { - match_type: string; + match_type: "phoneNumber" | "prefix"; prefix_number: string; comment?: string; - status: string; + status: "active" | "inactive"; country: string; }; }; type PhoneDevicesListDevicesQueryParams = { - type: string; - assignee_type?: string; - device_source?: string; - location_status?: string; + type: "assigned" | "unassigned"; + assignee_type?: "user" | "commonArea"; + device_source?: "haas" | "hotDesking"; + location_status?: "unknownAddress"; site_id?: string; - device_type?: string; + device_type?: "algo" | "audioCodes" | "cisco" | "cyberData" | "grandstream" | "poly" | "yealink" | "other"; keyword?: string; next_page_token?: string; page_size?: number; @@ -4580,13 +5000,13 @@ type PhoneDevicesListDevicesResponse = { extension_number?: number; id?: string; name?: string; - extension_type?: string; + extension_type?: "user" | "commonArea"; }; assignees?: { extension_number?: number; id?: string; name?: string; - extension_type?: string; + extension_type?: "user" | "commonArea"; extension_id?: string; }[]; device_type?: string; @@ -4597,7 +5017,7 @@ type PhoneDevicesListDevicesResponse = { id?: string; name?: string; }; - status?: string; + status?: "online" | "offline"; provision_template_id?: string; }[]; next_page_token?: string; @@ -4629,13 +5049,13 @@ type PhoneDevicesGetDeviceDetailsResponse = { extension_number?: number; id?: string; name?: string; - extension_type?: string; + extension_type?: "user" | "commonArea"; }; assignees?: { extension_number?: number; id?: string; name?: string; - extension_type?: string; + extension_type?: "user" | "commonArea"; extension_id?: string; }[]; device_type?: string; @@ -4660,23 +5080,23 @@ type PhoneDevicesGetDeviceDetailsResponse = { sip_domain?: string; user_name?: string; }[]; - type?: string; + type?: "assisted" | "ztp" | "manual"; url?: string; }; site?: { id?: string; name?: string; }; - status?: string; + status?: "online" | "offline"; provision_template_id?: string; private_ip?: string; public_ip?: string; policy?: { call_control?: { - status?: string; + status?: "unsupported" | "on" | "off"; }; hot_desking?: { - status?: string; + status?: "unsupported" | "on" | "off"; }; }; }; @@ -4689,7 +5109,6 @@ type PhoneDevicesUpdateDevicePathParams = { type PhoneDevicesUpdateDeviceRequestBody = { assigned_to?: string; display_name?: string; - mac_address?: string; provision_template_id?: string; }; type PhoneDevicesAssignEntityToDevicePathParams = { @@ -4698,6 +5117,7 @@ type PhoneDevicesAssignEntityToDevicePathParams = { type PhoneDevicesAssignEntityToDeviceRequestBody = { assignee_extension_ids: string[]; }; +type PhoneDevicesAssignEntityToDeviceResponse = never; type PhoneDevicesUnassignEntityFromDevicePathParams = { deviceId: string; extensionId: string; @@ -4711,6 +5131,35 @@ type PhoneDevicesUpdateProvisionTemplateOfDeviceRequestBody = { type PhoneDevicesRebootDeskPhonePathParams = { deviceId: string; }; +type PhoneDevicesListSmartphonesQueryParams = { + site_id?: string; + keyword?: string; + next_page_token?: string; + page_size?: number; +}; +type PhoneDevicesListSmartphonesResponse = { + smartphones: { + smartphone_id?: string; + device_name?: string; + device_type?: string; + serial_number?: string; + public_ip?: string; + activation_status?: "Activated"; + activation_time?: string; + assignee?: { + common_area_id?: string; + name?: string; + extension_number?: number; + }; + site?: { + site_id?: string; + name?: string; + }; + }[]; + next_page_token?: string; + page_size?: number; + total_records?: number; +}; type PhoneNumbersAddBYOCPhoneNumbersRequestBody = { carrier: string; phone_numbers: string[]; @@ -4725,10 +5174,10 @@ type PhoneNumbersAddBYOCPhoneNumbersResponse = { }; type PhoneNumbersListPhoneNumbersQueryParams = { next_page_token?: string; - type?: string; - extension_type?: string; + type?: "assigned" | "unassigned" | "byoc" | "all"; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "emergencyNumberPool" | "companyLocation" | "meetingService"; page_size?: number; - number_type?: string; + number_type?: "toll" | "tollfree"; pending_numbers?: boolean; site_id?: string; }; @@ -4740,7 +5189,7 @@ type PhoneNumbersListPhoneNumbersResponse = { extension_number?: number; id?: string; name?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "emergencyNumberPool" | "companyLocation" | "meetingService"; }; capability?: string[]; carrier?: { @@ -4756,12 +5205,12 @@ type PhoneNumbersListPhoneNumbersResponse = { state_code?: string; zip?: string; }; - emergency_address_status?: number; + emergency_address_status?: 1 | 2; emergency_address_update_time?: string; id?: string; location?: string; number?: string; - number_type?: string; + number_type?: "toll" | "tollfree"; sip_group?: { display_name?: string; id?: string; @@ -4770,8 +5219,8 @@ type PhoneNumbersListPhoneNumbersResponse = { id?: string; name?: string; }; - source?: string; - status?: string; + source?: "internal" | "external"; + status?: "pending" | "available"; }[]; total_records?: number; }; @@ -4804,7 +5253,7 @@ type PhoneNumbersGetPhoneNumberResponse = { id?: string; name?: string; }; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "emergencyNumberPool" | "companyLocation" | "meetingService"; }; capability?: string[]; carrier?: { @@ -4820,12 +5269,12 @@ type PhoneNumbersGetPhoneNumberResponse = { state_code?: string; zip?: string; }; - emergency_address_status?: number; + emergency_address_status?: 1 | 2; emergency_address_update_time?: string; id?: string; location?: string; number?: string; - number_type?: string; + number_type?: "toll" | "tollfree"; sip_group?: { display_name?: string; id?: string; @@ -4834,8 +5283,8 @@ type PhoneNumbersGetPhoneNumberResponse = { id?: string; name?: string; }; - source?: string; - status?: string; + source?: "internal" | "external"; + status?: "pending" | "available"; }; type PhoneNumbersUpdatePhoneNumberPathParams = { phoneNumberId: string; @@ -4874,6 +5323,8 @@ type PhonePlansListCallingPlansResponse = { type?: number; billing_account_id?: string; billing_account_name?: string; + billing_subscription_id?: string; + billing_subscription_name?: string; }[]; }; type PhonePlansListPlanInformationResponse = { @@ -4883,6 +5334,8 @@ type PhonePlansListPlanInformationResponse = { name?: string; subscribed?: number; type?: number; + billing_subscription_id?: string; + billing_subscription_name?: string; }[]; phone_numbers?: { assigned?: number; @@ -4962,6 +5415,62 @@ type PhoneRolesDeleteMembersInRolePathParams = { type PhoneRolesDeleteMembersInRoleQueryParams = { user_ids: string[]; }; +type PhoneRolesListPhoneRoleTargetsPathParams = { + roleId: string; +}; +type PhoneRolesListPhoneRoleTargetsQueryParams = { + is_default?: boolean; + user_id?: string; + selected?: boolean; + target_type?: "site" | "callQueue" | "autoReceptionist" | "user" | "group" | "sharedLineGroup" | "commonArea"; + site_id?: string; + keyword?: string; + page_size?: string; + next_page_token?: string; +}; +type PhoneRolesListPhoneRoleTargetsResponse = { + next_page_token?: string; + page_size?: number; + total_records?: number; + targets?: { + target_id?: string; + target_type?: "site" | "callQueue" | "autoReceptionist" | "user" | "group" | "sharedLineGroup" | "commonArea"; + target_name?: string; + extension_number?: number; + site_id?: string; + site_name?: string; + }[]; +}; +type PhoneRolesAddPhoneRoleTargetsPathParams = { + roleId: string; +}; +type PhoneRolesAddPhoneRoleTargetsRequestBody = { + is_default?: boolean; + user_id?: string; + targets: { + target_type?: "site" | "callQueue" | "autoReceptionist" | "user" | "group" | "sharedLineGroup" | "commonArea"; + target_ids: string[]; + }[]; +}; +type PhoneRolesAddPhoneRoleTargetsResponse = { + is_default?: boolean; + user_id?: string; + targets?: { + target_type?: "site" | "callQueue" | "autoReceptionist" | "user" | "group" | "sharedLineGroup" | "commonArea"; + target_ids?: string[]; + }[]; +}; +type PhoneRolesDeletePhoneRoleTargetsPathParams = { + roleId: string; +}; +type PhoneRolesDeletePhoneRoleTargetsRequestBody = { + is_default?: boolean; + user_id?: string; + targets: { + target_type?: "site" | "callQueue" | "autoReceptionist" | "user" | "group" | "sharedLineGroup" | "commonArea"; + target_ids: string[]; + }[]; +}; type PrivateDirectoryListPrivateDirectoryMembersQueryParams = { next_page_token?: string; page_size?: number; @@ -4974,11 +5483,11 @@ type PrivateDirectoryListPrivateDirectoryMembersResponse = { total_records?: number; private_directory_members?: { extension_id: string; - extension_type: string; + extension_type: "user" | "zoom_room" | "common_area" | "auto_receptionist" | "call_queue" | "shared_line_group"; extension_number: number; extension_display_name: string; extension_email?: string; - searchable_on_web_portal: string; + searchable_on_web_portal: "everybody" | "admins_only" | "nobody"; site_id?: string; site_name?: string; }[]; @@ -4987,7 +5496,7 @@ type PrivateDirectoryAddMembersToPrivateDirectoryRequestBody = { site_id?: string; members: { extension_id: string; - searchable_on_web_portal: string; + searchable_on_web_portal: "everybody" | "admins_only" | "nobody"; }[]; }; type PrivateDirectoryRemoveMemberFromPrivateDirectoryPathParams = { @@ -5001,7 +5510,7 @@ type PrivateDirectoryUpdatePrivateDirectoryMemberPathParams = { }; type PrivateDirectoryUpdatePrivateDirectoryMemberRequestBody = { site_id?: string; - searchable_on_web_portal: string; + searchable_on_web_portal: "everybody" | "admins_only" | "nobody"; }; type ProviderExchangeListCarrierPeeringPhoneNumbersQueryParams = { page_size?: number; @@ -5133,12 +5642,13 @@ type RecordingsGetRecordingByCallIDPathParams = { type RecordingsGetRecordingByCallIDResponse = { call_id?: string; call_log_id?: string; + call_history_id?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; + callee_number_type?: 1 | 2 | 3; caller_name?: string; caller_number?: string; - caller_number_type?: number; + caller_number_type?: 1 | 2; outgoing_by?: { name?: string; extension_number?: string; @@ -5158,16 +5668,16 @@ type RecordingsGetRecordingByCallIDResponse = { extension_number?: number; id?: string; name?: string; - type?: string; - extension_status?: string; + type?: "user" | "callQueue" | "commonArea"; + extension_status?: "inactive" | "deleted"; extension_deleted_time?: string; }; deleted_time?: string; days_left_auto_permantely_delete?: number; - soft_deleted_type?: string; - recording_type?: string; + soft_deleted_type?: "Manual" | "Data Retention"; + recording_type?: "OnDemand" | "Automatic"; file_url?: string; - disclaimer_status?: number; + disclaimer_status?: 0 | 1 | 2; }; type RecordingsDownloadPhoneRecordingPathParams = { fileId: string; @@ -5195,10 +5705,10 @@ type RecordingsGetCallRecordingsResponse = { call_log_id?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; + callee_number_type?: 1 | 2 | 3; caller_name?: string; caller_number?: string; - caller_number_type?: number; + caller_number_type?: 1 | 2; outgoing_by?: { name?: string; extension_number?: string; @@ -5208,8 +5718,8 @@ type RecordingsGetCallRecordingsResponse = { extension_number?: string; }; date_time?: string; - disclaimer_status?: number; - direction?: string; + disclaimer_status?: 0 | 1 | 2; + direction?: "inbound" | "outbound"; download_url?: string; duration?: number; end_time?: string; @@ -5219,8 +5729,8 @@ type RecordingsGetCallRecordingsResponse = { extension_number?: number; id?: string; name?: string; - type?: string; - extension_status?: string; + type?: "user" | "callQueue" | "commonArea"; + extension_status?: "inactive" | "deleted"; extension_deleted_time?: string; }; recording_type?: string; @@ -5246,7 +5756,7 @@ type RecordingsUpdateRecordingStatusPathParams = { recordingId: string; }; type RecordingsUpdateRecordingStatusRequestBody = { - action?: string; + action?: "recover"; }; type RecordingsGetUsersRecordingsPathParams = { userId: string; @@ -5268,10 +5778,10 @@ type RecordingsGetUsersRecordingsResponse = { call_history_id?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; + callee_number_type?: 1 | 2 | 3; caller_name?: string; caller_number?: string; - caller_number_type?: number; + caller_number_type?: 1 | 2; outgoing_by?: { name?: string; extension_number?: string; @@ -5311,15 +5821,15 @@ type ReportsGetCallChargesUsageReportResponse = { caller_billing_number?: string; callee_number?: string; callee_billing_number?: string; - call_type?: string; - service_type?: string; + call_type?: "voip" | "local" | "tollfree" | "international" | "callCenter"; + service_type?: "meeting" | "call"; calling_party_name?: string; cost_center?: string; employee_id?: string; department?: string; end_time?: string; duration?: number; - charge_mode?: string; + charge_mode?: "per_min" | "per_call" | "per_call_per_min" | "per_min_after_t_duration" | "per_call_per_min_after_t_duration"; rate?: string; currency?: string; total_charge?: string; @@ -5394,7 +5904,7 @@ type RoutingRulesListDirectoryBackupRoutingRulesResponse = { id?: string; name?: string; }; - type?: string; + type?: "other_sites" | "pstn" | "sip_group"; }; routing_rule_id?: string; site_id?: string; @@ -5406,7 +5916,7 @@ type RoutingRulesAddDirectoryBackupRoutingRuleRequestBody = { sip_group_id?: string; site_id?: string; translation?: string; - type?: string; + type?: "other_sites" | "pstn" | "sip_group"; }; type RoutingRulesAddDirectoryBackupRoutingRuleResponse = { name?: string; @@ -5424,7 +5934,7 @@ type RoutingRulesGetDirectoryBackupRoutingRuleResponse = { id?: string; name?: string; }; - type?: string; + type?: "other_sites" | "pstn" | "sip_group"; }; routing_rule_id?: string; site_id?: string; @@ -5442,7 +5952,28 @@ type RoutingRulesUpdateDirectoryBackupRoutingRuleRequestBody = { order?: number; sip_group_id?: string; translation?: string; - type?: string; + type?: "other_sites" | "pstn" | "sip_group"; +}; +type SMSPostSMSMessageRequestBody = { + attachments?: { + base64_encoding?: string; + type?: string; + }[]; + message?: string; + sender?: { + id?: string; + user_id?: string; + phone_number: string; + }; + session_id?: string; + to_members: { + phone_number?: string; + }[]; +}; +type SMSPostSMSMessageResponse = { + date_time?: string; + message_id?: string; + session_id?: string; }; type SMSGetAccountsSMSSessionsQueryParams = { page_size?: number; @@ -5451,7 +5982,7 @@ type SMSGetAccountsSMSSessionsQueryParams = { to?: string; session_type?: string; phone_number?: string; - filter_type?: string; + filter_type?: "sent_message_time" | "received_message_time" | "last_message_time" | "sent_received_message_time"; }; type SMSGetAccountsSMSSessionsResponse = { next_page_token?: string; @@ -5462,11 +5993,11 @@ type SMSGetAccountsSMSSessionsResponse = { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number?: string; is_session_owner?: boolean; - extension_status?: string; + extension_status?: "inactive" | "deleted"; extension_deleted_time?: string; }[]; session_id?: string; @@ -5492,18 +6023,18 @@ type SMSGetSMSSessionDetailsResponse = { id?: string; name?: string; size?: number; - type?: string; + type?: "OTHER" | "PNG" | "GIF" | "JPG" | "AUDIO" | "VIDEO"; }[]; date_time?: string; direction?: string; message?: string; message_id?: string; - message_type?: number; + message_type?: 1 | 2 | 3 | 4 | 5 | 6; sender?: { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number: string; }; @@ -5511,7 +6042,7 @@ type SMSGetSMSSessionDetailsResponse = { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number: string; }[]; @@ -5527,18 +6058,18 @@ type SMSGetSMSByMessageIDResponse = { id?: string; name?: string; size?: number; - type?: string; + type?: "OTHER" | "PNG" | "GIF" | "JPG" | "AUDIO" | "VIDEO"; }[]; date_time?: string; direction?: string; message?: string; message_id?: string; - message_type?: number; + message_type?: 1 | 2 | 3 | 4 | 5 | 6; sender?: { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number: string; }; @@ -5546,7 +6077,7 @@ type SMSGetSMSByMessageIDResponse = { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number: string; }[]; @@ -5555,7 +6086,7 @@ type SMSSyncSMSBySessionIDPathParams = { sessionId: string; }; type SMSSyncSMSBySessionIDQueryParams = { - sync_type?: string; + sync_type?: "FSync" | "ISync" | "BSync"; count?: number; sync_token?: string; }; @@ -5566,18 +6097,18 @@ type SMSSyncSMSBySessionIDResponse = { id?: string; name?: string; size?: number; - type?: string; + type?: "OTHER" | "PNG" | "GIF" | "JPG" | "AUDIO" | "VIDEO"; }[]; date_time?: string; direction?: string; message?: string; message_id?: string; - message_type?: number; + message_type?: 1 | 2 | 3 | 4 | 5 | 6; sender?: { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number: string; }; @@ -5585,7 +6116,7 @@ type SMSSyncSMSBySessionIDResponse = { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number: string; }[]; @@ -5602,7 +6133,7 @@ type SMSGetUsersSMSSessionsQueryParams = { from?: string; to?: string; phone_number?: string; - filter_type?: string; + filter_type?: "sent_message_time" | "received_message_time" | "last_message_time" | "sent_received_message_time"; }; type SMSGetUsersSMSSessionsResponse = { next_page_token?: string; @@ -5613,7 +6144,7 @@ type SMSGetUsersSMSSessionsResponse = { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number?: string; is_session_owner?: boolean; @@ -5626,7 +6157,7 @@ type SMSListUsersSMSSessionsInDescendingOrderPathParams = { userId: string; }; type SMSListUsersSMSSessionsInDescendingOrderQueryParams = { - sync_type: string; + sync_type: "FSync" | "BSync" | "ISync"; sync_token?: string; count?: number; session_type?: string; @@ -5637,18 +6168,18 @@ type SMSListUsersSMSSessionsInDescendingOrderResponse = { latest_message?: { attachments?: { id?: string; - type?: string; + type?: "OTHER" | "PNG" | "GIF" | "JPG/JPEG" | "AUDIO" | "VIDEO"; }[]; date_time?: string; direction?: string; message?: string; message_id?: string; - message_type?: number; + message_type?: 1 | 2 | 3 | 4 | 5 | 6; sender?: { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number: string; }; @@ -5656,7 +6187,7 @@ type SMSListUsersSMSSessionsInDescendingOrderResponse = { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number: string; }[]; @@ -5665,7 +6196,7 @@ type SMSListUsersSMSSessionsInDescendingOrderResponse = { display_name?: string; owner?: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; }; phone_number?: string; is_session_owner?: boolean; @@ -5686,7 +6217,7 @@ type SMSCampaignListSMSCampaignsResponse = { sms_campaigns?: { id?: string; display_name?: string; - status?: string; + status?: "draft" | "active" | "expired" | "pending" | "declined" | "--"; brand?: { id?: string; name?: string; @@ -5700,8 +6231,8 @@ type SMSCampaignGetSMSCampaignPathParams = { type SMSCampaignGetSMSCampaignResponse = { id?: string; display_name?: string; - status?: string; - service_type?: string; + status?: "draft" | "active" | "expired" | "pending" | "declined" | "--"; + service_type?: "zoomPhone" | "contactCenter"; brand?: { id?: string; name?: string; @@ -5712,9 +6243,9 @@ type SMSCampaignGetSMSCampaignResponse = { }[]; auto_renew?: boolean; create_time?: string; - use_case?: string; + use_case?: "lowVolumeMixed"; categories_fit?: boolean; - content_type?: string[]; + content_type?: ("urlLink" | "phoneNumber" | "ageGated" | "lending")[]; sample_message_1?: string; sample_message_2?: string; sample_message_3?: string; @@ -5725,10 +6256,14 @@ type SMSCampaignAssignPhoneNumberToSMSCampaignPathParams = { smsCampaignId: string; }; type SMSCampaignAssignPhoneNumberToSMSCampaignRequestBody = { - phone_numbers?: { + phone_numbers: { id?: string; number?: string; }[]; + loa_authorizing_person: string; + contact_number: string; + title: string; + contact_emails?: string; }; type SMSCampaignAssignPhoneNumberToSMSCampaignResponse = { phone_numbers?: { @@ -5747,7 +6282,7 @@ type SMSCampaignListOptStatusesOfPhoneNumbersAssignedToSMSCampaignResponse = { phone_number_campaign_opt_statuses: { consumer_phone_number: string; zoom_phone_user_number: string; - opt_status: string; + opt_status: "pending" | "opt_out" | "opt_in"; }[]; }; type SMSCampaignUpdateOptStatusesOfPhoneNumbersAssignedToSMSCampaignPathParams = { @@ -5756,12 +6291,26 @@ type SMSCampaignUpdateOptStatusesOfPhoneNumbersAssignedToSMSCampaignPathParams = type SMSCampaignUpdateOptStatusesOfPhoneNumbersAssignedToSMSCampaignRequestBody = { consumer_phone_number: string; zoom_phone_user_numbers: string[]; - opt_status: string; + opt_status: "opt_in" | "opt_out"; }; type SMSCampaignUnassignPhoneNumberPathParams = { smsCampaignId: string; phoneNumberId: string; }; +type SMSCampaignListUsersOptStatusesOfPhoneNumbersPathParams = { + userId: string; +}; +type SMSCampaignListUsersOptStatusesOfPhoneNumbersQueryParams = { + consumer_phone_numbers: string[]; + zoom_phone_user_numbers: string[]; +}; +type SMSCampaignListUsersOptStatusesOfPhoneNumbersResponse = { + phone_number_campaign_opt_statuses: { + consumer_phone_number: string; + zoom_phone_user_number: string; + opt_status: "pending" | "opt_out" | "opt_in"; + }[]; +}; type SettingTemplatesListSettingTemplatesQueryParams = { page_size?: number; next_page_token?: string; @@ -5774,7 +6323,7 @@ type SettingTemplatesListSettingTemplatesResponse = { description?: string; id?: string; name?: string; - type?: string; + type?: "user" | "group" | "autReceptionist" | "commonArea" | "zr" | "interop"; }[]; total_records?: number; }; @@ -5828,28 +6377,28 @@ type SettingTemplatesGetSettingTemplateDetailsResponse = { }; call_forwarding?: { enable?: boolean; - call_forwarding_type?: number; + call_forwarding_type?: 1 | 2 | 3 | 4; }; call_overflow?: { enable?: boolean; - call_overflow_type?: number; + call_overflow_type?: 1 | 2 | 3 | 4; }; }; profile?: { area_code?: string; country?: string; }; - type?: string; + type?: "user" | "group" | "autoReceptionist" | "commonArea" | "zr" | "interop"; user_settings?: { audio_prompt_language?: string; block_calls_without_caller_id?: boolean; call_handling?: { business_hours?: { - business_hour_action?: number; + business_hour_action?: 0 | 1 | 9 | 11 | 26 | 50; connect_to_operator?: { enable?: boolean; id?: string; - type?: string; + type?: "user" | "zoomRoom" | "commonArea" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; external_number?: { number?: string; description?: string; @@ -5858,11 +6407,11 @@ type SettingTemplatesGetSettingTemplateDetailsResponse = { require_press_1_before_connecting?: boolean; allow_caller_check_voicemail?: boolean; }; - busy_action?: number; + busy_action?: 0 | 1 | 11 | 12 | 13 | 26 | 50; busy_connect_operator?: { enable?: boolean; id?: string; - type?: string; + type?: "user" | "zoomRoom" | "commonAreaPhone" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; external_number?: { number?: string; description?: string; @@ -5874,19 +6423,19 @@ type SettingTemplatesGetSettingTemplateDetailsResponse = { custom_hours?: { from?: string; to?: string; - type?: number; - weekday?: number; + type?: 1 | 2; + weekday?: 1 | 2 | 3 | 4 | 5 | 6 | 7; }[]; ring_type?: string; - ringing_duration?: string; - type?: number; + ringing_duration?: "10" | "15" | "20" | "25" | "30" | "35" | "40" | "45" | "50" | "55" | "60"; + type?: 1 | 2; }; close_hours?: { - close_hour_action?: number; + close_hour_action?: 0 | 1 | 9 | 11 | 26 | 50; connect_to_operator?: { enable?: boolean; id?: string; - type?: string; + type?: "user" | "zoomRoom" | "commonAreaPhone" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; external_number?: { number?: string; description?: string; @@ -5895,11 +6444,11 @@ type SettingTemplatesGetSettingTemplateDetailsResponse = { require_press_1_before_connecting?: boolean; allow_caller_check_voicemail?: boolean; }; - busy_action?: number; + busy_action?: 0 | 1 | 11 | 12 | 13 | 26 | 50; busy_connect_operator?: { enable?: boolean; id?: string; - type?: string; + type?: "user" | "zoomRoom" | "commonArea" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; external_number?: { number?: string; description?: string; @@ -5908,13 +6457,13 @@ type SettingTemplatesGetSettingTemplateDetailsResponse = { require_press_1_before_connecting?: boolean; allow_caller_check_voicemail?: boolean; }; - max_wait_time?: string; + max_wait_time?: "10" | "15" | "20" | "25" | "30" | "35" | "40" | "45" | "50" | "55" | "60"; }; }; desk_phone?: { pin_code?: string; }; - hold_music?: string; + hold_music?: "default" | "disable"; }; }; type SettingTemplatesUpdateSettingTemplatePathParams = { @@ -5931,7 +6480,7 @@ type SettingTemplatesUpdateSettingTemplateRequestBody = { }; auto_call_recording?: { enable?: boolean; - recording_calls?: string; + recording_calls?: "inbound" | "outbound" | "both"; recording_start_prompt?: boolean; recording_transcription?: boolean; inbound_audio_notification?: { @@ -5951,11 +6500,11 @@ type SettingTemplatesUpdateSettingTemplateRequestBody = { }; call_forwarding?: { enable?: boolean; - call_forwarding_type?: number; + call_forwarding_type?: 1 | 2 | 3 | 4; }; call_overflow?: { enable?: boolean; - call_overflow_type?: number; + call_overflow_type?: 1 | 2 | 3 | 4; }; }; profile?: { @@ -5967,7 +6516,7 @@ type SettingTemplatesUpdateSettingTemplateRequestBody = { block_calls_without_caller_id?: boolean; call_handling?: { business_hours?: { - business_hour_action?: number; + business_hour_action?: 0 | 1 | 9 | 11 | 26 | 50; connect_to_operator?: { enable?: boolean; id?: string; @@ -5979,7 +6528,7 @@ type SettingTemplatesUpdateSettingTemplateRequestBody = { require_press_1_before_connecting?: boolean; allow_caller_check_voicemail?: boolean; }; - busy_action?: number; + busy_action?: 0 | 1 | 11 | 12 | 13 | 26 | 50; busy_connect_operator?: { enable?: boolean; id?: string; @@ -5994,15 +6543,15 @@ type SettingTemplatesUpdateSettingTemplateRequestBody = { custom_hours?: { from?: string; to?: string; - type?: number; - weekday?: number; + type?: 1 | 2; + weekday?: 1 | 2 | 3 | 4 | 5 | 6 | 7; }[]; ring_type?: string; - ringing_duration?: string; - type?: number; + ringing_duration?: "10" | "15" | "20" | "25" | "30" | "35" | "40" | "45" | "50" | "55" | "60"; + type?: 1 | 2; }; close_hours?: { - close_hour_action?: number; + close_hour_action?: 0 | 1 | 9 | 11 | 26 | 50; connect_to_operator?: { enable?: boolean; id?: string; @@ -6014,7 +6563,7 @@ type SettingTemplatesUpdateSettingTemplateRequestBody = { require_press_1_before_connecting?: boolean; allow_caller_check_voicemail?: boolean; }; - busy_action?: number; + busy_action?: 0 | 1 | 11 | 12 | 13 | 26 | 50; busy_connect_operator?: { enable?: boolean; id?: string; @@ -6026,13 +6575,36 @@ type SettingTemplatesUpdateSettingTemplateRequestBody = { require_press_1_before_connecting?: boolean; allow_caller_check_voicemail?: boolean; }; - max_wait_time?: string; + max_wait_time?: "10" | "15" | "20" | "25" | "30" | "35" | "40" | "45" | "50" | "55" | "60"; }; }; desk_phone?: { pin_code?: string; }; - hold_music?: string; + hold_music?: "default" | "disable"; + }; +}; +type SettingsGetAccountPolicyDetailsPathParams = { + policyType: "allow_emergency_calls"; +}; +type SettingsGetAccountPolicyDetailsResponse = { + allow_emergency_calls?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account"; + allow_emergency_calls_from_clients?: boolean; + allow_emergency_calls_from_deskphones?: boolean; + }; +}; +type SettingsUpdateAccountPolicyPathParams = { + policyType: "allow_emergency_calls"; +}; +type SettingsUpdateAccountPolicyRequestBody = { + allow_emergency_calls?: { + enable?: boolean; + locked?: boolean; + allow_emergency_calls_from_clients?: boolean; + allow_emergency_calls_from_deskphones?: boolean; }; }; type SettingsListPortedNumbersQueryParams = { @@ -6049,7 +6621,7 @@ type SettingsListPortedNumbersResponse = { source_number?: string; target_number?: string; }[]; - status?: string; + status?: "Not_Submitted" | "Waiting" | "Processing" | "Successfully" | "Rejected" | "Canceled" | "FOC"; submission_date_time?: string; }[]; total_records?: number; @@ -6084,7 +6656,7 @@ type SettingsGetPortedNumberDetailsResponse = { source_number?: string; target_number?: string; }[]; - status?: string; + status?: "Not_Submitted" | "Waiting" | "Processing" | "Successfully" | "Rejected" | "Canceled" | "FOC"; submission_date_time?: string; }; type SettingsGetPhoneAccountSettingsResponse = { @@ -6173,15 +6745,15 @@ type SharedLineAppearanceListSharedLineAppearancesResponse = { executive?: { name?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "commonArea"; }; assistants?: { id?: string; name?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "commonArea"; }[]; - privileges?: string[]; + privileges?: ("place_calls" | "answer_calls" | "pickup_hold_calls")[]; }[]; total_records?: number; }; @@ -6200,13 +6772,13 @@ type SharedLineGroupListSharedLineGroupsResponse = { phone_numbers?: { id?: string; number?: string; - status?: string; + status?: "pending" | "available"; }[]; site?: { id?: string; name?: string; }; - status?: string; + status?: "active" | "inactive"; }[]; total_records?: number; }; @@ -6249,7 +6821,7 @@ type SharedLineGroupGetSharedLineGroupResponse = { id?: string; name?: string; }; - status?: string; + status?: "active" | "inactive"; timezone?: string; policy?: { voicemail_access_members?: (({ @@ -6260,13 +6832,13 @@ type SharedLineGroupGetSharedLineGroupResponse = { } & { shared_id?: string; }) & { - access_user_type?: string; + access_user_type?: "user" | "commonArea"; })[]; }; cost_center?: string; department?: string; - audio_prompt_language?: string; - recording_storage_location?: string; + audio_prompt_language?: "en-US" | "en-GB" | "es-US" | "fr-CA" | "da-DK" | "de-DE" | "es-ES" | "fr-FR" | "it-IT" | "nl-NL" | "pt-PT" | "ja" | "ko-KR" | "pt-BR" | "zh-CN"; + recording_storage_location?: "US" | "AU" | "CA" | "DE" | "IN" | "JP" | "SG" | "BR" | "CN" | "MX"; own_storage_name?: string; allow_privacy?: boolean; }; @@ -6277,7 +6849,7 @@ type SharedLineGroupGetSharedLineGroupPolicyResponse = { check_voicemails_over_phone?: { enable: boolean; locked: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; }; @@ -6300,12 +6872,12 @@ type SharedLineGroupUpdateSharedLineGroupRequestBody = { display_name?: string; extension_number?: number; primary_number?: string; - status?: string; + status?: "active" | "inactive"; timezone?: string; cost_center?: string; department?: string; - audio_prompt_language?: string; - recording_storage_location?: string; + audio_prompt_language?: "en-US" | "en-GB" | "es-US" | "fr-CA" | "da-DK" | "de-DE" | "es-ES" | "fr-FR" | "it-IT" | "nl-NL" | "pt-PT" | "ja" | "ko-KR" | "pt-BR" | "zh-CN"; + recording_storage_location?: "US" | "AU" | "CA" | "DE" | "IN" | "JP" | "SG" | "BR" | "CN" | "MX"; allow_privacy?: boolean; }; type SharedLineGroupAddMembersToSharedLineGroupPathParams = { @@ -6364,7 +6936,7 @@ type SharedLineGroupAddPolicySettingToSharedLineGroupResponse = { } & { shared_id?: string; }) & { - access_user_type?: string; + access_user_type?: "user" | "commonArea"; })[]; }; type SharedLineGroupDeleteSLGPolicySettingPathParams = { @@ -6435,6 +7007,10 @@ type SitesCreatePhoneSiteRequestBody = { enable?: boolean; allow_extension_only_users_call_users_outside_site?: boolean; }; + india_state_code?: string; + india_city?: string; + india_sdca_npa?: string; + india_entity_name?: string; }; type SitesCreatePhoneSiteResponse = { id?: string; @@ -6464,14 +7040,14 @@ type SitesGetPhoneSiteDetailsResponse = { select_outbound_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; allow_hide_outbound_caller_id?: boolean; }; personal_audio_library?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; allow_music_on_hold_customization?: boolean; allow_voicemail_and_message_greeting_customization?: boolean; @@ -6482,13 +7058,13 @@ type SitesGetPhoneSiteDetailsResponse = { allow_videomail?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; voicemail_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; voicemail_notification_by_email?: { @@ -6497,26 +7073,28 @@ type SitesGetPhoneSiteDetailsResponse = { forward_voicemail_to_email?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; shared_voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; international_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; zoom_phone_on_mobile?: { + allow_calling_clients?: ("ios" | "android" | "intune" | "blackberry")[]; + allow_sms_mms_clients?: ("ios" | "android" | "intune" | "blackberry")[]; allow_calling_sms_mms?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; sms?: { @@ -6524,31 +7102,33 @@ type SitesGetPhoneSiteDetailsResponse = { international_sms?: boolean; international_sms_countries?: string[]; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; + allow_copy?: boolean; + allow_paste?: boolean; }; elevate_to_meeting?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; hand_off_to_room?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; mobile_switch_to_carrier?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; delegation?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; ad_hoc_call_recording?: { @@ -6557,12 +7137,12 @@ type SitesGetPhoneSiteDetailsResponse = { recording_transcription?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; auto_call_recording?: { @@ -6570,17 +7150,17 @@ type SitesGetPhoneSiteDetailsResponse = { disconnect_on_recording_failure?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; - recording_calls?: string; + recording_calls?: "inbound" | "outbound" | "both"; recording_explicit_consent?: boolean; recording_start_prompt?: boolean; recording_transcription?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; inbound_audio_notification?: { recording_start_prompt?: boolean; @@ -6593,27 +7173,27 @@ type SitesGetPhoneSiteDetailsResponse = { }; call_handling_forwarding_to_other_users?: { enable?: boolean; - call_forwarding_type?: number; + call_forwarding_type?: 1 | 2 | 3 | 4; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; check_voicemails_over_phone?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; call_queue_pickup_code?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; call_queue_opt_out_reason?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; call_queue_opt_out_reasons_list?: { code?: string; @@ -6626,70 +7206,70 @@ type SitesGetPhoneSiteDetailsResponse = { enable?: boolean; reset?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; items?: { - type?: string; + type?: "callLog" | "onDemandRecording" | "automaticRecording" | "voicemail" | "videomail" | "sms"; duration?: number; - time_unit?: string; + time_unit?: "year" | "month" | "day"; }[]; - delete_type?: number; + delete_type?: 1 | 2; }; call_park?: { call_not_picked_up_action?: number; enable?: boolean; - expiration_period?: number; + expiration_period?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; forward_to?: { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "zoomRoom" | "commonArea" | "ciscoRoom/polycomRoom" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; id?: string; }; - sequence?: number; + sequence?: 0 | 1; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; call_overflow?: { - call_overflow_type?: number; + call_overflow_type?: 1 | 2 | 3 | 4; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; call_transferring?: { - call_transferring_type?: number; + call_transferring_type?: 1 | 2 | 3 | 4; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; audio_intercom?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; block_calls_without_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; block_external_calls?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; block_business_hours?: boolean; block_closed_hours?: boolean; block_holiday_hours?: boolean; - block_call_action?: number; - block_call_change_type?: number; + block_call_action?: 0 | 9; + block_call_change_type?: 0 | 1; e2e_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; }; }; @@ -6697,12 +7277,215 @@ type SitesGetPhoneSiteDetailsResponse = { enable?: boolean; allow_extension_only_users_call_users_outside_site?: boolean; }; + external_calling_on_zoom_room_common_area?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + zoom_phone_on_pwa?: { + allow_calling?: boolean; + allow_sms_mms?: boolean; + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + sms_auto_reply?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + allow_end_user_edit_call_handling?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + allow_caller_reach_operator?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + forward_call_outside_of_site?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + allow_mobile_home_phone_callout?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + obfuscate_sensitive_data_during_call?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + prevent_users_upload_audio_files?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + voicemail_tasks?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + voicemail_intent_based_prioritization?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + team_sms_thread_summary?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + display_call_feedback_survey?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + feedback_type?: 1 | 2; + feedback_mos?: { + enable?: boolean; + min?: number; + max?: number; + }; + feedback_duration?: { + enable?: boolean; + min?: number; + max?: number; + }; + }; + call_live_transcription?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + transcription_start_prompt?: { + enable?: boolean; + audio_id?: string; + audio_name?: string; + }; + }; + call_screening?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + exclude_user_company_contacts?: boolean; + }; + sms_template?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + sms_template_list?: { + sms_template_id?: string; + name?: string; + description?: string; + content?: string; + active?: boolean; + }[]; + }; + advanced_encryption?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + disable_incoming_unencrypted_voicemail?: boolean; + }; + customize_line_name?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + user_line_name?: "phoneNumber" | "extensionNumber" | "displayName" | "displayName;extensionNumber" | "firstName;extensionNumber" | "firstName;lastName;extensionNumber"; + common_area_line_name?: "phoneNumber" | "extensionNumber" | "displayName" | "displayName;extensionNumber"; + }; + auto_opt_out_in_call_queue?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + prompt_before_opt_out_call_queue?: boolean; + }; + incoming_call_notification?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + block_type?: "block_activity" | "continue_with_alert"; + }; + call_summary?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + auto_call_summary?: boolean; + call_summary_start_prompt?: { + enable?: boolean; + audio_id?: string; + audio_name?: string; + }; + }; + schedule_firmware_update?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account"; + modified?: boolean; + repeat_type?: "weekly" | "monthly"; + repeat_setting?: { + weekly_setting?: { + weekday?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"; + }; + } | ({ + week_and_day?: { + week_of_month?: number; + weekday?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"; + }; + } | { + specific_date?: { + day_of_month?: number; + }; + }); + time_period_start?: number; + time_period_end?: number; + time_zone?: string; + end_setting?: { + never_end?: boolean; + end_date?: string; + }; + }; + zoom_phone_on_desktop?: { + allow_calling_clients?: ("mac_os" | "windows" | "vdi_client" | "linux")[]; + allow_sms_mms_clients?: ("mac_os" | "windows" | "vdi_client" | "linux")[]; + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; }; sip_zone?: { id?: string; name?: string; }; caller_id_name?: string; + india_state_code?: string; + india_city?: string; + india_sdca_npa?: string; + india_entity_name?: string; }; type SitesDeletePhoneSitePathParams = { siteId: string; @@ -6785,6 +7568,8 @@ type SitesUpdatePhoneSiteDetailsRequestBody = { enable?: boolean; reset?: boolean; locked?: boolean; + allow_calling_clients?: ("ios" | "android" | "intune" | "blackberry")[]; + allow_sms_mms_clients?: ("ios" | "android" | "intune" | "blackberry")[]; }; sms?: { enable?: boolean; @@ -6792,6 +7577,8 @@ type SitesUpdatePhoneSiteDetailsRequestBody = { locked?: boolean; international_sms?: boolean; international_sms_countries?: string[]; + allow_copy?: boolean; + allow_paste?: boolean; }; elevate_to_meeting?: { enable?: boolean; @@ -6821,9 +7608,9 @@ type SitesUpdatePhoneSiteDetailsRequestBody = { recording_transcription?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; }; auto_call_recording?: { @@ -6832,15 +7619,15 @@ type SitesUpdatePhoneSiteDetailsRequestBody = { enable?: boolean; reset?: boolean; locked?: boolean; - recording_calls?: string; + recording_calls?: "inbound" | "outbound" | "both"; recording_explicit_consent?: boolean; recording_start_prompt?: boolean; recording_transcription?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; inbound_audio_notification?: { recording_start_prompt?: boolean; @@ -6851,94 +7638,266 @@ type SitesUpdatePhoneSiteDetailsRequestBody = { recording_explicit_consent?: boolean; }; }; - call_handling_forwarding_to_other_users?: { + call_handling_forwarding_to_other_users?: { + enable?: boolean; + call_forwarding_type?: 1 | 2 | 3 | 4; + reset?: boolean; + locked?: boolean; + }; + check_voicemails_over_phone?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + call_queue_pickup_code?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + call_queue_opt_out_reason?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + call_queue_opt_out_reasons_list?: { + code?: string; + system?: boolean; + enable?: boolean; + }[]; + }; + show_user_last_transferred_call?: boolean; + auto_delete_data_after_retention_duration?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + items?: { + type?: "callLog" | "onDemandRecording" | "automaticRecording" | "voicemail" | "videomail" | "sms"; + duration?: number; + time_unit?: "year" | "month" | "day"; + }[]; + delete_type?: 1 | 2; + }; + call_park?: { + call_not_picked_up_action?: number; + enable?: boolean; + reset?: boolean; + locked?: boolean; + expiration_period?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; + forward_to_extension_id?: string; + sequence?: 0 | 1; + }; + call_overflow?: { + call_overflow_type?: 1 | 2 | 3 | 4; + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + call_transferring?: { + call_transferring_type?: 1 | 2 | 3 | 4; + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + audio_intercom?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + block_calls_without_caller_id?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + block_external_calls?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + block_business_hours?: boolean; + block_closed_hours?: boolean; + block_holiday_hours?: boolean; + block_call_action?: 0 | 9; + block_call_change_type?: 0 | 1; + e2e_encryption?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; + }; + }; + force_off_net?: { + enable?: boolean; + allow_extension_only_users_call_users_outside_site?: boolean; + }; + external_calling_on_zoom_room_common_area?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + zoom_phone_on_pwa?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + allow_calling?: boolean; + allow_sms_mms?: boolean; + }; + sms_auto_reply?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + allow_end_user_edit_call_handling?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + allow_caller_reach_operator?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + forward_call_outside_of_site?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + allow_mobile_home_phone_callout?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + obfuscate_sensitive_data_during_call?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + prevent_users_upload_audio_files?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + voicemail_tasks?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + }; + voicemail_intent_based_prioritization?: { enable?: boolean; - call_forwarding_type?: number; reset?: boolean; locked?: boolean; }; - check_voicemails_over_phone?: { + team_sms_thread_summary?: { enable?: boolean; reset?: boolean; locked?: boolean; }; - call_queue_pickup_code?: { + display_call_feedback_survey?: { enable?: boolean; reset?: boolean; locked?: boolean; + feedback_type?: 1 | 2; + feedback_mos?: { + enable?: boolean; + min?: number; + max?: number; + }; + feedback_duration?: { + enable?: boolean; + min?: number; + max?: number; + }; }; - call_queue_opt_out_reason?: { + call_live_transcription?: { enable?: boolean; reset?: boolean; locked?: boolean; - call_queue_opt_out_reasons_list?: { - code?: string; - system?: boolean; + transcription_start_prompt?: { enable?: boolean; - }[]; + audio_id?: string; + }; }; - show_user_last_transferred_call?: boolean; - auto_delete_data_after_retention_duration?: { + call_screening?: { enable?: boolean; reset?: boolean; locked?: boolean; - items?: { - type?: string; - duration?: number; - time_unit?: string; - }[]; - delete_type?: number; + exclude_user_company_contacts?: boolean; }; - call_park?: { - call_not_picked_up_action?: number; + sms_template?: { enable?: boolean; reset?: boolean; locked?: boolean; - expiration_period?: number; - forward_to_extension_id?: string; - sequence?: number; + sms_template_list?: { + sms_template_id: string; + active?: boolean; + }[]; }; - call_overflow?: { - call_overflow_type?: number; + advanced_encryption?: { enable?: boolean; reset?: boolean; locked?: boolean; + disable_incoming_unencrypted_voicemail?: boolean; }; - call_transferring?: { - call_transferring_type?: number; + customize_line_name?: { enable?: boolean; reset?: boolean; locked?: boolean; + user_line_name?: "phoneNumber" | "extensionNumber" | "displayName" | "displayName;extensionNumber" | "firstName;extensionNumber" | "firstName;lastName;extensionNumber"; + common_area_line_name?: "phoneNumber" | "extensionNumber" | "displayName" | "displayName;extensionNumber"; }; - audio_intercom?: { + auto_opt_out_in_call_queue?: { enable?: boolean; reset?: boolean; locked?: boolean; + prompt_before_opt_out_call_queue?: boolean; }; - block_calls_without_caller_id?: { + incoming_call_notification?: { enable?: boolean; reset?: boolean; locked?: boolean; + block_type?: "block_activity" | "continue_with_alert"; }; - block_external_calls?: { + call_summary?: { enable?: boolean; reset?: boolean; locked?: boolean; - block_business_hours?: boolean; - block_closed_hours?: boolean; - block_holiday_hours?: boolean; - block_call_action?: number; - block_call_change_type?: number; - e2e_encryption?: { + auto_call_summary?: boolean; + call_summary_start_prompt?: { enable?: boolean; - locked?: boolean; - locked_by?: string; - modified?: boolean; + audio_id?: string; }; }; - force_off_net?: { + schedule_firmware_update?: { enable?: boolean; - allow_extension_only_users_call_users_outside_site?: boolean; + reset?: boolean; + repeat_type?: "weekly" | "monthly"; + repeat_setting?: { + weekly_setting?: { + weekday?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"; + }; + } | { + monthly_setting?: { + week_and_day?: { + week_of_month?: number; + weekday?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"; + }; + } | { + specific_date?: { + day_of_month?: number; + }; + }; + }; + time_period_start?: number; + time_period_end?: number; + time_zone?: string; + end_setting?: { + never_end?: boolean; + end_date?: string; + }; + }; + zoom_phone_on_desktop?: { + enable?: boolean; + reset?: boolean; + locked?: boolean; + allow_calling_clients?: ("mac_os" | "windows" | "vdi_client" | "linux")[]; + allow_sms_mms_clients?: ("mac_os" | "windows" | "vdi_client" | "linux")[]; }; }; }; @@ -6948,7 +7907,7 @@ type SitesListCustomizedOutboundCallerIDPhoneNumbersPathParams = { type SitesListCustomizedOutboundCallerIDPhoneNumbersQueryParams = { selected?: boolean; site_id?: string; - extension_type?: string; + extension_type?: "autoReceptionist" | "callQueue" | "sharedLineGroup"; keyword?: string; page_size?: number; next_page_token?: string; @@ -6980,6 +7939,7 @@ type SitesAddCustomizedOutboundCallerIDPhoneNumbersPathParams = { type SitesAddCustomizedOutboundCallerIDPhoneNumbersRequestBody = { phone_number_ids?: string[]; }; +type SitesAddCustomizedOutboundCallerIDPhoneNumbersResponse = never; type SitesRemoveCustomizedOutboundCallerIDPhoneNumbersPathParams = { siteId: string; }; @@ -6988,7 +7948,7 @@ type SitesRemoveCustomizedOutboundCallerIDPhoneNumbersQueryParams = { }; type SitesGetPhoneSiteSettingPathParams = { siteId: string; - settingType: string; + settingType: "local_based_routing" | "business_hours" | "closed_hours" | "holiday_hours" | "security" | "outbound_caller_id" | "audio_prompt" | "desk_phone" | "dial_by_name" | "billing_account"; }; type SitesGetPhoneSiteSettingResponse = { location_based_routing?: { @@ -6997,12 +7957,12 @@ type SitesGetPhoneSiteSettingResponse = { enable_media_off_load_pstn_calls?: boolean; }; business_hours?: { - custom_hour_type?: number; + custom_hour_type?: 1 | 2; custom_hours?: { from?: string; to?: string; - type?: number; - weekday?: number; + type?: 0 | 1 | 2; + weekday?: 1 | 2 | 3 | 4 | 5 | 6 | 7; }[]; overflow?: { allow_caller_to_reach_operator?: boolean; @@ -7010,7 +7970,7 @@ type SitesGetPhoneSiteSettingResponse = { extension_id?: string; extension_number?: number; display_name?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "sharedLineGroup"; }; allow_caller_to_check_voicemail?: boolean; }; @@ -7022,7 +7982,7 @@ type SitesGetPhoneSiteSettingResponse = { extension_id?: string; extension_number?: number; display_name?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "sharedLineGroup"; }; allow_caller_to_check_voicemail?: boolean; }; @@ -7040,7 +8000,7 @@ type SitesGetPhoneSiteSettingResponse = { extension_id?: string; extension_number?: number; display_name?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "sharedLineGroup"; }; allow_caller_to_check_voicemail?: boolean; }; @@ -7151,14 +8111,18 @@ type SitesGetPhoneSiteSettingResponse = { }; desk_phone?: { hot_desking_session_timeout?: { - number: number; - unit?: string; + number: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30; + unit?: "minutes" | "hours"; + }; + general_setting?: { + setting_type?: "account_setting" | "custom_setting"; + web_interface?: boolean; }; }; dial_by_name?: { status?: boolean; inherit?: boolean; - rule?: string; + rule?: "first_name" | "last_name"; }; billing_account?: { id?: string; @@ -7167,7 +8131,7 @@ type SitesGetPhoneSiteSettingResponse = { }; type SitesAddSiteSettingPathParams = { siteId: string; - settingType: string; + settingType: "holiday_hours" | "security"; }; type SitesAddSiteSettingRequestBody = { device_type?: string; @@ -7187,7 +8151,7 @@ type SitesAddSiteSettingResponse = { }; type SitesDeleteSiteSettingPathParams = { siteId: string; - settingType: string; + settingType: "holiday_hours" | "security"; }; type SitesDeleteSiteSettingQueryParams = { device_type?: string; @@ -7195,7 +8159,7 @@ type SitesDeleteSiteSettingQueryParams = { }; type SitesUpdateSiteSettingPathParams = { siteId: string; - settingType: string; + settingType: "local_based_routing" | "business_hours" | "closed_hours" | "holiday_hours" | "outbound_caller_id" | "audio_prompt" | "desk_phone" | "dial_by_name" | "billing_account"; }; type SitesUpdateSiteSettingRequestBody = { location_based_routing?: { @@ -7204,12 +8168,12 @@ type SitesUpdateSiteSettingRequestBody = { enable_media_off_load_pstn_calls?: boolean; }; business_hours?: { - custom_hour_type?: number; + custom_hour_type?: 1 | 2; custom_hours?: { from?: string; to?: string; - type?: number; - weekday?: number; + type?: 0 | 1 | 2; + weekday?: 1 | 2 | 3 | 4 | 5 | 6 | 7; }[]; overflow?: { allow_caller_to_reach_operator?: boolean; @@ -7326,14 +8290,18 @@ type SitesUpdateSiteSettingRequestBody = { }; desk_phone?: { hot_desking_session_timeout?: { - number: number; - unit?: string; + number: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30; + unit?: "minutes" | "hours"; }; }; + general_setting?: { + setting_type?: "account_setting" | "custom_setting"; + web_interface?: boolean; + }; dial_by_name?: { status?: boolean; inherit?: boolean; - rule?: string; + rule?: "first_name" | "last_name"; }; billing_account?: { id?: string; @@ -7344,7 +8312,7 @@ type UsersListPhoneUsersQueryParams = { next_page_token?: string; site_id?: string; calling_type?: number; - status?: string; + status?: "activate" | "deactivate" | "pending"; department?: string; cost_center?: string; keyword?: string; @@ -7359,6 +8327,8 @@ type UsersListPhoneUsersResponse = { type?: number; billing_account_id?: string; billing_account_name?: string; + billing_subscription_id?: string; + billing_subscription_name?: string; }[]; email?: string; extension_id?: string; @@ -7380,7 +8350,7 @@ type UsersListPhoneUsersResponse = { }[]; }; type UsersUpdateMultipleUsersPropertiesInBatchRequestBody = { - batch_type?: string; + batch_type?: "move_site" | "assign_pending_user"; user_ids?: string[]; site_id?: string; }; @@ -7418,6 +8388,8 @@ type UsersGetUsersProfileResponse = { type?: number; billing_account_id?: string; billing_account_name?: string; + billing_subscription_id?: string; + billing_subscription_name?: string; }[]; cost_center?: string; department?: string; @@ -7446,12 +8418,12 @@ type UsersGetUsersProfileResponse = { recording_transcription?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; }; ad_hoc_call_recording_access_members?: ({ access_user_id?: string; @@ -7465,16 +8437,16 @@ type UsersGetUsersProfileResponse = { disconnect_on_recording_failure?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; - recording_calls?: string; + locked_by?: "account" | "user_group" | "site"; + recording_calls?: "inbound" | "outbound" | "both"; recording_explicit_consent?: boolean; recording_start_prompt?: boolean; recording_transcription?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; inbound_audio_notification?: { recording_start_prompt?: boolean; @@ -7493,31 +8465,31 @@ type UsersGetUsersProfileResponse = { shared_id?: string; })[]; call_overflow?: { - call_overflow_type?: number; + call_overflow_type?: 1 | 2 | 3 | 4; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; }; call_park?: { call_not_picked_up_action?: number; enable?: boolean; - expiration_period?: number; + expiration_period?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; forward_to?: { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "zoomRoom" | "commonArea" | "ciscoRoom/polycomRoom" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; id?: string; }; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; }; call_transferring?: { - call_transferring_type?: number; + call_transferring_type?: 1 | 2 | 3 | 4; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; }; delegation?: boolean; elevate_to_meeting?: boolean; @@ -7529,34 +8501,36 @@ type UsersGetUsersProfileResponse = { forwarding_to_external_numbers?: boolean; call_handling_forwarding_to_other_users?: { enable?: boolean; - call_forwarding_type?: number; + call_forwarding_type?: 1 | 2 | 3 | 4; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; }; hand_off_to_room?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; }; international_calling?: boolean; mobile_switch_to_carrier?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; }; select_outbound_caller_id?: { enable?: boolean; allow_hide_outbound_caller_id?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; }; sms?: { enable?: boolean; international_sms?: boolean; international_sms_countries?: string[]; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; + allow_copy?: boolean; + allow_paste?: boolean; }; voicemail?: { allow_delete?: boolean; @@ -7574,15 +8548,17 @@ type UsersGetUsersProfileResponse = { shared_id?: string; })[]; zoom_phone_on_mobile?: { + allow_calling_clients?: ("ios" | "android" | "intune" | "blackberry")[]; + allow_sms_mms_clients?: ("ios" | "android" | "intune" | "blackberry")[]; allow_calling_sms_mms?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; }; personal_audio_library?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; allow_music_on_hold_customization?: boolean; allow_voicemail_and_message_greeting_customization?: boolean; @@ -7590,7 +8566,7 @@ type UsersGetUsersProfileResponse = { voicemail_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; }; voicemail_notification_by_email?: { @@ -7598,37 +8574,37 @@ type UsersGetUsersProfileResponse = { include_voicemail_transcription?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; }; shared_voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; }; check_voicemails_over_phone?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; }; audio_intercom?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; }; peer_to_peer_media?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; }; e2e_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; }; outbound_calling?: { @@ -7649,19 +8625,27 @@ type UsersGetUsersProfileResponse = { voicemail_intent_based_prioritization?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; modified?: boolean; }; voicemail_tasks?: { enable?: boolean; locked?: boolean; modified?: boolean; - locked_by?: string; + locked_by?: "account" | "user_group" | "site"; + }; + zoom_phone_on_desktop?: { + allow_calling_clients?: ("mac_os" | "windows" | "vdi_client" | "linux")[]; + allow_sms_mms_clients?: ("mac_os" | "windows" | "vdi_client" | "linux")[]; + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "site"; + modified?: boolean; }; }; site_admin?: boolean; site_id?: string; - status?: string; + status?: "activate" | "deactivate"; }; type UsersUpdateUsersProfilePathParams = { userId: string; @@ -7677,24 +8661,24 @@ type UsersUpdateUsersProfileRequestBody = { reset?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; }; auto_call_recording?: { allow_stop_resume_recording?: boolean; disconnect_on_recording_failure?: boolean; enable?: boolean; - recording_calls?: string; + recording_calls?: "inbound" | "outbound" | "both"; recording_explicit_consent?: boolean; recording_start_prompt?: boolean; recording_transcription?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; reset?: boolean; inbound_audio_notification?: { @@ -7707,18 +8691,18 @@ type UsersUpdateUsersProfileRequestBody = { }; }; call_overflow?: { - call_overflow_type?: number; + call_overflow_type?: 1 | 2 | 3 | 4; enable?: boolean; reset?: boolean; }; call_park?: { call_not_picked_up_action?: number; enable?: boolean; - expiration_period?: number; + expiration_period?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; forward_to_extension_id?: string; }; call_transferring?: { - call_transferring_type?: number; + call_transferring_type?: 1 | 2 | 3 | 4; enable?: boolean; reset?: boolean; }; @@ -7732,7 +8716,7 @@ type UsersUpdateUsersProfileRequestBody = { forwarding_to_external_numbers?: boolean; call_handling_forwarding_to_other_users?: { enable?: boolean; - call_forwarding_type?: number; + call_forwarding_type?: 1 | 2 | 3 | 4; reset?: boolean; }; hand_off_to_room?: { @@ -7750,6 +8734,8 @@ type UsersUpdateUsersProfileRequestBody = { enable?: boolean; international_sms?: boolean; international_sms_countries?: string[]; + allow_copy?: boolean; + allow_paste?: boolean; }; voicemail?: { allow_delete?: boolean; @@ -7767,6 +8753,8 @@ type UsersUpdateUsersProfileRequestBody = { zoom_phone_on_mobile?: { allow_calling_sms_mms?: boolean; enable?: boolean; + allow_calling_clients?: ("ios" | "android" | "intune" | "blackberry")[]; + allow_sms_mms_clients?: ("ios" | "android" | "intune" | "blackberry")[]; }; personal_audio_library?: { allow_music_on_hold_customization?: boolean; @@ -7800,6 +8788,12 @@ type UsersUpdateUsersProfileRequestBody = { enable?: boolean; reset?: boolean; }; + zoom_phone_on_desktop?: { + enable?: boolean; + reset?: boolean; + allow_calling_clients?: ("mac_os" | "windows" | "vdi_client" | "linux")[]; + allow_sms_mms_clients?: ("mac_os" | "windows" | "vdi_client" | "linux")[]; + }; }; site_id?: string; template_id?: string; @@ -7810,6 +8804,8 @@ type UsersUpdateUsersCallingPlanPathParams = { type UsersUpdateUsersCallingPlanRequestBody = { source_type: number; target_type: number; + source_billing_subscription_id?: string; + target_billing_subscription_id?: string; }; type UsersAssignCallingPlanToUserPathParams = { userId: string; @@ -7818,6 +8814,7 @@ type UsersAssignCallingPlanToUserRequestBody = { calling_plans?: { type?: number; billing_account_id?: string; + billing_subscription_id?: string; }[]; }; type UsersUnassignUsersCallingPlanPathParams = { @@ -7833,7 +8830,7 @@ type UsersListUsersPhoneNumbersForCustomizedOutboundCallerIDPathParams = { type UsersListUsersPhoneNumbersForCustomizedOutboundCallerIDQueryParams = { selected?: boolean; site_id?: string; - extension_type?: string; + extension_type?: "autoReceptionist" | "callQueue" | "sharedLineGroup"; keyword?: string; page_size?: number; next_page_token?: string; @@ -7871,6 +8868,32 @@ type UsersRemoveUsersCustomizedOutboundCallerIDPhoneNumbersPathParams = { type UsersRemoveUsersCustomizedOutboundCallerIDPhoneNumbersQueryParams = { customize_ids?: string[]; }; +type UsersGetUserPolicyDetailsPathParams = { + userId: string; + policyType: "allow_emergency_calls"; +}; +type UsersGetUserPolicyDetailsResponse = { + allow_emergency_calls?: { + enable?: boolean; + locked?: boolean; + locked_by?: "invalid" | "account" | "user_group" | "site"; + modified?: boolean; + allow_emergency_calls_from_clients?: boolean; + allow_emergency_calls_from_deskphones?: boolean; + }; +}; +type UsersUpdateUserPolicyPathParams = { + userId: string; + policyType: "allow_emergency_calls"; +}; +type UsersUpdateUserPolicyRequestBody = { + allow_emergency_calls?: { + enable?: boolean; + reset?: boolean; + allow_emergency_calls_from_clients?: boolean; + allow_emergency_calls_from_deskphones?: boolean; + }; +}; type UsersGetUsersProfileSettingsPathParams = { userId: string; }; @@ -7902,13 +8925,13 @@ type UsersGetUsersProfileSettingsResponse = { id?: string; policy?: { call_control?: { - status?: string; + status?: "unsupported" | "on" | "off"; }; hot_desking?: { - status?: string; + status?: "unsupported" | "on" | "off"; }; }; - status?: string; + status?: "online" | "offline"; mac_address?: string; private_ip?: string; public_ip?: string; @@ -7929,7 +8952,7 @@ type UsersGetUsersProfileSettingsResponse = { name?: string; number?: string; }[]; - status?: string; + status?: "Active" | "Inactive"; voice_mail?: { access_user_id?: string; delete?: boolean; @@ -7942,9 +8965,9 @@ type UsersGetUsersProfileSettingsResponse = { extension_number?: string; extension_type?: string; display_name?: string; - status?: string; + status?: "active" | "pending"; device_id?: string; - device_status?: string; + device_status?: "online" | "offline" | "no device"; }[]; device?: { id?: string; @@ -8071,10 +9094,10 @@ type UsersUpdateUsersSharedAccessSettingRequestBody = { id?: string; policy?: { call_control?: { - status?: string; + status?: "on" | "off"; }; hot_desking?: { - status?: string; + status?: "on" | "off"; }; }; }[]; @@ -8124,18 +9147,18 @@ type VoicemailsGetUserVoicemailDetailsFromCallLogResponse = { call_history_id?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; + callee_number_type?: 1 | 2 | 3; caller_name?: string; caller_number?: string; - caller_number_type?: number; + caller_number_type?: 1 | 2; date_time?: string; download_url?: string; duration?: number; id?: string; - status?: string; + status?: "read" | "unread"; transcription?: { content?: string; - status?: number; + status?: 0 | 1 | 2 | 4 | 5 | 9 | 11 | 12 | 13 | 14 | 409 | 415 | 422 | 500 | 601 | 602 | 603 | 999; engine?: string; }; }; @@ -8144,7 +9167,7 @@ type VoicemailsGetUsersVoicemailsPathParams = { }; type VoicemailsGetUsersVoicemailsQueryParams = { page_size?: number; - status?: string; + status?: "all" | "read" | "unread"; next_page_token?: string; from?: string; to?: string; @@ -8163,23 +9186,23 @@ type VoicemailsGetUsersVoicemailsResponse = { call_history_id?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; + callee_number_type?: 1 | 2 | 3; caller_name?: string; caller_number?: string; - caller_number_type?: number; + caller_number_type?: 1 | 2; date_time?: string; download_url?: string; duration?: number; id?: string; - status?: string; + status?: "read" | "unread"; }[]; }; type VoicemailsGetAccountVoicemailsQueryParams = { page_size?: number; - status?: string; + status?: "all" | "read" | "unread"; site_id?: string; - owner_type?: string; - voicemail_type?: string; + owner_type?: "user" | "callQueue" | "sharedLineGroup" | "autoReceptionist" | "commonArea"; + voicemail_type?: "normal" | "spam" | "maybeSpam"; next_page_token?: string; from?: string; to?: string; @@ -8197,26 +9220,26 @@ type VoicemailsGetAccountVoicemailsResponse = { call_log_id?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; + callee_number_type?: 1 | 2 | 3; caller_name?: string; caller_number?: string; - caller_number_type?: number; + caller_number_type?: 1 | 2; date_time?: string; download_url?: string; duration?: number; id?: string; - status?: string; + status?: "read" | "unread"; owner?: { extension_number?: number; id?: string; name?: string; - type?: string; - extension_status?: string; + type?: "user" | "callQueue" | "sharedLineGroup" | "autoReceptionist" | "commonArea"; + extension_status?: "inactive" | "deleted"; extension_deleted_time?: string; }; deleted_time?: string; days_left_auto_permantely_delete?: number; - soft_deleted_type?: string; + soft_deleted_type?: "Manual" | "Data Retention"; }[]; }; type VoicemailsDownloadPhoneVoicemailPathParams = { @@ -8228,41 +9251,45 @@ type VoicemailsGetVoicemailDetailsPathParams = { type VoicemailsGetVoicemailDetailsResponse = { call_id?: string; call_log_id?: string; + call_history_id?: string; callee_name?: string; callee_number?: string; - callee_number_type?: number; + callee_number_type?: 1 | 2 | 3; caller_name?: string; caller_number?: string; - caller_number_type?: number; + caller_number_type?: 1 | 2; date_time?: string; download_url?: string; duration?: number; id?: string; - status?: string; + status?: "read" | "unread"; transcription?: { content?: string; - status?: number; + status?: 0 | 1 | 2 | 4 | 5 | 9 | 11 | 12 | 13 | 14 | 409 | 415 | 422 | 500 | 601 | 602 | 603 | 999; engine?: string; }; deleted_time?: string; days_left_auto_permantely_delete?: number; - soft_deleted_type?: string; - intent_detect_status?: string; + soft_deleted_type?: "Manual" | "Data Retention"; + intent_detect_status?: "not_started" | "processing" | "success" | "ai_detection_failed" | "unknown_reason_failed"; intent_results?: { intent_id?: string; confidence_score?: number; }[]; voice_mail_task?: { - status?: string; + status?: "processing" | "success" | "no_task" | "failure"; content?: string; - feedback?: string; + feedback?: "none" | "thumbs_up" | "thumbs_down"; }; }; +type VoicemailsDeleteVoicemailPathParams = { + voicemailId: string; +}; type VoicemailsUpdateVoicemailReadStatusPathParams = { voicemailId: string; }; type VoicemailsUpdateVoicemailReadStatusQueryParams = { - read_status: string; + read_status: "Read" | "Unread"; }; type ZoomRoomsListZoomRoomsUnderZoomPhoneLicenseQueryParams = { page_size?: number; @@ -8280,6 +9307,8 @@ type ZoomRoomsListZoomRoomsUnderZoomPhoneLicenseResponse = { type?: number; billing_account_id?: string; billing_account_name?: string; + billing_subscription_id?: string; + billing_subscription_name?: string; }[]; extension_id?: string; extension_number?: number; @@ -8301,6 +9330,7 @@ type ZoomRoomsAddZoomRoomToZoomPhoneRequestBody = { site_id?: string; calling_plans?: { type?: number; + billing_subscription_id?: string; }[]; }; type ZoomRoomsListZoomRoomsWithoutZoomPhoneAssignmentQueryParams = { @@ -8325,6 +9355,8 @@ type ZoomRoomsGetZoomRoomUnderZoomPhoneLicenseResponse = { type?: number; billing_account_id?: string; billing_account_name?: string; + billing_subscription_id?: string; + billing_subscription_name?: string; }[]; emergency_address?: { address_line1?: string; @@ -8346,11 +9378,11 @@ type ZoomRoomsGetZoomRoomUnderZoomPhoneLicenseResponse = { policy?: { international_calling?: { enable?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group" | "site" | "extension"; }; select_outbound_caller_id?: { enable?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group" | "site" | "extension"; }; }; site?: { @@ -8385,6 +9417,7 @@ type ZoomRoomsAssignCallingPlansToZoomRoomRequestBody = { calling_plans?: { type?: number; billing_account_id?: string; + billing_subscription_id?: string; }[]; }; type ZoomRoomsRemoveCallingPlanFromZoomRoomPathParams = { @@ -8403,6 +9436,7 @@ type ZoomRoomsAssignPhoneNumbersToZoomRoomRequestBody = { number?: string; }[]; }; +type ZoomRoomsAssignPhoneNumbersToZoomRoomResponse = object; type ZoomRoomsRemovePhoneNumberFromZoomRoomPathParams = { roomId: string; phoneNumberId: string; @@ -8417,7 +9451,7 @@ declare class PhoneEndpoints extends WebEndpoints { }) => Promise>; addPhoneNumbersForAccountsCustomizedOutboundCallerID: (_: object & { body?: AccountsAddPhoneNumbersForAccountsCustomizedOutboundCallerIDRequestBody; - }) => Promise>; + }) => Promise>; deletePhoneNumbersForAccountsCustomizedOutboundCallerID: (_: object & { query?: AccountsDeletePhoneNumbersForAccountsCustomizedOutboundCallerIDQueryParams; }) => Promise>; @@ -8560,7 +9594,7 @@ declare class PhoneEndpoints extends WebEndpoints { description?: string; phone_number?: string; }; - sub_setting_type?: string; + sub_setting_type?: "call_forwarding"; }; } | { body?: { @@ -8569,7 +9603,7 @@ declare class PhoneEndpoints extends WebEndpoints { from?: string; to?: string; }; - sub_setting_type?: string; + sub_setting_type?: "holiday"; }; }) & object)) => Promise>; deleteCallHandlingSetting: (_: { @@ -8593,7 +9627,7 @@ declare class PhoneEndpoints extends WebEndpoints { }[]; require_press_1_before_connecting?: boolean; }; - sub_setting_type?: string; + sub_setting_type?: "call_forwarding"; }; } | { body?: { @@ -8603,7 +9637,7 @@ declare class PhoneEndpoints extends WebEndpoints { name?: string; to?: string; }; - sub_setting_type?: string; + sub_setting_type?: "holiday"; }; } | { body?: { @@ -8612,12 +9646,12 @@ declare class PhoneEndpoints extends WebEndpoints { custom_hours_settings?: { from?: string; to?: string; - type?: number; - weekday?: number; + type?: 0 | 1 | 2; + weekday?: 1 | 2 | 3 | 4 | 5 | 6 | 7; }[]; - type?: number; + type?: 1 | 2; }; - sub_setting_type?: string; + sub_setting_type?: "custom_hours"; }; } | { body?: { @@ -8627,12 +9661,12 @@ declare class PhoneEndpoints extends WebEndpoints { audio_while_connecting_id?: string; call_distribution?: { handle_multiple_calls?: boolean; - ring_duration?: number; - ring_mode?: string; + ring_duration?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; + ring_mode?: "simultaneous" | "sequential" | "rotating" | "longest_idle"; skip_offline_device_phone_number?: boolean; }; - call_not_answer_action?: number; - busy_on_another_call_action?: number; + call_not_answer_action?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 18 | 19; + busy_on_another_call_action?: 1 | 2 | 4 | 6 | 7 | 8 | 9 | 10 | 12 | 21 | 22; busy_require_press_1_before_connecting?: boolean; un_answered_require_press_1_before_connecting?: boolean; overflow_play_callee_voicemail_greeting?: boolean; @@ -8647,20 +9681,20 @@ declare class PhoneEndpoints extends WebEndpoints { busy_forward_to_extension_id?: string; greeting_prompt_id?: string; max_call_in_queue?: number; - max_wait_time?: number; + max_wait_time?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 120 | 180 | 240 | 300 | 600 | 900 | 1200 | 1500 | 1800; music_on_hold_id?: string; operator_extension_id?: string; receive_call?: boolean; - ring_mode?: string; + ring_mode?: "simultaneous" | "sequential"; voicemail_greeting_id?: string; voicemail_leaving_instruction_id?: string; message_greeting_id?: string; forward_to_zcc_phone_number?: string; forward_to_partner_contact_center_id?: string; forward_to_teams_id?: string; - wrap_up_time?: number; + wrap_up_time?: 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 120 | 180 | 240 | 300; }; - sub_setting_type?: string; + sub_setting_type?: "call_handling"; }; }) & object)) => Promise>; }; @@ -8676,6 +9710,9 @@ declare class PhoneEndpoints extends WebEndpoints { } & { body: CallLogsAddClientCodeToCallHistoryRequestBody; } & object) => Promise>; + getCallHistoryDetail: (_: { + path: CallLogsGetCallHistoryDetailPathParams; + } & object) => Promise>; getAccountsCallLogs: (_: object & { query?: CallLogsGetAccountsCallLogsQueryParams; }) => Promise>; @@ -8687,6 +9724,9 @@ declare class PhoneEndpoints extends WebEndpoints { } & { body: CallLogsAddClientCodeToCallLogRequestBody; } & object) => Promise>; + getUserAICallSummaryDetail: (_: { + path: CallLogsGetUserAICallSummaryDetailPathParams; + } & object) => Promise>; getUsersCallHistory: (_: { path: CallLogsGetUsersCallHistoryPathParams; } & object & { @@ -8715,6 +9755,9 @@ declare class PhoneEndpoints extends WebEndpoints { } & object) => Promise>; }; readonly callQueues: { + listCallQueueAnalytics: (_: object & { + query?: CallQueuesListCallQueueAnalyticsQueryParams; + }) => Promise>; listCallQueues: (_: object & { query?: CallQueuesListCallQueuesQueryParams; }) => Promise>; @@ -8757,11 +9800,11 @@ declare class PhoneEndpoints extends WebEndpoints { unassignPhoneNumber: (_: { path: CallQueuesUnassignPhoneNumberPathParams; } & object) => Promise>; - addPolicySettingToCallQueue: (_: { - path: CallQueuesAddPolicySettingToCallQueuePathParams; + addPolicySubsettingToCallQueue: (_: { + path: CallQueuesAddPolicySubsettingToCallQueuePathParams; } & { - body?: CallQueuesAddPolicySettingToCallQueueRequestBody; - } & object) => Promise>; + body?: CallQueuesAddPolicySubsettingToCallQueueRequestBody; + } & object) => Promise>; deleteCQPolicySetting: (_: { path: CallQueuesDeleteCQPolicySettingPathParams; } & object & { @@ -8799,6 +9842,9 @@ declare class PhoneEndpoints extends WebEndpoints { addCommonArea: (_: object & { body: CommonAreasAddCommonAreaRequestBody; }) => Promise>; + generateActivationCodesForCommonAreas: (_: object & { + body: CommonAreasGenerateActivationCodesForCommonAreasRequestBody; + }) => Promise>; listActivationCodes: (_: object & { query?: CommonAreasListActivationCodesQueryParams; }) => Promise>; @@ -8844,20 +9890,20 @@ declare class PhoneEndpoints extends WebEndpoints { getCommonAreaSettings: (_: { path: CommonAreasGetCommonAreaSettingsPathParams; } & object) => Promise>; - addCommonAreaSettings: (_: { - path: CommonAreasAddCommonAreaSettingsPathParams; + addCommonAreaSetting: (_: { + path: CommonAreasAddCommonAreaSettingPathParams; } & { - body?: CommonAreasAddCommonAreaSettingsRequestBody; - } & object) => Promise>; + body?: CommonAreasAddCommonAreaSettingRequestBody; + } & object) => Promise>; deleteCommonAreaSetting: (_: { path: CommonAreasDeleteCommonAreaSettingPathParams; } & object & { query: CommonAreasDeleteCommonAreaSettingQueryParams; }) => Promise>; - updateCommonAreaSettings: (_: { - path: CommonAreasUpdateCommonAreaSettingsPathParams; + updateCommonAreaSetting: (_: { + path: CommonAreasUpdateCommonAreaSettingPathParams; } & { - body?: CommonAreasUpdateCommonAreaSettingsRequestBody; + body?: CommonAreasUpdateCommonAreaSettingRequestBody; } & object) => Promise>; }; readonly dashboard: { @@ -8870,6 +9916,24 @@ declare class PhoneEndpoints extends WebEndpoints { getCallDetailsFromCallLog: (_: { path: DashboardGetCallDetailsFromCallLogPathParams; } & object) => Promise>; + listDefaultEmergencyAddressUsers: (_: object & { + query: DashboardListDefaultEmergencyAddressUsersQueryParams; + }) => Promise>; + listDetectablePersonalLocationUsers: (_: object & { + query: DashboardListDetectablePersonalLocationUsersQueryParams; + }) => Promise>; + listUsersPermissionForLocationSharing: (_: object & { + query?: DashboardListUsersPermissionForLocationSharingQueryParams; + }) => Promise>; + listNomadicEmergencyServicesUsers: (_: object & { + query: DashboardListNomadicEmergencyServicesUsersQueryParams; + }) => Promise>; + listRealTimeLocationForIPPhones: (_: object & { + query: DashboardListRealTimeLocationForIPPhonesQueryParams; + }) => Promise>; + listRealTimeLocationForUsers: (_: object & { + query: DashboardListRealTimeLocationForUsersQueryParams; + }) => Promise>; listTrackedLocations: (_: object & { query?: DashboardListTrackedLocationsQueryParams; }) => Promise>; @@ -9030,6 +10094,14 @@ declare class PhoneEndpoints extends WebEndpoints { } & object) => Promise>; }; readonly groups: { + getGroupPolicyDetails: (_: { + path: GroupsGetGroupPolicyDetailsPathParams; + } & object) => Promise>; + updateGroupPolicy: (_: { + path: GroupsUpdateGroupPolicyPathParams; + } & { + body?: GroupsUpdateGroupPolicyRequestBody; + } & object) => Promise>; getGroupPhoneSettings: (_: { path: GroupsGetGroupPhoneSettingsPathParams; } & object & { @@ -9130,7 +10202,7 @@ declare class PhoneEndpoints extends WebEndpoints { body: MonitoringGroupsAddMembersToMonitoringGroupRequestBody; } & { query: MonitoringGroupsAddMembersToMonitoringGroupQueryParams; - }) => Promise>; + }) => Promise>; removeAllMonitorsOrMonitoredMembersFromMonitoringGroup: (_: { path: MonitoringGroupsRemoveAllMonitorsOrMonitoredMembersFromMonitoringGroupPathParams; } & object & { @@ -9273,7 +10345,7 @@ declare class PhoneEndpoints extends WebEndpoints { path: PhoneDevicesAssignEntityToDevicePathParams; } & { body: PhoneDevicesAssignEntityToDeviceRequestBody; - } & object) => Promise>; + } & object) => Promise>; unassignEntityFromDevice: (_: { path: PhoneDevicesUnassignEntityFromDevicePathParams; } & object) => Promise>; @@ -9285,6 +10357,9 @@ declare class PhoneEndpoints extends WebEndpoints { rebootDeskPhone: (_: { path: PhoneDevicesRebootDeskPhonePathParams; } & object) => Promise>; + listSmartphones: (_: object & { + query?: PhoneDevicesListSmartphonesQueryParams; + }) => Promise>; }; readonly phoneNumbers: { addBYOCPhoneNumbers: (_: object & { @@ -9353,6 +10428,21 @@ declare class PhoneEndpoints extends WebEndpoints { } & object & { query: PhoneRolesDeleteMembersInRoleQueryParams; }) => Promise>; + listPhoneRoleTargets: (_: { + path: PhoneRolesListPhoneRoleTargetsPathParams; + } & object & { + query?: PhoneRolesListPhoneRoleTargetsQueryParams; + }) => Promise>; + addPhoneRoleTargets: (_: { + path: PhoneRolesAddPhoneRoleTargetsPathParams; + } & { + body: PhoneRolesAddPhoneRoleTargetsRequestBody; + } & object) => Promise>; + deletePhoneRoleTargets: (_: { + path: PhoneRolesDeletePhoneRoleTargetsPathParams; + } & { + body: PhoneRolesDeletePhoneRoleTargetsRequestBody; + } & object) => Promise>; }; readonly privateDirectory: { listPrivateDirectoryMembers: (_: object & { @@ -9471,6 +10561,9 @@ declare class PhoneEndpoints extends WebEndpoints { } & object) => Promise>; }; readonly sMS: { + postSMSMessage: (_: object & { + body: SMSPostSMSMessageRequestBody; + }) => Promise>; getAccountsSMSSessions: (_: object & { query?: SMSGetAccountsSMSSessionsQueryParams; }) => Promise>; @@ -9508,7 +10601,7 @@ declare class PhoneEndpoints extends WebEndpoints { assignPhoneNumberToSMSCampaign: (_: { path: SMSCampaignAssignPhoneNumberToSMSCampaignPathParams; } & { - body?: SMSCampaignAssignPhoneNumberToSMSCampaignRequestBody; + body: SMSCampaignAssignPhoneNumberToSMSCampaignRequestBody; } & object) => Promise>; listOptStatusesOfPhoneNumbersAssignedToSMSCampaign: (_: { path: SMSCampaignListOptStatusesOfPhoneNumbersAssignedToSMSCampaignPathParams; @@ -9523,6 +10616,11 @@ declare class PhoneEndpoints extends WebEndpoints { unassignPhoneNumber: (_: { path: SMSCampaignUnassignPhoneNumberPathParams; } & object) => Promise>; + listUsersOptStatusesOfPhoneNumbers: (_: { + path: SMSCampaignListUsersOptStatusesOfPhoneNumbersPathParams; + } & object & { + query: SMSCampaignListUsersOptStatusesOfPhoneNumbersQueryParams; + }) => Promise>; }; readonly settingTemplates: { listSettingTemplates: (_: object & { @@ -9543,6 +10641,14 @@ declare class PhoneEndpoints extends WebEndpoints { } & object) => Promise>; }; readonly settings: { + getAccountPolicyDetails: (_: { + path: SettingsGetAccountPolicyDetailsPathParams; + } & object) => Promise>; + updateAccountPolicy: (_: { + path: SettingsUpdateAccountPolicyPathParams; + } & { + body?: SettingsUpdateAccountPolicyRequestBody; + } & object) => Promise>; listPortedNumbers: (_: object & { query?: SettingsListPortedNumbersQueryParams; }) => Promise>; @@ -9658,7 +10764,7 @@ declare class PhoneEndpoints extends WebEndpoints { path: SitesAddCustomizedOutboundCallerIDPhoneNumbersPathParams; } & { body?: SitesAddCustomizedOutboundCallerIDPhoneNumbersRequestBody; - } & object) => Promise>; + } & object) => Promise>; removeCustomizedOutboundCallerIDPhoneNumbers: (_: { path: SitesRemoveCustomizedOutboundCallerIDPhoneNumbersPathParams; } & object & { @@ -9731,6 +10837,14 @@ declare class PhoneEndpoints extends WebEndpoints { } & object & { query?: UsersRemoveUsersCustomizedOutboundCallerIDPhoneNumbersQueryParams; }) => Promise>; + getUserPolicyDetails: (_: { + path: UsersGetUserPolicyDetailsPathParams; + } & object) => Promise>; + updateUserPolicy: (_: { + path: UsersUpdateUserPolicyPathParams; + } & { + body?: UsersUpdateUserPolicyRequestBody; + } & object) => Promise>; getUsersProfileSettings: (_: { path: UsersGetUsersProfileSettingsPathParams; } & object) => Promise>; @@ -9773,6 +10887,9 @@ declare class PhoneEndpoints extends WebEndpoints { getVoicemailDetails: (_: { path: VoicemailsGetVoicemailDetailsPathParams; } & object) => Promise>; + deleteVoicemail: (_: { + path: VoicemailsDeleteVoicemailPathParams; + } & object) => Promise>; updateVoicemailReadStatus: (_: { path: VoicemailsUpdateVoicemailReadStatusPathParams; } & object & { @@ -9814,7 +10931,7 @@ declare class PhoneEndpoints extends WebEndpoints { path: ZoomRoomsAssignPhoneNumbersToZoomRoomPathParams; } & { body?: ZoomRoomsAssignPhoneNumbersToZoomRoomRequestBody; - } & object) => Promise>; + } & object) => Promise>; removePhoneNumberFromZoomRoom: (_: { path: ZoomRoomsRemovePhoneNumberFromZoomRoomPathParams; } & object) => Promise>; @@ -9822,7 +10939,7 @@ declare class PhoneEndpoints extends WebEndpoints { } type PhoneRecordingDeletedEvent = Event<"phone.recording_deleted"> & { - event: string; + event: "phone.recording_deleted"; event_ts: number; payload: { account_id: string; @@ -9835,7 +10952,7 @@ type PhoneRecordingDeletedEvent = Event<"phone.recording_deleted"> & { }; }; type PhoneCallerCallLogCompletedEvent = Event<"phone.caller_call_log_completed"> & { - event: string; + event: "phone.caller_call_log_completed"; event_ts: number; payload: { account_id: string; @@ -9844,16 +10961,16 @@ type PhoneCallerCallLogCompletedEvent = Event<"phone.caller_call_log_completed"> call_logs: { id: string; caller_number: string; - caller_number_type: number; - caller_number_source?: string; + caller_number_type: 1 | 2; + caller_number_source?: "internal" | "external" | "byop"; caller_name?: string; caller_location?: string; caller_did_number?: string; caller_country_code?: string; caller_country_iso_code?: string; callee_number: string; - callee_number_type: number; - callee_number_source?: string; + callee_number_type: 1 | 2 | 3; + callee_number_source?: "internal" | "external" | "byop"; callee_name?: string; callee_location?: string; callee_did_number?: string; @@ -9872,12 +10989,12 @@ type PhoneCallerCallLogCompletedEvent = Event<"phone.caller_call_log_completed"> has_voicemail: boolean; call_id: string; client_code?: string; - call_type: string; + call_type: "voip" | "pstn" | "tollfree" | "international" | "contactCenter"; call_end_time?: string; - direction?: string; + direction?: "inbound" | "outbound"; forwarded_to?: { extension_number?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup" | "pstn"; location?: string; name?: string; number_type?: number; @@ -9885,7 +11002,7 @@ type PhoneCallerCallLogCompletedEvent = Event<"phone.caller_call_log_completed"> }; forwarded_by?: { extension_number?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; location?: string; name?: string; number_type?: number; @@ -9899,7 +11016,7 @@ type PhoneCallerCallLogCompletedEvent = Event<"phone.caller_call_log_completed"> }; }; type PhoneRecordingCompletedForAccessMemberEvent = Event<"phone.recording_completed_for_access_member"> & { - event: string; + event: "phone.recording_completed_for_access_member"; event_ts: number; payload: { account_id: string; @@ -9907,11 +11024,11 @@ type PhoneRecordingCompletedForAccessMemberEvent = Event<"phone.recording_comple recordings: { id: string; caller_number: string; - caller_number_type: number; + caller_number_type: 1 | 2; caller_name?: string; caller_did_number?: string; callee_number: string; - callee_number_type: number; + callee_number_type: 1 | 2; callee_name: string; callee_did_number?: string; duration: number; @@ -9920,6 +11037,7 @@ type PhoneRecordingCompletedForAccessMemberEvent = Event<"phone.recording_comple user_id?: string; call_id?: string; call_log_id?: string; + call_history_id?: string; end_time?: string; recording_type?: string; site?: { @@ -9932,7 +11050,7 @@ type PhoneRecordingCompletedForAccessMemberEvent = Event<"phone.recording_comple extension_number?: number; has_access_permission?: boolean; }; - direction: string; + direction: "inbound" | "outbound"; outgoing_by?: { name?: string; extension_number?: string; @@ -9954,12 +11072,12 @@ type PhoneRecordingResumedEvent = Event<"phone.recording_resumed"> & { user_id: string; caller_number: string; callee_number: string; - direction: string; + direction: "inbound" | "outbound"; date_time: string; - recording_type: string; + recording_type: "OnDemand" | "Automatic"; call_id: string; owner: { - type: string; + type: "user" | "callQueue" | "commonArea"; id: string; name: string; extension_number: number; @@ -9969,7 +11087,7 @@ type PhoneRecordingResumedEvent = Event<"phone.recording_resumed"> & { event_ts: number; }; type PhoneRecordingTranscriptCompletedEvent = Event<"phone.recording_transcript_completed"> & { - event: string; + event: "phone.recording_transcript_completed"; event_ts: number; payload: { account_id: string; @@ -9977,10 +11095,10 @@ type PhoneRecordingTranscriptCompletedEvent = Event<"phone.recording_transcript_ recordings: { id: string; caller_number: string; - caller_number_type: number; + caller_number_type: 1 | 2; caller_name?: string; callee_number: string; - callee_number_type: number; + callee_number_type: 1 | 2; callee_name: string; duration?: number; transcript_download_url: string; @@ -9999,7 +11117,7 @@ type PhoneRecordingTranscriptCompletedEvent = Event<"phone.recording_transcript_ name: string; extension_number?: number; }; - direction: string; + direction: "inbound" | "outbound"; outgoing_by?: { name?: string; extension_number?: string; @@ -10013,7 +11131,7 @@ type PhoneRecordingTranscriptCompletedEvent = Event<"phone.recording_transcript_ }; }; type PhoneCallLogPermanentlyDeletedEvent = Event<"phone.call_log_permanently_deleted"> & { - event: string; + event: "phone.call_log_permanently_deleted"; event_ts: number; payload: { account_id: string; @@ -10027,7 +11145,7 @@ type PhoneCallLogPermanentlyDeletedEvent = Event<"phone.call_log_permanently_del }; }; type PhoneTransferCallToVoicemailInitiatedEvent = Event<"phone.transfer_call_to_voicemail_initiated"> & { - event: string; + event: "phone.transfer_call_to_voicemail_initiated"; event_ts: number; payload: { account_id: string; @@ -10039,7 +11157,7 @@ type PhoneTransferCallToVoicemailInitiatedEvent = Event<"phone.transfer_call_to_ extension_number?: number; id?: string; name?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "commonAreaPhone" | "sharedLineGroup"; }; date_time: string; }; @@ -10054,22 +11172,22 @@ type PhoneCalleeMissedEvent = Event<"phone.callee_missed"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; ringing_start_time: string; call_end_time: string; @@ -10092,7 +11210,7 @@ type PhoneCallerRingingEvent = Event<"phone.caller_ringing"> & { call_id: string; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; name?: string; phone_number: string; @@ -10101,22 +11219,22 @@ type PhoneCallerRingingEvent = Event<"phone.caller_ringing"> & { device_type?: string; device_name?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; name?: string; phone_number?: string; extension_number?: number; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; ringing_start_time: string; }; }; }; type PhoneVoicemailReceivedEvent = Event<"phone.voicemail_received"> & { - event: string; + event: "phone.voicemail_received"; event_ts: number; payload: { account_id: string; @@ -10127,23 +11245,24 @@ type PhoneVoicemailReceivedEvent = Event<"phone.voicemail_received"> & { duration: number; caller_user_id?: string; caller_number: string; - caller_number_type: number; + caller_number_type: 1 | 2; caller_name: string; caller_did_number?: string; callee_user_id?: string; callee_number: string; - callee_number_type: number; + callee_number_type: 1 | 2; callee_name: string; callee_did_number?: string; - callee_extension_type: string; + callee_extension_type: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; callee_id: string; call_log_id?: string; + call_history_id?: string; call_id?: string; }; }; }; type PhoneSmsSentEvent = Event<"phone.sms_sent"> & { - event: string; + event: "phone.sms_sent"; event_ts: number; payload: { account_id: string; @@ -10152,17 +11271,17 @@ type PhoneSmsSentEvent = Event<"phone.sms_sent"> & { sender: { phone_number: string; id?: string; - type?: string; + type?: "user"; display_name?: string; }; to_members: { id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist"; display_name?: string; phone_number: string; }[]; owner: { - type?: string; + type?: "user" | "callQueue" | "autoReceptionist"; id?: string; sms_sender_user_id?: string; }; @@ -10178,11 +11297,16 @@ type PhoneSmsSentEvent = Event<"phone.sms_sent"> & { message_id: string; message_type: number; date_time: string; + phone_number_campaign_opt_statuses?: { + consumer_phone_number: string; + zoom_phone_user_number: string; + opt_status: "pending" | "opt_out" | "opt_in"; + }[]; }; }; }; type PhoneVoicemailDeletedEvent = Event<"phone.voicemail_deleted"> & { - event: string; + event: "phone.voicemail_deleted"; event_ts: number; payload: { account_id: string; @@ -10194,7 +11318,7 @@ type PhoneVoicemailDeletedEvent = Event<"phone.voicemail_deleted"> & { }; }; type PhoneVoicemailTranscriptCompletedEvent = Event<"phone.voicemail_transcript_completed"> & { - event: string; + event: "phone.voicemail_transcript_completed"; event_ts: number; payload: { account_id: string; @@ -10202,25 +11326,26 @@ type PhoneVoicemailTranscriptCompletedEvent = Event<"phone.voicemail_transcript_ id: string; date_time: string; caller_number: string; - caller_number_type: number; + caller_number_type: 1 | 2; caller_name: string; callee_user_id?: string; callee_number: string; - callee_number_type: number; + callee_number_type: 1 | 2; callee_name: string; - callee_extension_type?: string; + callee_extension_type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; callee_id?: string; call_log_id?: string; + call_history_id?: string; call_id?: string; transcription: { - status: number; + status: 0 | 1 | 2 | 4 | 5 | 9 | 11 | 12 | 13 | 14 | 409 | 415 | 422 | 500 | 601 | 602 | 603 | 999; content: string; }; }; }; }; type PhoneRecordingPermanentlyDeletedEvent = Event<"phone.recording_permanently_deleted"> & { - event: string; + event: "phone.recording_permanently_deleted"; event_ts: number; payload: { account_id: string; @@ -10252,7 +11377,7 @@ type PhonePeeringNumberEmergencyAddressUpdatedEvent = Event<"phone.peering_numbe }; }; type PhoneSmsCampaignNumberOptOutEvent = Event<"phone.sms_campaign_number_opt_out"> & { - event: string; + event: "phone.sms_campaign_number_opt_out"; event_ts: number; payload: { account_id: string; @@ -10274,22 +11399,22 @@ type PhoneCallerEndedEvent = Event<"phone.caller_ended"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; ringing_start_time: string; answer_start_time?: string; @@ -10307,7 +11432,7 @@ type PhoneCalleeEndedEvent = Event<"phone.callee_ended"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; name?: string; phone_number: string; @@ -10315,16 +11440,16 @@ type PhoneCalleeEndedEvent = Event<"phone.callee_ended"> & { timezone?: string; device_name?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number?: string; user_id?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; ringing_start_time: string; answer_start_time?: string; @@ -10344,7 +11469,7 @@ type PhoneCalleeEndedEvent = Event<"phone.callee_ended"> & { }; }; type PhoneCalleeCallHistoryCompletedEvent = Event<"phone.callee_call_history_completed"> & { - event: string; + event: "phone.callee_call_history_completed"; event_ts: number; payload: { account_id: string; @@ -10352,19 +11477,22 @@ type PhoneCalleeCallHistoryCompletedEvent = Event<"phone.callee_call_history_com user_id: string; call_logs: { id: string; + call_path_id: string; call_id: string; group_id?: string; - connect_type?: string; - call_type?: string; - direction?: string; + connect_type?: "internal" | "external"; + call_type?: "general" | "emergency"; + direction?: "inbound" | "outbound"; + hide_caller_id?: boolean; + end_to_end?: boolean; caller_ext_id?: string; caller_name?: string; caller_email?: string; caller_employee_id?: string; caller_did_number?: string; caller_ext_number?: string; - caller_ext_type?: string; - caller_number_type?: string; + caller_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + caller_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number"; caller_device_private_ip?: string; caller_device_public_ip?: string; caller_device_type?: string; @@ -10379,8 +11507,8 @@ type PhoneCalleeCallHistoryCompletedEvent = Event<"phone.callee_call_history_com callee_ext_number?: string; callee_email?: string; callee_employee_id?: string; - callee_ext_type?: string; - callee_number_type?: string; + callee_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + callee_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number"; callee_device_private_ip?: string; callee_device_public_ip?: string; callee_device_type?: string; @@ -10392,15 +11520,15 @@ type PhoneCalleeCallHistoryCompletedEvent = Event<"phone.callee_call_history_com start_time: string; answer_time?: string; end_time?: string; - event?: string; - result: string; - result_reason?: string; + event?: "incoming" | "transfer_from_zoom_contact_center" | "shared_line_incoming" | "outgoing" | "call_me_on" | "outgoing_to_zoom_contact_center" | "warm_transfer" | "forward" | "ring_to_member" | "overflow" | "direct_transfer" | "barge" | "monitor" | "whisper" | "listen" | "takeover" | "conference_barge" | "park" | "timeout" | "park_pick_up" | "merge" | "shared"; + result: "answered" | "accepted" | "picked_up" | "connected" | "succeeded" | "voicemail" | "hang_up" | "canceled" | "call_failed" | "unconnected" | "rejected" | "busy" | "ring_timeout" | "overflowed" | "no_answer" | "invalid_key" | "invalid_operation" | "abandoned" | "system_blocked" | "service_unavailable"; + result_reason?: "answered_by_other" | "pickup_by_other" | "call_out_by_other"; operator_ext_number?: string; operator_ext_id?: string; - operator_ext_type?: string; + operator_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; operator_name?: string; recording_id?: string; - recording_type?: string; + recording_type?: "ad-hoc" | "automatic"; voicemail_id?: string; talk_time?: number; hold_time?: number; @@ -10410,7 +11538,7 @@ type PhoneCalleeCallHistoryCompletedEvent = Event<"phone.callee_call_history_com }; }; type PhoneVoicemailPermanentlyDeletedEvent = Event<"phone.voicemail_permanently_deleted"> & { - event: string; + event: "phone.voicemail_permanently_deleted"; event_ts: number; payload: { account_id: string; @@ -10422,7 +11550,7 @@ type PhoneVoicemailPermanentlyDeletedEvent = Event<"phone.voicemail_permanently_ }; }; type PhoneSmsCampaignNumberOptInEvent = Event<"phone.sms_campaign_number_opt_in"> & { - event: string; + event: "phone.sms_campaign_number_opt_in"; event_ts: number; payload: { account_id: string; @@ -10444,29 +11572,42 @@ type PhoneCalleeMuteEvent = Event<"phone.callee_mute"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter"; user_id?: string; phone_number: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; date_time: string; }; }; }; +type PhoneSmsEtiquetteWarnEvent = Event<"phone.sms_etiquette_warn"> & { + event: "phone.sms_etiquette_warn"; + event_ts: number; + payload: { + account_id: string; + object: { + email: string; + message: string; + policy_name: string; + date_time: string; + }; + }; +}; type PhoneCallHistoryDeletedEvent = Event<"phone.call_history_deleted"> & { - event: string; + event: "phone.call_history_deleted"; event_ts: number; payload: { account_id: string; @@ -10479,8 +11620,21 @@ type PhoneCallHistoryDeletedEvent = Event<"phone.call_history_deleted"> & { }; }; }; +type PhoneSmsEtiquetteBlockEvent = Event<"phone.sms_etiquette_block"> & { + event: "phone.sms_etiquette_block"; + event_ts: number; + payload: { + account_id: string; + object: { + email: string; + message: string; + policy_name: string; + date_time: string; + }; + }; +}; type PhoneCallLogDeletedEvent = Event<"phone.call_log_deleted"> & { - event: string; + event: "phone.call_log_deleted"; event_ts: number; payload: { account_id: string; @@ -10502,22 +11656,22 @@ type PhoneCallerHoldEvent = Event<"phone.caller_hold"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number?: string; user_id?: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; date_time: string; }; @@ -10532,7 +11686,7 @@ type PhoneCallerConnectedEvent = Event<"phone.caller_connected"> & { call_id: string; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; name?: string; phone_number: string; @@ -10540,15 +11694,15 @@ type PhoneCallerConnectedEvent = Event<"phone.caller_connected"> & { timezone?: string; device_type?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; name?: string; phone_number?: string; extension_number?: number; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; ringing_start_time: string; connected_start_time: string; @@ -10556,7 +11710,7 @@ type PhoneCallerConnectedEvent = Event<"phone.caller_connected"> & { }; }; type PhoneRecordingCompletedEvent = Event<"phone.recording_completed"> & { - event: string; + event: "phone.recording_completed"; event_ts: number; payload: { account_id: string; @@ -10564,11 +11718,11 @@ type PhoneRecordingCompletedEvent = Event<"phone.recording_completed"> & { recordings: { id: string; caller_number: string; - caller_number_type: number; + caller_number_type: 1 | 2; caller_name?: string; caller_did_number?: string; callee_number: string; - callee_number_type: number; + callee_number_type: 1 | 2; callee_name: string; callee_did_number?: string; duration: number; @@ -10577,6 +11731,7 @@ type PhoneRecordingCompletedEvent = Event<"phone.recording_completed"> & { user_id?: string; call_id?: string; call_log_id?: string; + call_history_id?: string; end_time?: string; recording_type?: string; site?: { @@ -10589,7 +11744,7 @@ type PhoneRecordingCompletedEvent = Event<"phone.recording_completed"> & { extension_number?: number; has_access_permission?: boolean; }; - direction: string; + direction: "inbound" | "outbound"; outgoing_by?: { name?: string; extension_number?: string; @@ -10611,14 +11766,14 @@ type PhoneRecordingStartedEvent = Event<"phone.recording_started"> & { user_id: string; caller_number: string; callee_number: string; - direction: string; + direction: "inbound" | "outbound"; date_time: string; - recording_type: string; + recording_type: "OnDemand" | "Automatic"; call_id: string; channel_id: string; sip_id: string; owner: { - type: string; + type: "user" | "callQueue" | "commonArea"; id: string; name: string; extension_number?: number; @@ -10628,7 +11783,7 @@ type PhoneRecordingStartedEvent = Event<"phone.recording_started"> & { event_ts: number; }; type PhoneSmsSentFailedEvent = Event<"phone.sms_sent_failed"> & { - event: string; + event: "phone.sms_sent_failed"; event_ts: number; payload: { account_id: string; @@ -10637,7 +11792,7 @@ type PhoneSmsSentFailedEvent = Event<"phone.sms_sent_failed"> & { sender: { phone_number: string; id?: string; - type?: string; + type?: "user"; display_name?: string; }; to_members: { @@ -10647,7 +11802,7 @@ type PhoneSmsSentFailedEvent = Event<"phone.sms_sent_failed"> & { is_message_owner?: boolean; }[]; owner: { - type?: string; + type?: "user" | "callQueue" | "autoReceptionist"; id?: string; sms_sender_user_id?: string; }; @@ -10663,11 +11818,16 @@ type PhoneSmsSentFailedEvent = Event<"phone.sms_sent_failed"> & { message_id: string; message_type: number; date_time: string; + phone_number_campaign_opt_statuses?: { + consumer_phone_number: string; + zoom_phone_user_number: string; + opt_status: "pending" | "opt_out" | "opt_in"; + }[]; }; }; }; type PhoneCalleeCallLogCompletedEvent = Event<"phone.callee_call_log_completed"> & { - event: string; + event: "phone.callee_call_log_completed"; event_ts: number; payload: { account_id: string; @@ -10677,8 +11837,8 @@ type PhoneCalleeCallLogCompletedEvent = Event<"phone.callee_call_log_completed"> id: string; caller_user_id?: string; caller_number: string; - caller_number_type: number; - caller_number_source?: string; + caller_number_type: 1 | 2; + caller_number_source?: "internal" | "external" | "byop"; caller_name?: string; caller_location?: string; caller_did_number?: string; @@ -10686,8 +11846,8 @@ type PhoneCalleeCallLogCompletedEvent = Event<"phone.callee_call_log_completed"> caller_country_iso_code?: string; callee_user_id?: string; callee_number: string; - callee_number_type: number; - callee_number_source?: string; + callee_number_type: 1 | 2; + callee_number_source?: "internal" | "external" | "byop"; callee_name?: string; callee_location?: string; callee_did_number?: string; @@ -10706,14 +11866,14 @@ type PhoneCalleeCallLogCompletedEvent = Event<"phone.callee_call_log_completed"> has_voicemail: boolean; call_id: string; client_code?: string; - call_type?: string; + call_type?: "voip" | "pstn" | "tollfree" | "international" | "contactCenter"; call_end_time?: string; - direction?: string; + direction?: "inbound" | "outbound"; answer_start_time?: string; waiting_time?: number; forwarded_to?: { extension_number?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; location?: string; name?: string; number_type?: number; @@ -10721,7 +11881,7 @@ type PhoneCalleeCallLogCompletedEvent = Event<"phone.callee_call_log_completed"> }; forwarded_by?: { extension_number?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; location?: string; name?: string; number_type?: number; @@ -10743,7 +11903,7 @@ type PhoneCalleeRingingEvent = Event<"phone.callee_ringing"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; name?: string; phone_number: string; @@ -10752,15 +11912,15 @@ type PhoneCalleeRingingEvent = Event<"phone.callee_ringing"> & { device_type?: string; device_name?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; name?: string; phone_number?: string; extension_number?: number; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; ringing_start_time: string; forwarded_by?: { @@ -10786,22 +11946,22 @@ type PhoneCallerUnholdEvent = Event<"phone.caller_unhold"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number?: string; user_id?: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; date_time: string; }; @@ -10816,22 +11976,22 @@ type PhoneCalleeHoldEvent = Event<"phone.callee_hold"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; date_time: string; }; @@ -10846,7 +12006,7 @@ type PhoneCalleeAnsweredEvent = Event<"phone.callee_answered"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; name?: string; phone_number: string; @@ -10855,15 +12015,15 @@ type PhoneCalleeAnsweredEvent = Event<"phone.callee_answered"> & { device_type?: string; device_name?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; name?: string; phone_number?: string; extension_number?: number; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; ringing_start_time: string; answer_start_time: string; @@ -10890,22 +12050,22 @@ type PhoneCallerUnmuteEvent = Event<"phone.caller_unmute"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; date_time: string; }; @@ -10936,14 +12096,14 @@ type PhoneBlindTransferInitiatedEvent = Event<"phone.blind_transfer_initiated"> extension_number?: number; id?: string; name?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "commonAreaPhone" | "sharedLineGroup"; }; date_time: string; }; }; }; type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> & { - event: string; + event: "phone.account_settings_updated"; event_ts: number; payload: { account_id: string; @@ -10953,7 +12113,7 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> call_live_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; transcription_start_prompt?: { enable?: boolean; audio_id?: string; @@ -10963,30 +12123,30 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> local_survivability_mode?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; external_calling_on_zoom_room_common_area?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; select_outbound_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; allow_hide_outbound_caller_id?: boolean; }; personal_audio_library?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; allow_music_on_hold_customization?: boolean; allow_voicemail_and_message_greeting_customization?: boolean; }; voicemail?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; allow_videomail?: boolean; allow_download?: boolean; allow_delete?: boolean; @@ -10996,12 +12156,12 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> voicemail_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; include_voicemail_file?: boolean; include_voicemail_transcription?: boolean; forward_voicemail_to_email?: boolean; @@ -11009,12 +12169,12 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> shared_voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; restricted_call_hours?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; time_zone?: { id?: string; name?: string; @@ -11026,20 +12186,20 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> allowed_call_locations?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; locations_applied?: boolean; allow_internal_calls?: boolean; }; check_voicemails_over_phone?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; auto_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; - recording_calls?: string; + locked_by?: "invalid" | "account"; + recording_calls?: "inbound" | "outbound" | "both"; recording_transcription?: boolean; recording_start_prompt?: boolean; recording_start_prompt_audio_id?: string; @@ -11048,15 +12208,15 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> disconnect_on_recording_failure?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_member?: string; - play_beep_volume?: number; - play_beep_time_interval?: number; + play_beep_member?: "allMembers" | "recordingUser"; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; }; }; ad_hoc_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; recording_transcription?: boolean; allow_download?: boolean; allow_delete?: boolean; @@ -11064,138 +12224,138 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> recording_explicit_consent?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_member?: string; - play_beep_volume?: number; - play_beep_time_interval?: number; + play_beep_member?: "allMembers" | "recordingUser"; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; }; }; international_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; outbound_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; outbound_sms?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; sms?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; international_sms?: boolean; }; sms_etiquette_tool?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; sms_etiquette_policy?: { id?: string; name?: string; description?: string; - rule?: number; + rule?: 1 | 2; content?: string; - action?: number; + action?: 1 | 2; active?: boolean; }[]; }; zoom_phone_on_mobile?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; allow_calling_sms_mms?: boolean; }; zoom_phone_on_pwa?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; e2e_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; call_handling_forwarding_to_other_users?: { enable?: boolean; locked?: boolean; - locked_by?: string; - call_forwarding_type?: number; + locked_by?: "invalid" | "account"; + call_forwarding_type?: 1 | 2 | 3 | 4; }; call_overflow?: { enable?: boolean; locked?: boolean; - locked_by?: string; - call_overflow_type?: number; + locked_by?: "invalid" | "account"; + call_overflow_type?: 1 | 2 | 3 | 4; }; call_transferring?: { enable?: boolean; locked?: boolean; - locked_by?: string; - call_transferring_type?: number; + locked_by?: "invalid" | "account"; + call_transferring_type?: 1 | 2 | 3 | 4; }; elevate_to_meeting?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; call_park?: { enable?: boolean; locked?: boolean; - locked_by?: string; - expiration_period?: number; + locked_by?: "invalid" | "account"; + expiration_period?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; call_not_picked_up_action?: number; forward_to?: { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "zoomRoom" | "commonArea" | "ciscoRoom/polycomRoom" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; id?: string; }; - sequence?: number; + sequence?: 0 | 1; }; hand_off_to_room?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; mobile_switch_to_carrier?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; delegation?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; audio_intercom?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; block_calls_without_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; block_external_calls?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; block_business_hours?: boolean; block_closed_hours?: boolean; block_holiday_hours?: boolean; - block_call_action?: number; + block_call_action?: 0 | 9; }; call_queue_opt_out_reason?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; call_queue_opt_out_reasons_list?: { code?: string; system?: boolean; @@ -11205,42 +12365,42 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> auto_delete_data_after_retention_duration?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; items?: { - type?: string; + type?: "callLog" | "onDemandRecording" | "automaticRecording" | "voicemail" | "videomail" | "sms"; duration?: number; - time_unit?: string; + time_unit?: "year" | "month" | "day"; }[]; - delete_type?: number; + delete_type?: 1 | 2; }; auto_call_from_third_party_apps?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; override_default_port?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; min_port?: number; max_port?: number; }; peer_to_peer_media?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; advanced_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; disable_incoming_unencrypted_voicemail?: boolean; }; display_call_feedback_survey?: { enable?: boolean; locked?: boolean; - locked_by?: string; - feedback_type?: number; + locked_by?: "invalid" | "account"; + feedback_type?: 1 | 2; feedback_mos?: { enable?: boolean; min?: string; @@ -11260,7 +12420,7 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> call_live_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; transcription_start_prompt?: { enable?: boolean; audio_id?: string; @@ -11270,30 +12430,30 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> local_survivability_mode?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; external_calling_on_zoom_room_common_area?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; select_outbound_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; allow_hide_outbound_caller_id?: boolean; }; personal_audio_library?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; allow_music_on_hold_customization?: boolean; allow_voicemail_and_message_greeting_customization?: boolean; }; voicemail?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; allow_videomail?: boolean; allow_download?: boolean; allow_delete?: boolean; @@ -11303,12 +12463,12 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> voicemail_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; include_voicemail_file?: boolean; include_voicemail_transcription?: boolean; forward_voicemail_to_email?: boolean; @@ -11316,12 +12476,12 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> shared_voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; restricted_call_hours?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; time_zone?: { id?: string; name?: string; @@ -11333,20 +12493,20 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> allowed_call_locations?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; locations_applied?: boolean; allow_internal_calls?: boolean; }; check_voicemails_over_phone?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; auto_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; - recording_calls?: string; + locked_by?: "invalid" | "account"; + recording_calls?: "inbound" | "outbound" | "both"; recording_transcription?: boolean; recording_start_prompt?: boolean; recording_start_prompt_audio_id?: string; @@ -11355,15 +12515,15 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> disconnect_on_recording_failure?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_member?: string; - play_beep_volume?: number; - play_beep_time_interval?: number; + play_beep_member?: "allMembers" | "recordingUser"; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; }; }; ad_hoc_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; recording_transcription?: boolean; allow_download?: boolean; allow_delete?: boolean; @@ -11371,138 +12531,138 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> recording_explicit_consent?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_member?: string; - play_beep_volume?: number; - play_beep_time_interval?: number; + play_beep_member?: "allMembers" | "recordingUser"; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; }; }; international_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; outbound_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; outbound_sms?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; sms?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; international_sms?: boolean; }; sms_etiquette_tool?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; sms_etiquette_policy?: { id?: string; name?: string; description?: string; - rule?: number; + rule?: 1 | 2; content?: string; - action?: number; + action?: 1 | 2; active?: boolean; }[]; }; zoom_phone_on_mobile?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; allow_calling_sms_mms?: boolean; }; zoom_phone_on_pwa?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; e2e_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; call_handling_forwarding_to_other_users?: { enable?: boolean; locked?: boolean; - locked_by?: string; - call_forwarding_type?: number; + locked_by?: "invalid" | "account"; + call_forwarding_type?: 1 | 2 | 3 | 4; }; call_overflow?: { enable?: boolean; locked?: boolean; - locked_by?: string; - call_overflow_type?: number; + locked_by?: "invalid" | "account"; + call_overflow_type?: 1 | 2 | 3 | 4; }; call_transferring?: { enable?: boolean; locked?: boolean; - locked_by?: string; - call_transferring_type?: number; + locked_by?: "invalid" | "account"; + call_transferring_type?: 1 | 2 | 3 | 4; }; elevate_to_meeting?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; call_park?: { enable?: boolean; locked?: boolean; - locked_by?: string; - expiration_period?: number; + locked_by?: "invalid" | "account"; + expiration_period?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; call_not_picked_up_action?: number; forward_to?: { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "zoomRoom" | "commonArea" | "ciscoRoom/polycomRoom" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; id?: string; }; - sequence?: number; + sequence?: 0 | 1; }; hand_off_to_room?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; mobile_switch_to_carrier?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; delegation?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; audio_intercom?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; block_calls_without_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; block_external_calls?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; block_business_hours?: boolean; block_closed_hours?: boolean; block_holiday_hours?: boolean; - block_call_action?: number; + block_call_action?: 0 | 9; }; call_queue_opt_out_reason?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; call_queue_opt_out_reasons_list?: { code?: string; system?: boolean; @@ -11512,42 +12672,42 @@ type PhoneAccountSettingsUpdatedEvent = Event<"phone.account_settings_updated"> auto_delete_data_after_retention_duration?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; items?: { - type?: string; + type?: "callLog" | "onDemandRecording" | "automaticRecording" | "voicemail" | "videomail" | "sms"; duration?: number; - time_unit?: string; + time_unit?: "year" | "month" | "day"; }[]; - delete_type?: number; + delete_type?: 1 | 2; }; auto_call_from_third_party_apps?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; override_default_port?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; min_port?: number; max_port?: number; }; peer_to_peer_media?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; }; advanced_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account"; disable_incoming_unencrypted_voicemail?: boolean; }; display_call_feedback_survey?: { enable?: boolean; locked?: boolean; - locked_by?: string; - feedback_type?: number; + locked_by?: "invalid" | "account"; + feedback_type?: 1 | 2; feedback_mos?: { enable?: boolean; min?: string; @@ -11572,22 +12732,22 @@ type PhoneCalleeMeetingInvitingEvent = Event<"phone.callee_meeting_inviting"> & call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; meeting_id?: string; date_time: string; @@ -11603,22 +12763,22 @@ type PhoneCalleeParkedEvent = Event<"phone.callee_parked"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; park_code?: string; park_failure_reason?: string; @@ -11639,7 +12799,7 @@ type PhoneEmergencyAlertEvent = Event<"phone.emergency_alert"> & { extension_number?: string; timezone?: string; display_name?: string; - extension_type: string; + extension_type: "user" | "interop" | "commonAreaPhone"; site_id?: string; site_name?: string; }; @@ -11651,8 +12811,8 @@ type PhoneEmergencyAlertEvent = Event<"phone.emergency_alert"> & { ip?: string[]; bssid?: string[]; }; - router: string; - deliver_to: string; + router: "ZOOM" | "BYOC Carrier" | "Mobile Carrier"; + deliver_to: "PSAP" | "SAFETY_TEAM" | "NONE" | "BOTH" | "MOBILE_911_CALL" | "SIP_GROUP" | "BOTH_SAFETY_TEAM_AND_SIP" | "OVERFLOW_TO_SIP_GROUP" | "TO_PSAP_FOR_MISSED_BY_SAFETY_TEAM"; ringing_start_time: string; emergency_address?: { country?: string; @@ -11665,6 +12825,23 @@ type PhoneEmergencyAlertEvent = Event<"phone.emergency_alert"> & { }; }; }; +type PhoneAiCallSummaryChangedEvent = Event<"phone.ai_call_summary_changed"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + edited?: boolean; + deleted?: boolean; + ai_call_summary_id: string; + user_id: string; + call_id: string; + call_log_ids?: string[]; + created_time?: string; + modified_time?: string; + }; + }; +}; type PhoneCalleeRejectedEvent = Event<"phone.callee_rejected"> & { event: string; event_ts: number; @@ -11674,22 +12851,22 @@ type PhoneCalleeRejectedEvent = Event<"phone.callee_rejected"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; ringing_start_time: string; call_end_time: string; @@ -11698,7 +12875,7 @@ type PhoneCalleeRejectedEvent = Event<"phone.callee_rejected"> & { }; }; type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { - event: string; + event: "phone.group_settings_updated"; event_ts: number; payload: { account_id: string; @@ -11708,7 +12885,7 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { call_live_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; transcription_start_prompt?: { enable?: boolean; @@ -11719,20 +12896,20 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { local_survivability_mode?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; select_outbound_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_hide_outbound_caller_id?: boolean; }; personal_audio_library?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_music_on_hold_customization?: boolean; allow_voicemail_and_message_greeting_customization?: boolean; @@ -11740,7 +12917,7 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { voicemail?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_delete?: boolean; allow_download?: boolean; @@ -11751,7 +12928,7 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { voicemail_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; voicemail_notification_by_email?: { @@ -11760,19 +12937,19 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { forward_voicemail_to_email?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; shared_voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; restricted_call_hours?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; time_zone?: { id?: string; @@ -11785,7 +12962,7 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { allowed_call_locations?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; locations_applied?: boolean; allow_internal_calls?: boolean; @@ -11793,14 +12970,14 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { check_voicemails_over_phone?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; auto_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; - recording_calls?: string; + locked_by?: "invalid" | "account" | "user_group"; + recording_calls?: "inbound" | "outbound" | "both"; recording_transcription?: boolean; recording_start_prompt?: boolean; recording_start_prompt_audio_id?: string; @@ -11809,15 +12986,15 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { disconnect_on_recording_failure?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; }; ad_hoc_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; recording_transcription?: boolean; allow_download?: boolean; @@ -11826,22 +13003,22 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { recording_explicit_consent?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; }; zoom_phone_on_mobile?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_calling_sms_mms?: boolean; }; zoom_phone_on_pwa?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; sms_etiquette_tool?: { @@ -11851,145 +13028,145 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { id?: string; name?: string; description?: string; - rule?: number; + rule?: 1 | 2; content?: string; - action?: number; + action?: 1 | 2; active?: boolean; }[]; }; outbound_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; outbound_sms?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; international_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; sms?: { enable?: boolean; international_sms?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; e2e_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; call_handling_forwarding_to_other_users?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - call_forwarding_type?: number; + call_forwarding_type?: 1 | 2 | 3 | 4; }; call_overflow?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - call_overflow_type?: number; + call_overflow_type?: 1 | 2 | 3 | 4; }; call_transferring?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - call_transferring_type?: number; + call_transferring_type?: 1 | 2 | 3 | 4; }; elevate_to_meeting?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; call_park?: { enable?: boolean; - expiration_period?: number; + expiration_period?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; call_not_picked_up_action?: number; forward_to?: { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "zoomRoom" | "commonArea" | "ciscoRoom/polycomRoom" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; id?: string; }; - sequence?: number; + sequence?: 0 | 1; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; hand_off_to_room?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; mobile_switch_to_carrier?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; delegation?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; audio_intercom?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; block_calls_without_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; block_external_calls?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; block_business_hours?: boolean; block_closed_hours?: boolean; block_holiday_hours?: boolean; - block_call_action?: number; + block_call_action?: 0 | 9; }; peer_to_peer_media?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; advanced_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; disable_incoming_unencrypted_voicemail?: boolean; }; display_call_feedback_survey?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - feedback_type?: number; + feedback_type?: 1 | 2; feedback_mos?: { enable?: boolean; min?: string; @@ -12009,7 +13186,7 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { call_live_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; transcription_start_prompt?: { enable?: boolean; @@ -12020,20 +13197,20 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { local_survivability_mode?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; select_outbound_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_hide_outbound_caller_id?: boolean; }; personal_audio_library?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_music_on_hold_customization?: boolean; allow_voicemail_and_message_greeting_customization?: boolean; @@ -12041,7 +13218,7 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { voicemail?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_delete?: boolean; allow_download?: boolean; @@ -12052,7 +13229,7 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { voicemail_transcription?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; voicemail_notification_by_email?: { @@ -12061,19 +13238,19 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { forward_voicemail_to_email?: boolean; enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; shared_voicemail_notification_by_email?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; restricted_call_hours?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; time_zone?: { id?: string; @@ -12086,7 +13263,7 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { allowed_call_locations?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; locations_applied?: boolean; allow_internal_calls?: boolean; @@ -12094,14 +13271,14 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { check_voicemails_over_phone?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; auto_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; - recording_calls?: string; + locked_by?: "invalid" | "account" | "user_group"; + recording_calls?: "inbound" | "outbound" | "both"; recording_transcription?: boolean; recording_start_prompt?: boolean; recording_start_prompt_audio_id?: string; @@ -12110,15 +13287,15 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { disconnect_on_recording_failure?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; }; ad_hoc_call_recording?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "site"; modified?: boolean; recording_transcription?: boolean; allow_download?: boolean; @@ -12127,22 +13304,22 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { recording_explicit_consent?: boolean; play_recording_beep_tone?: { enable?: boolean; - play_beep_volume?: number; - play_beep_time_interval?: number; - play_beep_member?: string; + play_beep_volume?: 0 | 20 | 40 | 60 | 80 | 100; + play_beep_time_interval?: 5 | 10 | 15 | 20 | 25 | 30 | 60 | 120; + play_beep_member?: "allMember" | "recordingSide"; }; }; zoom_phone_on_mobile?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; allow_calling_sms_mms?: boolean; }; zoom_phone_on_pwa?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; sms_etiquette_tool?: { @@ -12152,145 +13329,145 @@ type PhoneGroupSettingsUpdatedEvent = Event<"phone.group_settings_updated"> & { id?: string; name?: string; description?: string; - rule?: number; + rule?: 1 | 2; content?: string; - action?: number; + action?: 1 | 2; active?: boolean; }[]; }; outbound_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; outbound_sms?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; international_calling?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; sms?: { enable?: boolean; international_sms?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; e2e_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; call_handling_forwarding_to_other_users?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - call_forwarding_type?: number; + call_forwarding_type?: 1 | 2 | 3 | 4; }; call_overflow?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - call_overflow_type?: number; + call_overflow_type?: 1 | 2 | 3 | 4; }; call_transferring?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - call_transferring_type?: number; + call_transferring_type?: 1 | 2 | 3 | 4; }; elevate_to_meeting?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; call_park?: { enable?: boolean; - expiration_period?: number; + expiration_period?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60; call_not_picked_up_action?: number; forward_to?: { display_name?: string; extension_id?: string; extension_number?: number; - extension_type?: string; + extension_type?: "user" | "zoomRoom" | "commonArea" | "ciscoRoom/polycomRoom" | "autoReceptionist" | "callQueue" | "sharedLineGroup"; id?: string; }; - sequence?: number; + sequence?: 0 | 1; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; hand_off_to_room?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; mobile_switch_to_carrier?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; delegation?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; audio_intercom?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; block_calls_without_caller_id?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; block_external_calls?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; block_business_hours?: boolean; block_closed_hours?: boolean; block_holiday_hours?: boolean; - block_call_action?: number; + block_call_action?: 0 | 9; }; peer_to_peer_media?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; }; advanced_encryption?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; disable_incoming_unencrypted_voicemail?: boolean; }; display_call_feedback_survey?: { enable?: boolean; locked?: boolean; - locked_by?: string; + locked_by?: "invalid" | "account" | "user_group"; modified?: boolean; - feedback_type?: number; + feedback_type?: 1 | 2; feedback_mos?: { enable?: boolean; min?: string; @@ -12315,22 +13492,22 @@ type PhoneCalleeUnmuteEvent = Event<"phone.callee_unmute"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; date_time: string; }; @@ -12345,14 +13522,14 @@ type PhoneRecordingStoppedEvent = Event<"phone.recording_stopped"> & { user_id: string; caller_number: string; callee_number: string; - direction: string; + direction: "inbound" | "outbound"; date_time: string; - recording_type: string; + recording_type: "OnDemand" | "Automatic"; call_id: string; channel_id: string; sip_id: string; owner: { - type: string; + type: "user" | "callQueue" | "commonArea"; id: string; name: string; extension_number?: number; @@ -12370,22 +13547,22 @@ type PhoneCallerMeetingInvitingEvent = Event<"phone.caller_meeting_inviting"> & call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; meeting_id?: string; date_time: string; @@ -12393,7 +13570,7 @@ type PhoneCallerMeetingInvitingEvent = Event<"phone.caller_meeting_inviting"> & }; }; type PhoneVoicemailReceivedForAccessMemberEvent = Event<"phone.voicemail_received_for_access_member"> & { - event: string; + event: "phone.voicemail_received_for_access_member"; event_ts: number; payload: { account_id: string; @@ -12403,20 +13580,21 @@ type PhoneVoicemailReceivedForAccessMemberEvent = Event<"phone.voicemail_receive download_url: string; duration: number; caller_number: string; - caller_number_type: number; + caller_number_type: 1 | 2; caller_name: string; caller_did_number?: string; callee_user_id?: string; callee_number: string; - callee_number_type: number; + callee_number_type: 1 | 2; callee_name: string; callee_did_number?: string; - callee_extension_type: string; + callee_extension_type: "user" | "callQueue" | "autoReceptionist" | "sharedLineGroup"; callee_id: string; call_log_id?: string; + call_history_id?: string; call_id?: string; access_member_id?: string; - access_member_extension_type?: string; + access_member_extension_type?: "user" | "commonAreaPhone"; }; }; }; @@ -12429,22 +13607,22 @@ type PhoneCalleeUnholdEvent = Event<"phone.callee_unhold"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number?: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number?: string; user_id?: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; date_time: string; }; @@ -12464,7 +13642,7 @@ type PhoneConferenceStartedEvent = Event<"phone.conference_started"> & { extension_number?: number; id?: string; name?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist" | "commonAreaPhone" | "sharedLineGroup"; }; date_time: string; }; @@ -12485,7 +13663,7 @@ type PhoneGenericDeviceProvisionEvent = Event<"phone.generic_device_provision"> name: string; }; provision?: { - type: string; + type: "manual"; sip_accounts?: { sip_domain?: string; outbound_proxy: string; @@ -12516,12 +13694,12 @@ type PhoneRecordingPausedEvent = Event<"phone.recording_paused"> & { user_id: string; caller_number: string; callee_number: string; - direction: string; + direction: "inbound" | "outbound"; date_time: string; - recording_type: string; + recording_type: "OnDemand" | "Automatic"; call_id: string; owner: { - type: string; + type: "user" | "callQueue" | "commonArea"; id: string; name: string; extension_number?: number; @@ -12531,7 +13709,7 @@ type PhoneRecordingPausedEvent = Event<"phone.recording_paused"> & { event_ts: number; }; type PhoneSmsReceivedEvent = Event<"phone.sms_received"> & { - event: string; + event: "phone.sms_received"; event_ts: number; payload: { account_id: string; @@ -12539,18 +13717,18 @@ type PhoneSmsReceivedEvent = Event<"phone.sms_received"> & { sender: { phone_number: string; id?: string; - type?: string; + type?: "user" | "callQueue" | "autoReceptionist"; display_name?: string; }; to_members: { id?: string; - type?: string; + type?: "user"; display_name?: string; phone_number: string; is_message_owner?: boolean; }[]; owner: { - type?: string; + type?: "user" | "callQueue" | "autoReceptionist"; id?: string; team_id?: string; }; @@ -12566,6 +13744,11 @@ type PhoneSmsReceivedEvent = Event<"phone.sms_received"> & { message_id: string; message_type: number; date_time: string; + phone_number_campaign_opt_statuses?: { + consumer_phone_number: string; + zoom_phone_user_number: string; + opt_status: "pending" | "opt_out" | "opt_in"; + }[]; }; }; }; @@ -12578,14 +13761,14 @@ type PhoneRecordingFailedEvent = Event<"phone.recording_failed"> & { user_id: string; caller_number: string; callee_number: string; - direction: string; + direction: "inbound" | "outbound"; date_time: string; - recording_type: string; + recording_type: "OnDemand" | "Automatic"; call_id: string; channel_id: string; sip_id: string; owner: { - type: string; + type: "user" | "callQueue" | "commonArea"; id: string; name: string; extension_number?: number; @@ -12595,7 +13778,7 @@ type PhoneRecordingFailedEvent = Event<"phone.recording_failed"> & { event_ts: number; }; type PhoneCallerCallHistoryCompletedEvent = Event<"phone.caller_call_history_completed"> & { - event: string; + event: "phone.caller_call_history_completed"; event_ts: number; payload: { account_id: string; @@ -12603,19 +13786,22 @@ type PhoneCallerCallHistoryCompletedEvent = Event<"phone.caller_call_history_com user_id: string; call_logs: { id: string; + call_path_id: string; call_id: string; group_id?: string; - connect_type?: string; - call_type?: string; - direction?: string; + connect_type?: "internal" | "external"; + call_type?: "general" | "emergency"; + direction?: "inbound" | "outbound"; + hide_caller_id?: boolean; + end_to_end?: boolean; caller_ext_id?: string; caller_name?: string; caller_email?: string; caller_employee_id?: string; caller_did_number?: string; caller_ext_number?: string; - caller_ext_type?: string; - caller_number_type?: string; + caller_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + caller_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number"; caller_device_private_ip?: string; caller_device_public_ip?: string; caller_device_type?: string; @@ -12630,8 +13816,8 @@ type PhoneCallerCallHistoryCompletedEvent = Event<"phone.caller_call_history_com callee_ext_number?: string; callee_email?: string; callee_employee_id?: string; - callee_ext_type?: string; - callee_number_type?: string; + callee_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; + callee_number_type?: "zoom_pstn" | "zoom_toll_free_number" | "external_pstn" | "external_contact" | "byoc" | "byop" | "3rd_party_contact_center" | "zoom_service_number" | "external_service_number" | "zoom_contact_center" | "meeting_phone_number" | "meeting_id" | "anonymous_number"; callee_device_private_ip?: string; callee_device_public_ip?: string; callee_device_type?: string; @@ -12643,15 +13829,15 @@ type PhoneCallerCallHistoryCompletedEvent = Event<"phone.caller_call_history_com start_time: string; answer_time?: string; end_time?: string; - event?: string; - result: string; - result_reason?: string; + event?: "incoming" | "transfer_from_zoom_contact_center" | "shared_line_incoming" | "outgoing" | "call_me_on" | "outgoing_to_zoom_contact_center" | "warm_transfer" | "forward" | "ring_to_member" | "overflow" | "direct_transfer" | "barge" | "monitor" | "whisper" | "listen" | "takeover" | "conference_barge" | "park" | "timeout" | "park_pick_up" | "merge" | "shared"; + result: "answered" | "accepted" | "picked_up" | "connected" | "succeeded" | "voicemail" | "hang_up" | "canceled" | "call_failed" | "unconnected" | "rejected" | "busy" | "ring_timeout" | "overflowed" | "no_answer" | "invalid_key" | "invalid_operation" | "abandoned" | "system_blocked" | "service_unavailable"; + result_reason?: "answered_by_other" | "pickup_by_other" | "call_out_by_other"; operator_ext_number?: string; operator_ext_id?: string; - operator_ext_type?: string; + operator_ext_type?: "user" | "call_queue" | "auto_receptionist" | "common_area" | "zoom_room" | "cisco_room" | "shared_line_group" | "group_call_pickup" | "external_contact"; operator_name?: string; recording_id?: string; - recording_type?: string; + recording_type?: "ad-hoc" | "automatic"; voicemail_id?: string; talk_time?: number; hold_time?: number; @@ -12681,58 +13867,31 @@ type PhoneCallerMuteEvent = Event<"phone.caller_mute"> & { call_id: string; callee: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; user_id?: string; phone_number: string; extension_number?: number; timezone?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; caller: { extension_id?: string; - extension_type?: string; + extension_type?: "user" | "callQueue" | "autoReceptionist" | "commonArea" | "commonAreaPhone" | "sharedLineGroup" | "zoomRoom" | "ciscoRoom/PolycomRoom" | "contactCenter" | "pstn" | "five9" | "twilio"; phone_number: string; user_id?: string; extension_number?: number; timezone?: string; device_id?: string; - connection_type?: string; + connection_type?: "pstn_off_net" | "voip" | "pstn_on_net" | "contact_center" | "byop"; }; date_time: string; }; }; }; -type PhoneEvents = PhoneRecordingDeletedEvent | PhoneCallerCallLogCompletedEvent | PhoneRecordingCompletedForAccessMemberEvent | PhoneRecordingResumedEvent | PhoneRecordingTranscriptCompletedEvent | PhoneCallLogPermanentlyDeletedEvent | PhoneTransferCallToVoicemailInitiatedEvent | PhoneCalleeMissedEvent | PhoneCallerRingingEvent | PhoneVoicemailReceivedEvent | PhoneSmsSentEvent | PhoneVoicemailDeletedEvent | PhoneVoicemailTranscriptCompletedEvent | PhoneRecordingPermanentlyDeletedEvent | PhonePeeringNumberEmergencyAddressUpdatedEvent | PhoneSmsCampaignNumberOptOutEvent | PhoneCallerEndedEvent | PhoneCalleeEndedEvent | PhoneCalleeCallHistoryCompletedEvent | PhoneVoicemailPermanentlyDeletedEvent | PhoneSmsCampaignNumberOptInEvent | PhoneCalleeMuteEvent | PhoneCallHistoryDeletedEvent | PhoneCallLogDeletedEvent | PhoneCallerHoldEvent | PhoneCallerConnectedEvent | PhoneRecordingCompletedEvent | PhoneRecordingStartedEvent | PhoneSmsSentFailedEvent | PhoneCalleeCallLogCompletedEvent | PhoneCalleeRingingEvent | PhoneCallerUnholdEvent | PhoneCalleeHoldEvent | PhoneCalleeAnsweredEvent | PhoneCallerUnmuteEvent | PhoneDeviceRegistrationEvent | PhoneBlindTransferInitiatedEvent | PhoneAccountSettingsUpdatedEvent | PhoneCalleeMeetingInvitingEvent | PhoneCalleeParkedEvent | PhoneEmergencyAlertEvent | PhoneCalleeRejectedEvent | PhoneGroupSettingsUpdatedEvent | PhoneCalleeUnmuteEvent | PhoneRecordingStoppedEvent | PhoneCallerMeetingInvitingEvent | PhoneVoicemailReceivedForAccessMemberEvent | PhoneCalleeUnholdEvent | PhoneConferenceStartedEvent | PhoneGenericDeviceProvisionEvent | PhoneRecordingPausedEvent | PhoneSmsReceivedEvent | PhoneRecordingFailedEvent | PhoneCallerCallHistoryCompletedEvent | PhonePeeringNumberCnamUpdatedEvent | PhoneCallerMuteEvent; +type PhoneEvents = PhoneRecordingDeletedEvent | PhoneCallerCallLogCompletedEvent | PhoneRecordingCompletedForAccessMemberEvent | PhoneRecordingResumedEvent | PhoneRecordingTranscriptCompletedEvent | PhoneCallLogPermanentlyDeletedEvent | PhoneTransferCallToVoicemailInitiatedEvent | PhoneCalleeMissedEvent | PhoneCallerRingingEvent | PhoneVoicemailReceivedEvent | PhoneSmsSentEvent | PhoneVoicemailDeletedEvent | PhoneVoicemailTranscriptCompletedEvent | PhoneRecordingPermanentlyDeletedEvent | PhonePeeringNumberEmergencyAddressUpdatedEvent | PhoneSmsCampaignNumberOptOutEvent | PhoneCallerEndedEvent | PhoneCalleeEndedEvent | PhoneCalleeCallHistoryCompletedEvent | PhoneVoicemailPermanentlyDeletedEvent | PhoneSmsCampaignNumberOptInEvent | PhoneCalleeMuteEvent | PhoneSmsEtiquetteWarnEvent | PhoneCallHistoryDeletedEvent | PhoneSmsEtiquetteBlockEvent | PhoneCallLogDeletedEvent | PhoneCallerHoldEvent | PhoneCallerConnectedEvent | PhoneRecordingCompletedEvent | PhoneRecordingStartedEvent | PhoneSmsSentFailedEvent | PhoneCalleeCallLogCompletedEvent | PhoneCalleeRingingEvent | PhoneCallerUnholdEvent | PhoneCalleeHoldEvent | PhoneCalleeAnsweredEvent | PhoneCallerUnmuteEvent | PhoneDeviceRegistrationEvent | PhoneBlindTransferInitiatedEvent | PhoneAccountSettingsUpdatedEvent | PhoneCalleeMeetingInvitingEvent | PhoneCalleeParkedEvent | PhoneEmergencyAlertEvent | PhoneAiCallSummaryChangedEvent | PhoneCalleeRejectedEvent | PhoneGroupSettingsUpdatedEvent | PhoneCalleeUnmuteEvent | PhoneRecordingStoppedEvent | PhoneCallerMeetingInvitingEvent | PhoneVoicemailReceivedForAccessMemberEvent | PhoneCalleeUnholdEvent | PhoneConferenceStartedEvent | PhoneGenericDeviceProvisionEvent | PhoneRecordingPausedEvent | PhoneSmsReceivedEvent | PhoneRecordingFailedEvent | PhoneCallerCallHistoryCompletedEvent | PhonePeeringNumberCnamUpdatedEvent | PhoneCallerMuteEvent; declare class PhoneEventProcessor extends EventManager { } -/** - * Credentials for access token & refresh token, which are used to access Zoom's APIs. - * - * As access token is short-lived (usually a single hour), its expiration time is checked - * first. If it's possible to use the access token, it's used; however, if it has expired - * or is close to expiring, the refresh token should be used to generate a new access token - * before the API call is made. Refresh tokens are generally valid for 90 days. - * - * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} - * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. - * It's likely that this error will be rare, but it _can_ be thrown. - */ -interface OAuthToken { - accessToken: string; - expirationTimeIso: string; - refreshToken: string; - scopes: string[]; -} -declare class OAuth extends InteractiveAuth { - private assertResponseAccessToken; - private fetchAccessToken; - getToken(): Promise; - initRedirectCode(code: string): Promise; - private mapOAuthToken; - private refreshAccessToken; -} - type PhoneOptions = CommonClientOptions; declare class PhoneOAuthClient = PhoneOptions> extends ProductClient { protected initAuth({ clientId, clientSecret, tokenStore, ...restOptions }: OptionsType): OAuth; @@ -12747,4 +13906,5 @@ declare class PhoneS2SAuthClient typeof obj.installerOptions.redirectUri !== "undefined" && typeof obj.installerOptions.stateStore !== "undefined"; @@ -450,7 +453,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -460,14 +466,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -579,9 +591,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -595,6 +604,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? createServer : createServer$1; } @@ -609,8 +631,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -627,69 +661,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -723,18 +772,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -743,87 +798,13 @@ class HttpReceiver { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -852,10 +833,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -1026,9 +1020,14 @@ class PhoneEndpoints extends WebEndpoints { urlPathBuilder: ({ callLogId }) => `/phone/call_history/${callLogId}` }), addClientCodeToCallHistory: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ callLogId }) => `/phone/call_history/${callLogId}/client_code` }), + getCallHistoryDetail: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ callHistoryId }) => `/phone/call_history_detail/${callHistoryId}` }), getAccountsCallLogs: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/call_logs` }), getCallLogDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ callLogId }) => `/phone/call_logs/${callLogId}` }), addClientCodeToCallLog: this.buildEndpoint({ method: "PUT", urlPathBuilder: ({ callLogId }) => `/phone/call_logs/${callLogId}/client_code` }), + getUserAICallSummaryDetail: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ userId, aiCallSummaryId }) => `/phone/user/${userId}/ai_call_summary/${aiCallSummaryId}` + }), getUsersCallHistory: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/phone/users/${userId}/call_history` }), syncUsersCallHistory: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/phone/users/${userId}/call_history/sync` }), deleteUsersCallHistory: this.buildEndpoint({ @@ -1043,6 +1042,7 @@ class PhoneEndpoints extends WebEndpoints { }) }; callQueues = { + listCallQueueAnalytics: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/call_queue_analytics` }), listCallQueues: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/call_queues` }), createCallQueue: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/phone/call_queues` }), getCallQueueDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ callQueueId }) => `/phone/call_queues/${callQueueId}` }), @@ -1070,7 +1070,7 @@ class PhoneEndpoints extends WebEndpoints { method: "DELETE", urlPathBuilder: ({ callQueueId, phoneNumberId }) => `/phone/call_queues/${callQueueId}/phone_numbers/${phoneNumberId}` }), - addPolicySettingToCallQueue: this.buildEndpoint({ + addPolicySubsettingToCallQueue: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ callQueueId, policyType }) => `/phone/call_queues/${callQueueId}/policies/${policyType}` }), @@ -1102,6 +1102,7 @@ class PhoneEndpoints extends WebEndpoints { commonAreas = { listCommonAreas: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/common_areas` }), addCommonArea: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/phone/common_areas` }), + generateActivationCodesForCommonAreas: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/phone/common_areas/activation_code` }), listActivationCodes: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/common_areas/activation_codes` }), applyTemplateToCommonAreas: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ templateId }) => `/phone/common_areas/template_id/${templateId}` }), getCommonAreaDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ commonAreaId }) => `/phone/common_areas/${commonAreaId}` }), @@ -1122,7 +1123,7 @@ class PhoneEndpoints extends WebEndpoints { }), updateCommonAreaPinCode: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ commonAreaId }) => `/phone/common_areas/${commonAreaId}/pin_code` }), getCommonAreaSettings: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ commonAreaId }) => `/phone/common_areas/${commonAreaId}/settings` }), - addCommonAreaSettings: this.buildEndpoint({ + addCommonAreaSetting: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ commonAreaId, settingType }) => `/phone/common_areas/${commonAreaId}/settings/${settingType}` }), @@ -1130,7 +1131,7 @@ class PhoneEndpoints extends WebEndpoints { method: "DELETE", urlPathBuilder: ({ commonAreaId, settingType }) => `/phone/common_areas/${commonAreaId}/settings/${settingType}` }), - updateCommonAreaSettings: this.buildEndpoint({ + updateCommonAreaSetting: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ commonAreaId, settingType }) => `/phone/common_areas/${commonAreaId}/settings/${settingType}` }) @@ -1142,6 +1143,12 @@ class PhoneEndpoints extends WebEndpoints { urlPathBuilder: ({ callId }) => `/phone/metrics/call_logs/${callId}/qos` }), getCallDetailsFromCallLog: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ call_id }) => `/phone/metrics/call_logs/${call_id}` }), + listDefaultEmergencyAddressUsers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/default_emergency_address/users` }), + listDetectablePersonalLocationUsers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/detectable_personal_location/users` }), + listUsersPermissionForLocationSharing: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/location_sharing_permission/users` }), + listNomadicEmergencyServicesUsers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/nomadic_emergency_services/users` }), + listRealTimeLocationForIPPhones: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/realtime_location/devices` }), + listRealTimeLocationForUsers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/emergency_services/realtime_location/users` }), listTrackedLocations: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/location_tracking` }), listPastCallMetrics: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/metrics/past_calls` }) }; @@ -1216,6 +1223,14 @@ class PhoneEndpoints extends WebEndpoints { }) }; groups = { + getGroupPolicyDetails: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ groupId, policyType }) => `/phone/groups/${groupId}/policies/${policyType}` + }), + updateGroupPolicy: this.buildEndpoint({ + method: "PATCH", + urlPathBuilder: ({ groupId, policyType }) => `/phone/groups/${groupId}/policies/${policyType}` + }), getGroupPhoneSettings: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ groupId }) => `/phone/groups/${groupId}/settings` }) }; iVR = { @@ -1364,7 +1379,8 @@ class PhoneEndpoints extends WebEndpoints { rebootDeskPhone: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ deviceId }) => `/phone/devices/${deviceId}/reboot` - }) + }), + listSmartphones: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/smartphones` }) }; phoneNumbers = { addBYOCPhoneNumbers: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/phone/byoc_numbers` }), @@ -1403,7 +1419,10 @@ class PhoneEndpoints extends WebEndpoints { updatePhoneRole: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}` }), listMembersInRole: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/members` }), addMembersToRoles: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/members` }), - deleteMembersInRole: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/members` }) + deleteMembersInRole: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/members` }), + listPhoneRoleTargets: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/targets` }), + addPhoneRoleTargets: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/targets` }), + deletePhoneRoleTargets: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ roleId }) => `/phone/roles/${roleId}/targets` }) }; privateDirectory = { listPrivateDirectoryMembers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/private_directory/members` }), @@ -1454,6 +1473,10 @@ class PhoneEndpoints extends WebEndpoints { updateDirectoryBackupRoutingRule: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ routingRuleId }) => `/phone/routing_rules/${routingRuleId}` }) }; sMS = { + postSMSMessage: this.buildEndpoint({ + method: "POST", + urlPathBuilder: () => `/phone/sms/messages` + }), getAccountsSMSSessions: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/sms/sessions` }), getSMSSessionDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/phone/sms/sessions/${sessionId}` }), getSMSByMessageID: this.buildEndpoint({ @@ -1479,7 +1502,8 @@ class PhoneEndpoints extends WebEndpoints { unassignPhoneNumber: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ smsCampaignId, phoneNumberId }) => `/phone/sms_campaigns/${smsCampaignId}/phone_numbers/${phoneNumberId}` - }) + }), + listUsersOptStatusesOfPhoneNumbers: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/phone/user/${userId}/sms_campaigns/phone_numbers/opt_status` }) }; settingTemplates = { listSettingTemplates: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/setting_templates` }), @@ -1488,6 +1512,8 @@ class PhoneEndpoints extends WebEndpoints { updateSettingTemplate: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ templateId }) => `/phone/setting_templates/${templateId}` }) }; settings = { + getAccountPolicyDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ policyType }) => `/phone/policies/${policyType}` }), + updateAccountPolicy: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ policyType }) => `/phone/policies/${policyType}` }), listPortedNumbers: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/phone/ported_numbers/orders` }), getPortedNumberDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ orderId }) => `/phone/ported_numbers/orders/${orderId}` }), getPhoneAccountSettings: this.buildEndpoint({ @@ -1611,6 +1637,11 @@ class PhoneEndpoints extends WebEndpoints { method: "DELETE", urlPathBuilder: ({ userId }) => `/phone/users/${userId}/outbound_caller_id/customized_numbers` }), + getUserPolicyDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId, policyType }) => `/phone/users/${userId}/policies/${policyType}` }), + updateUserPolicy: this.buildEndpoint({ + method: "PATCH", + urlPathBuilder: ({ userId, policyType }) => `/phone/users/${userId}/policies/${policyType}` + }), getUsersProfileSettings: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ userId }) => `/phone/users/${userId}/settings` }), updateUsersProfileSettings: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ userId }) => `/phone/users/${userId}/settings` }), addUsersSharedAccessSetting: this.buildEndpoint({ @@ -1635,6 +1666,10 @@ class PhoneEndpoints extends WebEndpoints { urlPathBuilder: ({ fileId }) => `/phone/voice_mails/download/${fileId}` }), getVoicemailDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ voicemailId }) => `/phone/voice_mails/${voicemailId}` }), + deleteVoicemail: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ voicemailId }) => `/phone/voice_mails/${voicemailId}` + }), updateVoicemailReadStatus: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ voicemailId }) => `/phone/voice_mails/${voicemailId}` }) }; zoomRooms = { @@ -1648,10 +1683,7 @@ class PhoneEndpoints extends WebEndpoints { removeZoomRoomFromZPAccount: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ roomId }) => `/phone/rooms/${roomId}` }), updateZoomRoomUnderZoomPhoneLicense: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ roomId }) => `/phone/rooms/${roomId}` }), assignCallingPlansToZoomRoom: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ roomId }) => `/phone/rooms/${roomId}/calling_plans` }), - removeCallingPlanFromZoomRoom: this.buildEndpoint({ - method: "DELETE", - urlPathBuilder: ({ roomId, type }) => `/phone/rooms/${roomId}/calling_plans/${type.toString()}` - }), + removeCallingPlanFromZoomRoom: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ roomId, type }) => `/phone/rooms/${roomId}/calling_plans/${type}` }), assignPhoneNumbersToZoomRoom: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ roomId }) => `/phone/rooms/${roomId}/phone_numbers` }), removePhoneNumberFromZoomRoom: this.buildEndpoint({ method: "DELETE", @@ -1747,7 +1779,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -1768,7 +1802,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. diff --git a/teamchat/teamchat.cjs b/teamchat/teamchat.cjs index 4792e06..bb2b2fc 100644 --- a/teamchat/teamchat.cjs +++ b/teamchat/teamchat.cjs @@ -426,6 +426,9 @@ class JwtStateStore { const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; const hasInstallerOptions = (obj) => typeof obj.installerOptions.redirectUri !== "undefined" && typeof obj.installerOptions.stateStore !== "undefined"; @@ -452,7 +455,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -462,14 +468,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -581,9 +593,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -597,6 +606,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? node_https.createServer : node_http.createServer; } @@ -611,8 +633,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -629,69 +663,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, exports.StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, exports.StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -725,18 +774,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(exports.StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -829,7 +884,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -850,7 +907,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. @@ -858,87 +915,13 @@ class ProductClient { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -967,10 +950,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${node_path.basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -1033,7 +1029,32 @@ class WebEndpoints { } class TeamChatEndpoints extends WebEndpoints { + chatChannelMentionGroup = { + listChannelMentionGroups: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/mention_group` }), + createChannelMentionGroup: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/mention_group` }), + deleteChannelMentionGroup: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ channelId, mentionGroupId }) => `/chat/channels/${channelId}/mention_group/${mentionGroupId}` + }), + updateChannelMentionGroupInformation: this.buildEndpoint({ + method: "PATCH", + urlPathBuilder: ({ channelId, mentionGroupId }) => `/chat/channels/${channelId}/mention_group/${mentionGroupId}` + }), + listMembersOfMentionGroup: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ channelId, mentionGroupId }) => `/chat/channels/${channelId}/mention_group/${mentionGroupId}/members` + }), + addChannelMembersToMentionGroup: this.buildEndpoint({ + method: "POST", + urlPathBuilder: ({ channelId, mentionGroupId }) => `/chat/channels/${channelId}/mention_group/${mentionGroupId}/members` + }), + removeChannelMentionGroupMembers: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ channelId, mentionGroupId }) => `/chat/channels/${channelId}/mention_group/${mentionGroupId}/members` + }) + }; chatChannels = { + listChannelActivityLogs: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/activities/channels` }), performOperationsOnChannels: this.buildEndpoint({ method: "PATCH", urlPathBuilder: () => `/chat/channels/events` }), getChannel: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}` }), deleteChannel: this.buildEndpoint({ @@ -1066,6 +1087,7 @@ class TeamChatEndpoints extends WebEndpoints { batchDeleteChannels: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ userId }) => `/chat/users/${userId}/channels` }), listAccountsPublicChannels: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/channels` }), searchUsersOrAccountsChannels: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/chat/channels/search` }), + listChannelActivityLogs: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/activities` }), getRetentionPolicyOfChannel: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/retention` }), updateRetentionPolicyOfChannel: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/retention` }), getChannel: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId, userId }) => `/chat/users/${userId}/channels/${channelId}` }), @@ -1094,6 +1116,10 @@ class TeamChatEndpoints extends WebEndpoints { method: "POST", urlPathBuilder: ({ channelId, userId }) => `/chat/users/${userId}/channels/${channelId}/members` }), + batchRemoveMembersFromUsersChannel: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ channelId, userId }) => `/chat/users/${userId}/channels/${channelId}/members` + }), removeMember: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ channelId, identifier, userId }) => `/chat/users/${userId}/channels/${channelId}/members/${identifier}` @@ -1137,6 +1163,7 @@ class TeamChatEndpoints extends WebEndpoints { chatMessages = { performOperationsOnMessageOfChannel: this.buildEndpoint({ method: "PATCH", urlPathBuilder: () => `/chat/channel/message/events` }), listPinnedHistoryMessagesOfChannel: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/pinned` }), + getForwardedMessage: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ forwardId }) => `/chat/forwarded_message/${forwardId}` }), listBookmarks: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/messages/bookmarks` }), addOrRemoveBookmark: this.buildEndpoint({ method: "PATCH", urlPathBuilder: () => `/chat/messages/bookmarks` }), listScheduledMessages: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/messages/schedule` }), @@ -1162,6 +1189,8 @@ class TeamChatEndpoints extends WebEndpoints { chatMigration = { migrateChannelMembers: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ channelId }) => `/chat/migration/channels/${channelId}/members` }), migrateChatMessageReactions: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/chat/migration/emoji_reactions` }), + getMigratedZoomChannelIDs: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/migration/mappings/channels` }), + getMigratedZoomUserIDs: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/migration/mappings/users` }), migrateChatMessages: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/chat/migration/messages` }), migrateChatChannel: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ identifier }) => `/chat/migration/users/${identifier}/channels` }), migrateConversationOrChannelOperations: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ identifier }) => `/chat/migration/users/${identifier}/events` }) @@ -1202,6 +1231,9 @@ class TeamChatEndpoints extends WebEndpoints { addIMDirectoryGroupMembers: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ groupId }) => `/im/groups/${groupId}/members` }), deleteIMDirectoryGroupMember: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ groupId, memberId }) => `/im/groups/${groupId}/members/${memberId}` }) }; + invitations = { + sendNewContactInvitation: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ userId }) => `/chat/users/${userId}/invitations` }) + }; legalHold = { listLegalHoldMatters: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/legalhold/matters` }), addLegalHoldMatter: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/chat/legalhold/matters` }), @@ -1215,7 +1247,7 @@ class TeamChatEndpoints extends WebEndpoints { }; reports = { getChatSessionsReports: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/chat/sessions` }), - getChatMessagesReports: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/report/chat/sessions/${sessionId}` }) + getChatMessageReports: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/report/chat/sessions/${sessionId}` }) }; sharedSpaces = { listSharedSpaces: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/spaces` }), diff --git a/teamchat/teamchat.d.ts b/teamchat/teamchat.d.ts index f23fcc0..0712d8d 100644 --- a/teamchat/teamchat.d.ts +++ b/teamchat/teamchat.d.ts @@ -1,96 +1,9 @@ -import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { AxiosResponse } from 'axios'; +import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { Server } from 'node:http'; import { ServerOptions } from 'node:https'; import { ReadStream } from 'node:fs'; -type AllKeysOf = T extends any ? keyof T : never; -type AllPropsOptional = Exclude<{ - [P in keyof T]: undefined extends T[P] ? True : False; -}[keyof T], undefined> extends True ? True : False; -type Constructor = new (...args: any[]) => T; -type ExactlyOneOf = { - [K in keyof T]: T[K] & ProhibitKeys, keyof T[K]>>; -}[number]; -type MaybeArray = T | T[]; -type MaybePromise = T | Promise; -type ProhibitKeys = { - [P in K]?: never; -}; -type StringIndexed = Record; - -/** - * {@link StateStore} defines methods for generating and verifying OAuth state. - * - * This interface is implemented internally for the default state store; however, - * it can also be implemented and passed to an OAuth client as well. - */ -interface StateStore { - /** - * Generate a new state string, which is directly appended to the OAuth `state` parameter. - */ - generateState(): MaybePromise; - /** - * Verify that the state received during OAuth callback is valid and not forged. - * - * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. - * - * @param state The state parameter that was received during OAuth callback - */ - verifyState(state: string): MaybePromise; -} -/** - * Guard if an object implements the {@link StateStore} interface — most notably, - * `generateState()` and `verifyState(state: string)`. - */ -declare const isStateStore: (obj: unknown) => obj is StateStore; - -interface TokenStore { - getLatestToken(): MaybePromise; - storeToken(token: Token): MaybePromise; -} - -interface RivetError extends Error { - readonly errorCode: ErrorCode; -} - -declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ - readonly ApiResponseError: "zoom_rivet_api_response_error"; - readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; - readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; - readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; - readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; - readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; - readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; - readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; - readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; - readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; - readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; - readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; - readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; - readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; - readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; - readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; - readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; -}[K]>; -declare const ApiResponseError: Constructor; -declare const AwsReceiverRequestError: Constructor; -declare const ClientCredentialsRawResponseError: Constructor; -declare const S2SRawResponseError: Constructor; -declare const CommonHttpRequestError: Constructor; -declare const ReceiverInconsistentStateError: Constructor; -declare const ReceiverOAuthFlowError: Constructor; -declare const HTTPReceiverConstructionError: Constructor; -declare const HTTPReceiverPortNotNumberError: Constructor; -declare const HTTPReceiverRequestError: Constructor; -declare const OAuthInstallerNotInitializedError: Constructor; -declare const OAuthTokenDoesNotExistError: Constructor; -declare const OAuthTokenFetchFailedError: Constructor; -declare const OAuthTokenRawResponseError: Constructor; -declare const OAuthTokenRefreshFailedError: Constructor; -declare const OAuthStateVerificationFailedError: Constructor; -declare const ProductClientConstructionError: Constructor; - declare enum LogLevel { ERROR = "error", WARN = "warn", @@ -150,6 +63,24 @@ declare class ConsoleLogger implements Logger { private static isMoreOrEqualSevere; } +type AllKeysOf = T extends any ? keyof T : never; +type AllPropsOptional = Exclude<{ + [P in keyof T]: undefined extends T[P] ? True : False; +}[keyof T], undefined> extends True ? True : False; +type Constructor = new (...args: any[]) => T; +type ExactlyOneOf = { + [K in keyof T]: T[K] & ProhibitKeys, keyof T[K]>>; +}[number]; +type MaybeArray = T | T[]; +type MaybePromise = T | Promise; +type ProhibitKeys = Partial>; +type StringIndexed = Record; + +interface TokenStore { + getLatestToken(): MaybePromise; + storeToken(token: Token): MaybePromise; +} + interface AuthOptions { clientId: string; clientSecret: string; @@ -195,6 +126,17 @@ declare abstract class Auth { }>, "grant_type">): Promise; } +interface ClientCredentialsToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} + +interface JwtToken { + token: string; + expirationTimeIso: string; +} + interface S2SAuthToken { accessToken: string; expirationTimeIso: string; @@ -233,12 +175,31 @@ declare class EventManager { protected withContext, Context>(): ContextListener; } +declare enum StatusCode { + OK = 200, + TEMPORARY_REDIRECT = 302, + BAD_REQUEST = 400, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + INTERNAL_SERVER_ERROR = 500 +} +interface ReceiverInitOptions { + eventEmitter?: GenericEventManager | undefined; + interactiveAuth?: InteractiveAuth | undefined; +} +interface Receiver { + canInstall(): true | false; + init(options: ReceiverInitOptions): void; + start(...args: any[]): MaybePromise; + stop(...args: any[]): MaybePromise; +} + interface HttpReceiverOptions extends Partial { endpoints?: MaybeArray | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; port?: number | string | undefined; - webhooksSecretToken: string; - logger?: Logger; - logLevel?: LogLevel; + webhooksSecretToken?: string | undefined; } type SecureServerOptions = { [K in (typeof secureServerOptionKeys)[number]]: ServerOptions[K]; @@ -251,10 +212,15 @@ declare class HttpReceiver implements Receiver { private logger; constructor(options: HttpReceiverOptions); canInstall(): true; + private buildDeletedStateCookieHeader; + private buildStateCookieHeader; + private getRequestCookie; private getServerCreator; private hasEndpoint; private hasSecureOptions; init({ eventEmitter, interactiveAuth }: ReceiverInitOptions): void; + private setResponseCookie; + private areNormalizedUrlsEqual; start(port?: number | string): Promise; stop(): Promise; private writeTemporaryRedirect; @@ -277,6 +243,7 @@ interface WebEndpointOptions { baseUrl?: string | undefined; doubleEncodeUrl?: boolean | undefined; timeout?: number | undefined; + userAgentName?: string | undefined; } type EndpointArguments = (PathSchema extends NoParams ? object : AllPropsOptional extends "t" ? { path?: PathSchema; @@ -298,6 +265,7 @@ declare class WebEndpoints { constructor(options: WebEndpointOptions); protected buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }: BuildEndpointOptions): (_: EndpointArguments) => Promise>; private buildUserAgent; + private getCustomUserAgentName; private getHeaders; private getRequestBody; private isOk; @@ -305,7 +273,7 @@ declare class WebEndpoints { private makeRequest; } -type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & { +type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & Pick & { disableReceiver?: boolean | undefined; logger?: Logger | undefined; logLevel?: LogLevel | undefined; @@ -313,38 +281,68 @@ type CommonClientOptions = GetAuthOptions interface ClientReceiverOptions { receiver: R; } -type ClientConstructorOptions, R extends Receiver> = IsReceiverDisabled extends true ? O : O & (ClientReceiverOptions | HttpReceiverOptions); +type ClientConstructorOptions, R extends Receiver> = (O & { + disableReceiver: true; +}) | (O & (ClientReceiverOptions | HttpReceiverOptions)); type ExtractInstallerOptions = A extends InteractiveAuth ? [ ReturnType ] extends [true] ? WideInstallerOptions : object : object; type ExtractAuthTokenType = A extends Auth ? T : never; -type GenericClientOptions = CommonClientOptions; type GetAuthOptions = AuthOptions> & (A extends S2SAuth ? S2SAuthOptions : object); -type IsReceiverDisabled> = [ - O["disableReceiver"] -] extends [true] ? true : false; type WideInstallerOptions = { installerOptions: InstallerOptions; }; declare abstract class ProductClient, ReceiverType extends Receiver> { private readonly auth; readonly endpoints: EndpointsType; - readonly webEventConsumer: EventProcessorType; + readonly webEventConsumer?: EventProcessorType | undefined; private readonly receiver?; constructor(options: ClientConstructorOptions); protected abstract initAuth(options: OptionsType): AuthType; protected abstract initEndpoints(auth: AuthType, options: OptionsType): EndpointsType; - protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType; + protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType | undefined; private initDefaultReceiver; - start(this: IsReceiverDisabled extends true ? never : this): Promise>; + start(): Promise>; } +/** + * {@link StateStore} defines methods for generating and verifying OAuth state. + * + * This interface is implemented internally for the default state store; however, + * it can also be implemented and passed to an OAuth client as well. + */ +interface StateStore { + /** + * Generate a new state string, which is directly appended to the OAuth `state` parameter. + */ + generateState(): MaybePromise; + /** + * Verify that the state received during OAuth callback is valid and not forged. + * + * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. + * + * @param state The state parameter that was received during OAuth callback + */ + verifyState(state: string): MaybePromise; +} +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +declare const isStateStore: (obj: unknown) => obj is StateStore; + +interface AuthorizationUrlResult { + fullUrl: string; + generatedState: string; +} interface InstallerOptions { directInstall?: boolean | undefined; installPath?: string | undefined; redirectUri: string; redirectUriPath?: string | undefined; stateStore: StateStore | string; + stateCookieName?: string | undefined; + stateCookieMaxAge?: number | undefined; } /** * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication @@ -358,50 +356,19 @@ interface InstallerOptions { */ declare abstract class InteractiveAuth extends Auth { installerOptions?: ReturnType; - getAuthorizationUrl(): Promise; + getAuthorizationUrl(): Promise; getFullRedirectUri(): string; - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }: InstallerOptions): { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }: InstallerOptions): { directInstall: boolean; installPath: string; redirectUri: string; redirectUriPath: string; stateStore: StateStore; + stateCookieName: string; + stateCookieMaxAge: number; }; } -declare enum StatusCode { - OK = 200, - TEMPORARY_REDIRECT = 302, - BAD_REQUEST = 400, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - INTERNAL_SERVER_ERROR = 500 -} -interface ReceiverInitOptions { - eventEmitter: GenericEventManager; - interactiveAuth?: InteractiveAuth | undefined; -} -interface Receiver { - canInstall(): true | false; - init(options: ReceiverInitOptions): void; - start(...args: any[]): MaybePromise; - stop(...args: any[]): MaybePromise; -} - -interface AwsLambdaReceiverOptions { - webhooksSecretToken: string; -} -declare class AwsLambdaReceiver implements Receiver { - private eventEmitter?; - private readonly webhooksSecretToken; - constructor({ webhooksSecretToken }: AwsLambdaReceiverOptions); - buildResponse(statusCode: StatusCode, body: object): LambdaFunctionURLResult; - canInstall(): false; - init({ eventEmitter }: ReceiverInitOptions): void; - start(): LambdaFunctionURLHandler; - stop(): Promise; -} - /** * Credentials for access token & refresh token, which are used to access Zoom's APIs. * @@ -429,6 +396,161 @@ declare class OAuth extends InteractiveAuth { private refreshAccessToken; } +interface RivetError extends Error { + readonly errorCode: ErrorCode; +} + +declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ + readonly ApiResponseError: "zoom_rivet_api_response_error"; + readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; + readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; + readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; + readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; + readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; + readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; + readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; + readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; + readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; + readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; + readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; + readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; + readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; + readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; + readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; + readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; +}[K]>; +declare const ApiResponseError: Constructor; +declare const AwsReceiverRequestError: Constructor; +declare const ClientCredentialsRawResponseError: Constructor; +declare const S2SRawResponseError: Constructor; +declare const CommonHttpRequestError: Constructor; +declare const ReceiverInconsistentStateError: Constructor; +declare const ReceiverOAuthFlowError: Constructor; +declare const HTTPReceiverConstructionError: Constructor; +declare const HTTPReceiverPortNotNumberError: Constructor; +declare const HTTPReceiverRequestError: Constructor; +declare const OAuthInstallerNotInitializedError: Constructor; +declare const OAuthTokenDoesNotExistError: Constructor; +declare const OAuthTokenFetchFailedError: Constructor; +declare const OAuthTokenRawResponseError: Constructor; +declare const OAuthTokenRefreshFailedError: Constructor; +declare const OAuthStateVerificationFailedError: Constructor; +declare const ProductClientConstructionError: Constructor; + +interface AwsLambdaReceiverOptions { + webhooksSecretToken: string; +} +declare class AwsLambdaReceiver implements Receiver { + private eventEmitter?; + private readonly webhooksSecretToken; + constructor({ webhooksSecretToken }: AwsLambdaReceiverOptions); + buildResponse(statusCode: StatusCode, body: object): LambdaFunctionURLResult; + canInstall(): false; + init({ eventEmitter }: ReceiverInitOptions): void; + start(): LambdaFunctionURLHandler; + stop(): Promise; +} + +type ChatChannelMentionGroupListChannelMentionGroupsPathParams = { + channelId: string; +}; +type ChatChannelMentionGroupListChannelMentionGroupsResponse = { + mention_group_list: { + mention_group_id: string; + mention_group_name: string; + mention_group_description?: string; + }[]; +}; +type ChatChannelMentionGroupCreateChannelMentionGroupPathParams = { + channelId: string; +}; +type ChatChannelMentionGroupCreateChannelMentionGroupRequestBody = { + mention_group_name: string; + mention_group_description?: string; + identifiers: string[]; +}; +type ChatChannelMentionGroupCreateChannelMentionGroupResponse = { + mention_group_id: string; +}; +type ChatChannelMentionGroupDeleteChannelMentionGroupPathParams = { + channelId: string; + mentionGroupId: string; +}; +type ChatChannelMentionGroupUpdateChannelMentionGroupInformationPathParams = { + channelId: string; + mentionGroupId: string; +}; +type ChatChannelMentionGroupUpdateChannelMentionGroupInformationRequestBody = { + mention_group_name?: string; + mention_group_description?: string; +}; +type ChatChannelMentionGroupListMembersOfMentionGroupPathParams = { + channelId: string; + mentionGroupId: string; +}; +type ChatChannelMentionGroupListMembersOfMentionGroupQueryParams = { + next_page_token?: string; + page_size?: number; +}; +type ChatChannelMentionGroupListMembersOfMentionGroupResponse = { + mention_group_id: string; + members?: { + user_id?: string; + member_id?: string; + email?: string; + first_name?: string; + last_name?: string; + disaplay_name?: string; + is_external?: boolean; + }[]; + next_page_token?: string; + page_size: number; + has_more: boolean; +}; +type ChatChannelMentionGroupAddChannelMembersToMentionGroupPathParams = { + channelId: string; + mentionGroupId: string; +}; +type ChatChannelMentionGroupAddChannelMembersToMentionGroupRequestBody = { + identifiers: string[]; +}; +type ChatChannelMentionGroupRemoveChannelMentionGroupMembersPathParams = { + channelId: string; + mentionGroupId: string; +}; +type ChatChannelMentionGroupRemoveChannelMentionGroupMembersQueryParams = { + identifiers: string; +}; +type ChatChannelsListChannelActivityLogsQueryParams = { + page_size?: number; + next_page_token?: string; + activity_type?: "membership"; + start_date: string; + end_date: string; + channel_id?: string; +}; +type ChatChannelsListChannelActivityLogsResponse = { + channel_activity_logs: { + channel_id: string; + activity_type: "membership"; + activity_timestamp: number; + operator: { + display_name?: string; + user_id?: string; + member_id?: string; + }; + members?: { + member_status?: "joined" | "left"; + member_list?: { + display_name?: string; + user_id?: string; + member_id?: string; + }[]; + }; + }[]; + next_page_token?: string; + page_size?: number; +}; type ChatChannelsPerformOperationsOnChannelsRequestBody = { method: "archive" | "unarchive"; channel_ids: string[]; @@ -450,6 +572,10 @@ type ChatChannelsGetChannelResponse = { posting_permissions?: 1 | 2 | 3; mention_all_permissions?: 1 | 2 | 3; allow_to_add_external_users?: 0 | 1 | 2 | 3; + designated_posting_members?: { + member_id?: string; + user_id?: string; + }[]; }; id?: string; jid?: string; @@ -605,6 +731,10 @@ type ChatChannelsCreateChannelRequestBody = { }[]; name?: string; type?: 1 | 2 | 3 | 4; + shared_space?: { + space_id: string; + space_channel_type?: "private" | "public_for_members"; + }; }; type ChatChannelsCreateChannelResponse = { id?: string; @@ -654,6 +784,7 @@ type ChatChannelsAccountLevelSearchUsersOrAccountsChannelsRequestBody = { channel_name: string; }; haystack: "user_joined" | "public" | "all"; + include_archived?: boolean; }; type ChatChannelsAccountLevelSearchUsersOrAccountsChannelsResponse = { channels: { @@ -661,11 +792,42 @@ type ChatChannelsAccountLevelSearchUsersOrAccountsChannelsResponse = { name: string; type: 0 | 1 | 2 | 3 | 4 | 5; channel_url: string; - total_members: number; + member_count: number; }[]; next_page_token?: string; page_size: number; }; +type ChatChannelsAccountLevelListChannelActivityLogsPathParams = { + channelId: string; +}; +type ChatChannelsAccountLevelListChannelActivityLogsQueryParams = { + page_size?: number; + next_page_token?: string; + activity_type?: "membership"; + start_date: string; + end_date: string; +}; +type ChatChannelsAccountLevelListChannelActivityLogsResponse = { + channel_activity_logs: { + activity_type: "membership"; + activity_timestamp: number; + operator: { + display_name?: string; + user_id?: string; + member_id?: string; + }; + members?: { + member_status?: "joined" | "left"; + member_list?: { + display_name?: string; + user_id?: string; + member_id?: string; + }[]; + }; + }[]; + next_page_token?: string; + page_size?: number; +}; type ChatChannelsAccountLevelGetRetentionPolicyOfChannelPathParams = { channelId: string; }; @@ -696,6 +858,10 @@ type ChatChannelsAccountLevelGetChannelResponse = { posting_permissions?: 1 | 2 | 3; mention_all_permissions?: 1 | 2 | 3; allow_to_add_external_users?: 0 | 1 | 2 | 3; + designated_posting_members?: { + member_id?: string; + user_id?: string; + }[]; }; id?: string; jid?: string; @@ -743,6 +909,7 @@ type ChatChannelsAccountLevelListChannelAdministratorsResponse = { role?: "admin" | "owner"; last_name?: string; name?: string; + is_external?: boolean; }[]; next_page_token?: string; page_size?: number; @@ -808,6 +975,13 @@ type ChatChannelsAccountLevelInviteChannelMembersResponse = { ids?: string; member_ids?: string; }; +type ChatChannelsAccountLevelBatchRemoveMembersFromUsersChannelPathParams = { + channelId: string; + userId: string; +}; +type ChatChannelsAccountLevelBatchRemoveMembersFromUsersChannelQueryParams = { + identifiers: string; +}; type ChatChannelsAccountLevelRemoveMemberPathParams = { channelId: string; identifier: string; @@ -834,7 +1008,7 @@ type ChatEmojiListCustomEmojisResponse = { }; type ChatEmojiAddCustomEmojiRequestBody = { name: string; - file: ReadStream; + file: Blob | Buffer | ReadStream; }; type ChatEmojiAddCustomEmojiResponse = { file_id: string; @@ -854,6 +1028,7 @@ type ChatFilesGetFileInfoResponse = { digest: string; created_time: number; modified_time: number; + download_url: string; public_url?: string; }; type ChatFilesDeleteChatFilePathParams = { @@ -862,8 +1037,11 @@ type ChatFilesDeleteChatFilePathParams = { type ChatFilesUploadChatFilePathParams = { userId: string; }; +type ChatFilesUploadChatFileQueryParams = { + postToPersonalChat?: string; +}; type ChatFilesUploadChatFileRequestBody = { - file?: ReadStream; + file?: Blob | Buffer | ReadStream; }; type ChatFilesUploadChatFileResponse = { id?: string; @@ -872,7 +1050,7 @@ type ChatFilesSendChatFilePathParams = { userId: string; }; type ChatFilesSendChatFileRequestBody = { - files: ReadStream[]; + files: (Blob | Buffer | ReadStream)[]; reply_main_message_id?: string; to_channel?: string; to_contact?: string; @@ -908,6 +1086,55 @@ type ChatMessagesListPinnedHistoryMessagesOfChannelResponse = { channel_id: string; page_size?: number; }; +type ChatMessagesGetForwardedMessagePathParams = { + forwardId: string; +}; +type ChatMessagesGetForwardedMessageResponse = { + bot_message?: object; + date_time?: string; + download_url?: string; + file_id?: string; + file_name?: string; + file_size?: number; + rich_text?: { + start_position?: number; + end_position?: number; + format_type?: "Bold" | "Italic" | "Strikethrough" | "BulletedList" | "NumberedList" | "Underline" | "FontSize" | "FontColor" | "BackgroundColor" | "LeftIndent" | "Paragraph" | "Quote" | "AddLink"; + format_attr?: string; + }[]; + files?: { + download_url?: string; + file_id?: string; + file_name?: string; + file_size?: number; + }[]; + forward_id?: string; + message?: string; + message_type?: "plain_text_message" | "jpg_image_file" | "audio_file" | "video_file" | "png_image_file" | "gif_file" | "giphy_file" | "code_snippet" | "file_and_text" | "others"; + reactions?: { + emoji?: string; + total_count?: number; + user_ids?: string[]; + member_ids?: string[]; + }[]; + reply_main_message_id?: string; + reply_main_message_timestamp?: number; + sender?: string; + send_member_id?: string; + sender_display_name?: string; + timestamp?: number; + at_items?: { + at_contact?: string; + at_contact_member_id?: string; + at_type?: 1 | 2; + end_position?: number; + start_position?: number; + }[]; + interactive_cards?: { + card_id?: string; + card_json?: string; + }[]; +}; type ChatMessagesListBookmarksQueryParams = { to_contact?: string; to_channel?: string; @@ -1040,6 +1267,7 @@ type ChatMessagesListUsersChatMessagesResponse = { file_size?: number; id?: string; message?: string; + message_type?: "plain_text_message" | "jpg_image_file" | "audio_file" | "video_file" | "png_image_file" | "gif_file" | "giphy_file" | "code_snippet" | "file_and_text" | "others"; reactions?: { emoji?: string; total_count?: number; @@ -1051,7 +1279,7 @@ type ChatMessagesListUsersChatMessagesResponse = { reply_main_message_id?: string; reply_main_message_timestamp?: number; sender?: string; - sender_member_id?: string; + send_member_id?: string; sender_display_name?: string; status?: "Deleted" | "Edited" | "Normal"; timestamp?: number; @@ -1062,14 +1290,14 @@ type ChatMessagesListUsersChatMessagesResponse = { end_position?: number; start_position?: number; }[]; + interactive_cards?: { + card_id?: string; + card_json?: string; + }[]; }[]; next_page_token?: string; page_size?: number; to?: string; - interactive_cards?: { - card_id?: string; - card_json?: string; - }[]; }; type ChatMessagesSendChatMessagePathParams = { userId: string; @@ -1130,6 +1358,7 @@ type ChatMessagesGetMessageResponse = { }[]; id?: string; message?: string; + message_type?: "plain_text_message" | "jpg_image_file" | "audio_file" | "video_file" | "png_image_file" | "gif_file" | "giphy_file" | "code_snippet" | "file_and_text" | "others"; reactions?: { emoji?: string; total_count?: number; @@ -1139,7 +1368,7 @@ type ChatMessagesGetMessageResponse = { reply_main_message_id?: string; reply_main_message_timestamp?: number; sender?: string; - sender_member_id?: string; + send_member_id?: string; sender_display_name?: string; timestamp?: number; message_url?: string; @@ -1187,6 +1416,8 @@ type ChatMessagesReactToChatMessageRequestBody = { emoji?: string; to_channel?: string; to_contact?: string; + custom_emoji?: boolean; + custom_emoji_id?: string; }; type ChatMessagesMarkMessageReadOrUnreadPathParams = { userId: string; @@ -1212,6 +1443,8 @@ type ChatMessagesRetrieveThreadQueryParams = { need_main_message?: boolean; need_emoji?: boolean; need_attachment?: boolean; + need_rich_text?: boolean; + need_at_items?: boolean; }; type ChatMessagesRetrieveThreadResponse = { total: number; @@ -1233,6 +1466,29 @@ type ChatMessagesRetrieveThreadResponse = { file_size?: number; download_url?: string; }[]; + sender_user_id?: string; + sender_email?: string; + sender_member_id?: string; + sender_display_name?: string; + rich_text?: { + start_position?: number; + end_position?: number; + format_type?: "Bold" | "Italic" | "Strikethrough" | "BulletedList" | "NumberedList" | "Underline" | "FontSize" | "FontColor" | "BackgroundColor" | "LeftIndent" | "Paragraph" | "Quote" | "AddLink"; + format_attr?: string; + }[]; + at_items?: { + at_contact?: string; + at_contact_member_id?: string; + at_type?: 1 | 2; + end_position?: number; + start_position?: number; + }[]; + bot_message?: object; + interactive_cards?: { + card_id?: string; + card_json?: string; + }[]; + message_type?: "plain_text_message" | "jpg_image_file" | "audio_file" | "video_file" | "png_image_file" | "gif_file" | "giphy_file" | "code_snippet" | "file_and_text" | "others"; }[]; }; type ChatMigrationMigrateChannelMembersPathParams = { @@ -1256,13 +1512,39 @@ type ChatMigrationMigrateChatMessageReactionsRequestBody = { }[]; }[]; }; +type ChatMigrationGetMigratedZoomChannelIDsQueryParams = { + origin_platform: "slack"; + origin_team_id: string; + origin_channel_ids: string; +}; +type ChatMigrationGetMigratedZoomChannelIDsResponse = { + mappings_found?: number; + mappings_not_found?: number; + mappings?: { + origin_channel_id?: string; + zm_channel_id?: string; + }[]; +}; +type ChatMigrationGetMigratedZoomUserIDsQueryParams = { + origin_platform: "slack"; + origin_team_id?: string; + origin_user_ids: string; +}; +type ChatMigrationGetMigratedZoomUserIDsResponse = { + mappings_found?: number; + mappings_not_found?: number; + mappings?: { + origin_user_id?: string; + zm_user_id?: string; + }[]; +}; type ChatMigrationMigrateChatMessagesRequestBody = { messages: { message_timestamp: number; sender: string; to_channel?: string; to_contact?: string; - message?: string; + message: string; file_ids?: string[]; reply_main_message_id?: string; reply_main_message_timestamp?: number; @@ -1284,7 +1566,7 @@ type ChatMigrationMigrateChatChannelRequestBody = { created_time: string; }; type ChatMigrationMigrateChatChannelResponse = { - channel_id: string; + id: string; }; type ChatMigrationMigrateConversationOrChannelOperationsPathParams = { identifier: string; @@ -1529,7 +1811,6 @@ type IMGroupsListIMDirectoryGroupMembersQueryParams = { type IMGroupsListIMDirectoryGroupMembersResponse = { next_page_token?: string; page_count?: number; - /***Deprecated.** We will no longer support this field in a future release. Instead, use the `next_page_token` for pagination.*/ page_number?: number; page_size?: number; total_records?: number; @@ -1551,10 +1832,21 @@ type IMGroupsAddIMDirectoryGroupMembersRequestBody = { id?: string; }[]; }; +type IMGroupsAddIMDirectoryGroupMembersResponse = { + added_at?: string; + ids?: string; +}; type IMGroupsDeleteIMDirectoryGroupMemberPathParams = { groupId: string; memberId: string; }; +type InvitationsSendNewContactInvitationPathParams = { + userId: string; +}; +type InvitationsSendNewContactInvitationRequestBody = { + email: string; + message?: string; +}; type LegalHoldListLegalHoldMattersQueryParams = { page_size?: number; next_page_token?: string; @@ -1634,21 +1926,26 @@ type ReportsGetChatSessionsReportsResponse = { name?: string; type?: "Group" | "1:1"; channel_id?: string; + member_emails?: string[]; + status?: "active" | "deleted"; + has_external_member?: boolean; }[]; to?: string; }; -type ReportsGetChatMessagesReportsPathParams = { +type ReportsGetChatMessageReportsPathParams = { sessionId: string; }; -type ReportsGetChatMessagesReportsQueryParams = { +type ReportsGetChatMessageReportsQueryParams = { from: string; to: string; next_page_token?: string; page_size?: number; include_fields?: "edited_messages" | "deleted_messages" | "edited_messages,deleted_messages"; include_bot_message?: boolean; + include_reactions?: boolean; + query_all_modifications?: boolean; }; -type ReportsGetChatMessagesReportsResponse = { +type ReportsGetChatMessageReportsResponse = { deleted_messages?: { date_time?: string; files?: { @@ -1657,15 +1954,27 @@ type ReportsGetChatMessagesReportsResponse = { file_name?: string; file_size?: number; }[]; + giphy_information?: { + giphy_view_url?: string; + }[]; id?: string; message?: string; receiver?: string; + is_sender_external?: boolean; reply_main_message_id?: string; reply_main_message_timestamp?: number; sender?: string; sender_member_id?: string; sender_display_name?: string; timestamp?: number; + action_timestamp?: number; + forward_id?: string; + rich_text?: { + start_position?: number; + end_position?: number; + format_type?: "Bold" | "Italic" | "Strikethrough" | "BulletedList" | "NumberedList" | "Underline" | "FontSize" | "FontColor" | "BackgroundColor" | "LeftIndent" | "Paragraph" | "Quote" | "AddLink"; + format_attr?: string; + }[]; }[]; edited_messages?: { date_time?: string; @@ -1675,15 +1984,27 @@ type ReportsGetChatMessagesReportsResponse = { file_name?: string; file_size?: number; }[]; + giphy_information?: { + giphy_view_url?: string; + }[]; id?: string; message?: string; receiver?: string; + is_sender_external?: boolean; reply_main_message_id?: string; reply_main_message_timestamp?: number; sender_member_id?: string; sender?: string; sender_display_name?: string; timestamp?: number; + action_timestamp?: number; + forward_id?: string; + rich_text?: { + start_position?: number; + end_position?: number; + format_type?: "Bold" | "Italic" | "Strikethrough" | "BulletedList" | "NumberedList" | "Underline" | "FontSize" | "FontColor" | "BackgroundColor" | "LeftIndent" | "Paragraph" | "Quote" | "AddLink"; + format_attr?: string; + }[]; }[]; from?: string; messages?: { @@ -1694,21 +2015,39 @@ type ReportsGetChatMessagesReportsResponse = { file_name?: string; file_size?: number; }[]; + giphy_information?: { + giphy_view_url?: string; + }[]; id?: string; message?: string; + reactions?: { + emoji?: string; + total_count?: number; + user_ids?: string[]; + member_ids?: string[]; + }[]; receiver?: string; + is_sender_external?: boolean; reply_main_message_id?: string; reply_main_message_timestamp?: number; sender?: string; sender_member_id?: string; sender_display_name?: string; timestamp?: number; + action_timestamp?: number; + forward_id?: string; bot_message?: { is_markdown_support?: boolean; source?: string; external_sender_email?: string; content?: object; }; + rich_text?: { + start_position?: number; + end_position?: number; + format_type?: "Bold" | "Italic" | "Strikethrough" | "BulletedList" | "NumberedList" | "Underline" | "FontSize" | "FontColor" | "BackgroundColor" | "LeftIndent" | "Paragraph" | "Quote" | "AddLink"; + format_attr?: string; + }[]; }[]; next_page_token?: string; page_size?: number; @@ -1930,7 +2269,43 @@ type SharedSpacesTransferSharedSpaceOwnershipQueryParams = { identifier: string; }; declare class TeamChatEndpoints extends WebEndpoints { + readonly chatChannelMentionGroup: { + listChannelMentionGroups: (_: { + path: ChatChannelMentionGroupListChannelMentionGroupsPathParams; + } & object) => Promise>; + createChannelMentionGroup: (_: { + path: ChatChannelMentionGroupCreateChannelMentionGroupPathParams; + } & { + body: ChatChannelMentionGroupCreateChannelMentionGroupRequestBody; + } & object) => Promise>; + deleteChannelMentionGroup: (_: { + path: ChatChannelMentionGroupDeleteChannelMentionGroupPathParams; + } & object) => Promise>; + updateChannelMentionGroupInformation: (_: { + path: ChatChannelMentionGroupUpdateChannelMentionGroupInformationPathParams; + } & { + body?: ChatChannelMentionGroupUpdateChannelMentionGroupInformationRequestBody; + } & object) => Promise>; + listMembersOfMentionGroup: (_: { + path: ChatChannelMentionGroupListMembersOfMentionGroupPathParams; + } & object & { + query?: ChatChannelMentionGroupListMembersOfMentionGroupQueryParams; + }) => Promise>; + addChannelMembersToMentionGroup: (_: { + path: ChatChannelMentionGroupAddChannelMembersToMentionGroupPathParams; + } & { + body: ChatChannelMentionGroupAddChannelMembersToMentionGroupRequestBody; + } & object) => Promise>; + removeChannelMentionGroupMembers: (_: { + path: ChatChannelMentionGroupRemoveChannelMentionGroupMembersPathParams; + } & object & { + query: ChatChannelMentionGroupRemoveChannelMentionGroupMembersQueryParams; + }) => Promise>; + }; readonly chatChannels: { + listChannelActivityLogs: (_: object & { + query: ChatChannelsListChannelActivityLogsQueryParams; + }) => Promise>; performOperationsOnChannels: (_: object & { body: ChatChannelsPerformOperationsOnChannelsRequestBody; }) => Promise>; @@ -2003,6 +2378,11 @@ declare class TeamChatEndpoints extends WebEndpoints { searchUsersOrAccountsChannels: (_: object & { body: ChatChannelsAccountLevelSearchUsersOrAccountsChannelsRequestBody; }) => Promise>; + listChannelActivityLogs: (_: { + path: ChatChannelsAccountLevelListChannelActivityLogsPathParams; + } & object & { + query: ChatChannelsAccountLevelListChannelActivityLogsQueryParams; + }) => Promise>; getRetentionPolicyOfChannel: (_: { path: ChatChannelsAccountLevelGetRetentionPolicyOfChannelPathParams; } & object) => Promise>; @@ -2047,6 +2427,11 @@ declare class TeamChatEndpoints extends WebEndpoints { } & { body?: ChatChannelsAccountLevelInviteChannelMembersRequestBody; } & object) => Promise>; + batchRemoveMembersFromUsersChannel: (_: { + path: ChatChannelsAccountLevelBatchRemoveMembersFromUsersChannelPathParams; + } & object & { + query: ChatChannelsAccountLevelBatchRemoveMembersFromUsersChannelQueryParams; + }) => Promise>; removeMember: (_: { path: ChatChannelsAccountLevelRemoveMemberPathParams; } & object) => Promise>; @@ -2073,7 +2458,9 @@ declare class TeamChatEndpoints extends WebEndpoints { path: ChatFilesUploadChatFilePathParams; } & { body?: ChatFilesUploadChatFileRequestBody; - } & object) => Promise>; + } & { + query?: ChatFilesUploadChatFileQueryParams; + }) => Promise>; sendChatFile: (_: { path: ChatFilesSendChatFilePathParams; } & { @@ -2089,6 +2476,9 @@ declare class TeamChatEndpoints extends WebEndpoints { } & object & { query?: ChatMessagesListPinnedHistoryMessagesOfChannelQueryParams; }) => Promise>; + getForwardedMessage: (_: { + path: ChatMessagesGetForwardedMessagePathParams; + } & object) => Promise>; listBookmarks: (_: object & { query?: ChatMessagesListBookmarksQueryParams; }) => Promise>; @@ -2155,6 +2545,12 @@ declare class TeamChatEndpoints extends WebEndpoints { migrateChatMessageReactions: (_: object & { body: ChatMigrationMigrateChatMessageReactionsRequestBody; }) => Promise>; + getMigratedZoomChannelIDs: (_: object & { + query: ChatMigrationGetMigratedZoomChannelIDsQueryParams; + }) => Promise>; + getMigratedZoomUserIDs: (_: object & { + query: ChatMigrationGetMigratedZoomUserIDsQueryParams; + }) => Promise>; migrateChatMessages: (_: object & { body: ChatMigrationMigrateChatMessagesRequestBody; }) => Promise>; @@ -2241,11 +2637,18 @@ declare class TeamChatEndpoints extends WebEndpoints { path: IMGroupsAddIMDirectoryGroupMembersPathParams; } & { body?: IMGroupsAddIMDirectoryGroupMembersRequestBody; - } & object) => Promise>; + } & object) => Promise>; deleteIMDirectoryGroupMember: (_: { path: IMGroupsDeleteIMDirectoryGroupMemberPathParams; } & object) => Promise>; }; + readonly invitations: { + sendNewContactInvitation: (_: { + path: InvitationsSendNewContactInvitationPathParams; + } & { + body: InvitationsSendNewContactInvitationRequestBody; + } & object) => Promise>; + }; readonly legalHold: { listLegalHoldMatters: (_: object & { query?: LegalHoldListLegalHoldMattersQueryParams; @@ -2276,11 +2679,11 @@ declare class TeamChatEndpoints extends WebEndpoints { getChatSessionsReports: (_: object & { query: ReportsGetChatSessionsReportsQueryParams; }) => Promise>; - getChatMessagesReports: (_: { - path: ReportsGetChatMessagesReportsPathParams; + getChatMessageReports: (_: { + path: ReportsGetChatMessageReportsPathParams; } & object & { - query: ReportsGetChatMessagesReportsQueryParams; - }) => Promise>; + query: ReportsGetChatMessageReportsQueryParams; + }) => Promise>; }; readonly sharedSpaces: { listSharedSpaces: (_: object & { @@ -2749,10 +3152,12 @@ type TeamChatDmMessagePostedEvent = Event<"team_chat.dm_message_posted"> & { object: { message_id: string; reply_main_message_id?: string; + session_id: string; date_time: string; timestamp: number; contact_email: string; contact_id: string; + contact_account_id: string; contact_member_id: string; message?: string; files?: { @@ -2881,6 +3286,7 @@ type TeamChatChannelMessageDeletedEvent = Event<"team_chat.channel_message_delet date_time: string; timestamp: number; channel_id: string; + channel_owner_account_id: string; channel_name: string; reply_main_message_id?: string; }; @@ -3023,10 +3429,12 @@ type TeamChatDmMessageUpdatedEvent = Event<"team_chat.dm_message_updated"> & { object: { message_id: string; reply_main_message_id?: string; + session_id: string; date_time: string; timestamp: number; contact_email: string; contact_id: string; + contact_account_id: string; contact_member_id: string; message?: string; files?: { @@ -3174,6 +3582,7 @@ type TeamChatFileUnsharedEvent = Event<"team_chat.file_unshared"> & { operator: string; operator_id: string; operator_member_id: string; + by_external_user: boolean; object: { type: "to_contact" | "to_channel"; channel_id?: string; @@ -3202,9 +3611,11 @@ type TeamChatChannelMessageUpdatedEvent = Event<"team_chat.channel_message_updat object: { message_id: string; reply_main_message_id?: string; + session_id: string; date_time: string; timestamp: number; channel_id: string; + channel_owner_account_id: string; channel_name: string; message?: string; files?: { @@ -3220,6 +3631,14 @@ type TeamChatChannelMessageUpdatedEvent = Event<"team_chat.channel_message_updat format_type: "Bold" | "Italic" | "Strikethrough" | "BulletedList" | "NumberedList" | "Underline" | "FontSize" | "FontColor" | "BackgroundColor" | "LeftIndent" | "Paragraph" | "Quote" | "AddLink"; format_attr?: string; }[]; + at_items?: { + at_contact?: string; + at_contact_user_id?: string; + at_contact_member_id?: string; + at_type: 1 | 2; + end_position: number; + start_position: number; + }[]; }; }; }; @@ -3278,6 +3697,7 @@ type TeamChatDmMessageDeletedEvent = Event<"team_chat.dm_message_deleted"> & { object: { message_id: string; date_time: string; + contact_account_id: string; timestamp: number; contact_email: string; contact_id: string; @@ -3316,9 +3736,11 @@ type TeamChatChannelMessagePostedEvent = Event<"team_chat.channel_message_posted object: { message_id: string; reply_main_message_id?: string; + session_id: string; date_time: string; timestamp: number; channel_id: string; + channel_owner_account_id: string; channel_name: string; message?: string; files?: { @@ -3334,6 +3756,14 @@ type TeamChatChannelMessagePostedEvent = Event<"team_chat.channel_message_posted format_type: "Bold" | "Italic" | "Strikethrough" | "BulletedList" | "NumberedList" | "Underline" | "FontSize" | "FontColor" | "BackgroundColor" | "LeftIndent" | "Paragraph" | "Quote" | "AddLink"; format_attr?: string; }[]; + at_items?: { + at_contact?: string; + at_contact_user_id?: string; + at_contact_member_id?: string; + at_type: 1 | 2; + end_position: number; + start_position: number; + }[]; }; }; }; @@ -3509,6 +3939,7 @@ type TeamChatFileSharedEvent = Event<"team_chat.file_shared"> & { operator: string; operator_id: string; operator_member_id: string; + by_external_user: boolean; object: { type: "to_contact" | "to_channel"; channel_id?: string; @@ -3710,4 +4141,5 @@ declare class TeamChatS2SAuthClient typeof obj.installerOptions.redirectUri !== "undefined" && typeof obj.installerOptions.stateStore !== "undefined"; @@ -450,7 +453,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -460,14 +466,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -579,9 +591,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -595,6 +604,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? createServer : createServer$1; } @@ -609,8 +631,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -627,69 +661,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -723,18 +772,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -827,7 +882,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -848,7 +905,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. @@ -856,87 +913,13 @@ class ProductClient { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -965,10 +948,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -1031,7 +1027,32 @@ class WebEndpoints { } class TeamChatEndpoints extends WebEndpoints { + chatChannelMentionGroup = { + listChannelMentionGroups: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/mention_group` }), + createChannelMentionGroup: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/mention_group` }), + deleteChannelMentionGroup: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ channelId, mentionGroupId }) => `/chat/channels/${channelId}/mention_group/${mentionGroupId}` + }), + updateChannelMentionGroupInformation: this.buildEndpoint({ + method: "PATCH", + urlPathBuilder: ({ channelId, mentionGroupId }) => `/chat/channels/${channelId}/mention_group/${mentionGroupId}` + }), + listMembersOfMentionGroup: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ channelId, mentionGroupId }) => `/chat/channels/${channelId}/mention_group/${mentionGroupId}/members` + }), + addChannelMembersToMentionGroup: this.buildEndpoint({ + method: "POST", + urlPathBuilder: ({ channelId, mentionGroupId }) => `/chat/channels/${channelId}/mention_group/${mentionGroupId}/members` + }), + removeChannelMentionGroupMembers: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ channelId, mentionGroupId }) => `/chat/channels/${channelId}/mention_group/${mentionGroupId}/members` + }) + }; chatChannels = { + listChannelActivityLogs: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/activities/channels` }), performOperationsOnChannels: this.buildEndpoint({ method: "PATCH", urlPathBuilder: () => `/chat/channels/events` }), getChannel: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}` }), deleteChannel: this.buildEndpoint({ @@ -1064,6 +1085,7 @@ class TeamChatEndpoints extends WebEndpoints { batchDeleteChannels: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ userId }) => `/chat/users/${userId}/channels` }), listAccountsPublicChannels: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/channels` }), searchUsersOrAccountsChannels: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/chat/channels/search` }), + listChannelActivityLogs: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/activities` }), getRetentionPolicyOfChannel: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/retention` }), updateRetentionPolicyOfChannel: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/retention` }), getChannel: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId, userId }) => `/chat/users/${userId}/channels/${channelId}` }), @@ -1092,6 +1114,10 @@ class TeamChatEndpoints extends WebEndpoints { method: "POST", urlPathBuilder: ({ channelId, userId }) => `/chat/users/${userId}/channels/${channelId}/members` }), + batchRemoveMembersFromUsersChannel: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ channelId, userId }) => `/chat/users/${userId}/channels/${channelId}/members` + }), removeMember: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ channelId, identifier, userId }) => `/chat/users/${userId}/channels/${channelId}/members/${identifier}` @@ -1135,6 +1161,7 @@ class TeamChatEndpoints extends WebEndpoints { chatMessages = { performOperationsOnMessageOfChannel: this.buildEndpoint({ method: "PATCH", urlPathBuilder: () => `/chat/channel/message/events` }), listPinnedHistoryMessagesOfChannel: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ channelId }) => `/chat/channels/${channelId}/pinned` }), + getForwardedMessage: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ forwardId }) => `/chat/forwarded_message/${forwardId}` }), listBookmarks: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/messages/bookmarks` }), addOrRemoveBookmark: this.buildEndpoint({ method: "PATCH", urlPathBuilder: () => `/chat/messages/bookmarks` }), listScheduledMessages: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/messages/schedule` }), @@ -1160,6 +1187,8 @@ class TeamChatEndpoints extends WebEndpoints { chatMigration = { migrateChannelMembers: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ channelId }) => `/chat/migration/channels/${channelId}/members` }), migrateChatMessageReactions: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/chat/migration/emoji_reactions` }), + getMigratedZoomChannelIDs: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/migration/mappings/channels` }), + getMigratedZoomUserIDs: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/migration/mappings/users` }), migrateChatMessages: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/chat/migration/messages` }), migrateChatChannel: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ identifier }) => `/chat/migration/users/${identifier}/channels` }), migrateConversationOrChannelOperations: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ identifier }) => `/chat/migration/users/${identifier}/events` }) @@ -1200,6 +1229,9 @@ class TeamChatEndpoints extends WebEndpoints { addIMDirectoryGroupMembers: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ groupId }) => `/im/groups/${groupId}/members` }), deleteIMDirectoryGroupMember: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ groupId, memberId }) => `/im/groups/${groupId}/members/${memberId}` }) }; + invitations = { + sendNewContactInvitation: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ userId }) => `/chat/users/${userId}/invitations` }) + }; legalHold = { listLegalHoldMatters: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/legalhold/matters` }), addLegalHoldMatter: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/chat/legalhold/matters` }), @@ -1213,7 +1245,7 @@ class TeamChatEndpoints extends WebEndpoints { }; reports = { getChatSessionsReports: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/report/chat/sessions` }), - getChatMessagesReports: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/report/chat/sessions/${sessionId}` }) + getChatMessageReports: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/report/chat/sessions/${sessionId}` }) }; sharedSpaces = { listSharedSpaces: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/chat/spaces` }), diff --git a/users/users.cjs b/users/users.cjs index e13edf5..1b0d265 100644 --- a/users/users.cjs +++ b/users/users.cjs @@ -426,6 +426,9 @@ class JwtStateStore { const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; const hasInstallerOptions = (obj) => typeof obj.installerOptions.redirectUri !== "undefined" && typeof obj.installerOptions.stateStore !== "undefined"; @@ -452,7 +455,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -462,14 +468,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -581,9 +593,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -597,6 +606,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? node_https.createServer : node_http.createServer; } @@ -611,8 +633,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -629,69 +663,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, exports.StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, exports.StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -725,18 +774,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(exports.StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -745,87 +800,13 @@ class HttpReceiver { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -854,10 +835,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${node_path.basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -933,6 +927,21 @@ class UsersEndpoints extends WebEndpoints { addContactGroupMembers: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ groupId }) => `/contacts/groups/${groupId}/members` }), removeMembersInContactGroup: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ groupId }) => `/contacts/groups/${groupId}/members` }) }; + divisions = { + listDivisions: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/divisions` }), + createDivision: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/divisions` }), + getDivision: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ divisionId }) => `/divisions/${divisionId}` + }), + deleteDivision: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ divisionId }) => `/divisions/${divisionId}` + }), + updateDivision: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ divisionId }) => `/divisions/${divisionId}` }), + listDivisionMembers: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ divisionId }) => `/divisions/${divisionId}/users` }), + assignDivision: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ divisionId }) => `/divisions/${divisionId}/users` }) + }; groups = { listGroups: this.buildEndpoint({ method: "GET", @@ -955,10 +964,7 @@ class UsersEndpoints extends WebEndpoints { urlPathBuilder: ({ groupId }) => `/groups/${groupId}` }), listGroupAdmins: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/admins` }), - addGroupAdmins: this.buildEndpoint({ - method: "POST", - urlPathBuilder: ({ groupId }) => `/groups/${groupId}/admins` - }), + addGroupAdmins: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/admins` }), deleteGroupAdmin: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ groupId, userId }) => `/groups/${groupId}/admins/${userId}` @@ -967,10 +973,7 @@ class UsersEndpoints extends WebEndpoints { getLockedSettings: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/lock_settings` }), updateLockedSettings: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/lock_settings` }), listGroupMembers: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/members` }), - addGroupMembers: this.buildEndpoint({ - method: "POST", - urlPathBuilder: ({ groupId }) => `/groups/${groupId}/members` - }), + addGroupMembers: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/members` }), deleteGroupMember: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ groupId, memberId }) => `/groups/${groupId}/members/${memberId}` @@ -997,7 +1000,7 @@ class UsersEndpoints extends WebEndpoints { urlPathBuilder: () => `/users` }), checkUserEmail: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/users/email` }), - bulkUpdateFeaturesForUser: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/users/features` }), + bulkUpdateFeaturesForUsers: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/users/features` }), getUsersZAK: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/users/me/zak` @@ -1171,7 +1174,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -1192,7 +1197,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. diff --git a/users/users.d.ts b/users/users.d.ts index 1306f5d..90d25a0 100644 --- a/users/users.d.ts +++ b/users/users.d.ts @@ -1,88 +1,8 @@ -import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { AxiosResponse } from 'axios'; +import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { Server } from 'node:http'; import { ServerOptions } from 'node:https'; -type AllPropsOptional = Exclude<{ - [P in keyof T]: undefined extends T[P] ? True : False; -}[keyof T], undefined> extends True ? True : False; -type Constructor = new (...args: any[]) => T; -type MaybeArray = T | T[]; -type MaybePromise = T | Promise; -type StringIndexed = Record; - -/** - * {@link StateStore} defines methods for generating and verifying OAuth state. - * - * This interface is implemented internally for the default state store; however, - * it can also be implemented and passed to an OAuth client as well. - */ -interface StateStore { - /** - * Generate a new state string, which is directly appended to the OAuth `state` parameter. - */ - generateState(): MaybePromise; - /** - * Verify that the state received during OAuth callback is valid and not forged. - * - * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. - * - * @param state The state parameter that was received during OAuth callback - */ - verifyState(state: string): MaybePromise; -} -/** - * Guard if an object implements the {@link StateStore} interface — most notably, - * `generateState()` and `verifyState(state: string)`. - */ -declare const isStateStore: (obj: unknown) => obj is StateStore; - -interface TokenStore { - getLatestToken(): MaybePromise; - storeToken(token: Token): MaybePromise; -} - -interface RivetError extends Error { - readonly errorCode: ErrorCode; -} - -declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ - readonly ApiResponseError: "zoom_rivet_api_response_error"; - readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; - readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; - readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; - readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; - readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; - readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; - readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; - readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; - readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; - readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; - readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; - readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; - readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; - readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; - readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; - readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; -}[K]>; -declare const ApiResponseError: Constructor; -declare const AwsReceiverRequestError: Constructor; -declare const ClientCredentialsRawResponseError: Constructor; -declare const S2SRawResponseError: Constructor; -declare const CommonHttpRequestError: Constructor; -declare const ReceiverInconsistentStateError: Constructor; -declare const ReceiverOAuthFlowError: Constructor; -declare const HTTPReceiverConstructionError: Constructor; -declare const HTTPReceiverPortNotNumberError: Constructor; -declare const HTTPReceiverRequestError: Constructor; -declare const OAuthInstallerNotInitializedError: Constructor; -declare const OAuthTokenDoesNotExistError: Constructor; -declare const OAuthTokenFetchFailedError: Constructor; -declare const OAuthTokenRawResponseError: Constructor; -declare const OAuthTokenRefreshFailedError: Constructor; -declare const OAuthStateVerificationFailedError: Constructor; -declare const ProductClientConstructionError: Constructor; - declare enum LogLevel { ERROR = "error", WARN = "warn", @@ -142,6 +62,19 @@ declare class ConsoleLogger implements Logger { private static isMoreOrEqualSevere; } +type AllPropsOptional = Exclude<{ + [P in keyof T]: undefined extends T[P] ? True : False; +}[keyof T], undefined> extends True ? True : False; +type Constructor = new (...args: any[]) => T; +type MaybeArray = T | T[]; +type MaybePromise = T | Promise; +type StringIndexed = Record; + +interface TokenStore { + getLatestToken(): MaybePromise; + storeToken(token: Token): MaybePromise; +} + interface AuthOptions { clientId: string; clientSecret: string; @@ -187,6 +120,17 @@ declare abstract class Auth { }>, "grant_type">): Promise; } +interface ClientCredentialsToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} + +interface JwtToken { + token: string; + expirationTimeIso: string; +} + interface S2SAuthToken { accessToken: string; expirationTimeIso: string; @@ -225,12 +169,31 @@ declare class EventManager { protected withContext, Context>(): ContextListener; } +declare enum StatusCode { + OK = 200, + TEMPORARY_REDIRECT = 302, + BAD_REQUEST = 400, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + INTERNAL_SERVER_ERROR = 500 +} +interface ReceiverInitOptions { + eventEmitter?: GenericEventManager | undefined; + interactiveAuth?: InteractiveAuth | undefined; +} +interface Receiver { + canInstall(): true | false; + init(options: ReceiverInitOptions): void; + start(...args: any[]): MaybePromise; + stop(...args: any[]): MaybePromise; +} + interface HttpReceiverOptions extends Partial { endpoints?: MaybeArray | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; port?: number | string | undefined; - webhooksSecretToken: string; - logger?: Logger; - logLevel?: LogLevel; + webhooksSecretToken?: string | undefined; } type SecureServerOptions = { [K in (typeof secureServerOptionKeys)[number]]: ServerOptions[K]; @@ -243,10 +206,15 @@ declare class HttpReceiver implements Receiver { private logger; constructor(options: HttpReceiverOptions); canInstall(): true; + private buildDeletedStateCookieHeader; + private buildStateCookieHeader; + private getRequestCookie; private getServerCreator; private hasEndpoint; private hasSecureOptions; init({ eventEmitter, interactiveAuth }: ReceiverInitOptions): void; + private setResponseCookie; + private areNormalizedUrlsEqual; start(port?: number | string): Promise; stop(): Promise; private writeTemporaryRedirect; @@ -269,6 +237,7 @@ interface WebEndpointOptions { baseUrl?: string | undefined; doubleEncodeUrl?: boolean | undefined; timeout?: number | undefined; + userAgentName?: string | undefined; } type EndpointArguments = (PathSchema extends NoParams ? object : AllPropsOptional extends "t" ? { path?: PathSchema; @@ -290,6 +259,7 @@ declare class WebEndpoints { constructor(options: WebEndpointOptions); protected buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }: BuildEndpointOptions): (_: EndpointArguments) => Promise>; private buildUserAgent; + private getCustomUserAgentName; private getHeaders; private getRequestBody; private isOk; @@ -297,7 +267,7 @@ declare class WebEndpoints { private makeRequest; } -type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & { +type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & Pick & { disableReceiver?: boolean | undefined; logger?: Logger | undefined; logLevel?: LogLevel | undefined; @@ -305,38 +275,68 @@ type CommonClientOptions = GetAuthOptions interface ClientReceiverOptions { receiver: R; } -type ClientConstructorOptions, R extends Receiver> = IsReceiverDisabled extends true ? O : O & (ClientReceiverOptions | HttpReceiverOptions); +type ClientConstructorOptions, R extends Receiver> = (O & { + disableReceiver: true; +}) | (O & (ClientReceiverOptions | HttpReceiverOptions)); type ExtractInstallerOptions = A extends InteractiveAuth ? [ ReturnType ] extends [true] ? WideInstallerOptions : object : object; type ExtractAuthTokenType = A extends Auth ? T : never; -type GenericClientOptions = CommonClientOptions; type GetAuthOptions = AuthOptions> & (A extends S2SAuth ? S2SAuthOptions : object); -type IsReceiverDisabled> = [ - O["disableReceiver"] -] extends [true] ? true : false; type WideInstallerOptions = { installerOptions: InstallerOptions; }; declare abstract class ProductClient, ReceiverType extends Receiver> { private readonly auth; readonly endpoints: EndpointsType; - readonly webEventConsumer: EventProcessorType; + readonly webEventConsumer?: EventProcessorType | undefined; private readonly receiver?; constructor(options: ClientConstructorOptions); protected abstract initAuth(options: OptionsType): AuthType; protected abstract initEndpoints(auth: AuthType, options: OptionsType): EndpointsType; - protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType; + protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType | undefined; private initDefaultReceiver; - start(this: IsReceiverDisabled extends true ? never : this): Promise>; + start(): Promise>; +} + +/** + * {@link StateStore} defines methods for generating and verifying OAuth state. + * + * This interface is implemented internally for the default state store; however, + * it can also be implemented and passed to an OAuth client as well. + */ +interface StateStore { + /** + * Generate a new state string, which is directly appended to the OAuth `state` parameter. + */ + generateState(): MaybePromise; + /** + * Verify that the state received during OAuth callback is valid and not forged. + * + * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. + * + * @param state The state parameter that was received during OAuth callback + */ + verifyState(state: string): MaybePromise; } +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +declare const isStateStore: (obj: unknown) => obj is StateStore; +interface AuthorizationUrlResult { + fullUrl: string; + generatedState: string; +} interface InstallerOptions { directInstall?: boolean | undefined; installPath?: string | undefined; redirectUri: string; redirectUriPath?: string | undefined; stateStore: StateStore | string; + stateCookieName?: string | undefined; + stateCookieMaxAge?: number | undefined; } /** * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication @@ -350,36 +350,87 @@ interface InstallerOptions { */ declare abstract class InteractiveAuth extends Auth { installerOptions?: ReturnType; - getAuthorizationUrl(): Promise; + getAuthorizationUrl(): Promise; getFullRedirectUri(): string; - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }: InstallerOptions): { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }: InstallerOptions): { directInstall: boolean; installPath: string; redirectUri: string; redirectUriPath: string; stateStore: StateStore; + stateCookieName: string; + stateCookieMaxAge: number; }; } -declare enum StatusCode { - OK = 200, - TEMPORARY_REDIRECT = 302, - BAD_REQUEST = 400, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - INTERNAL_SERVER_ERROR = 500 +/** + * Credentials for access token & refresh token, which are used to access Zoom's APIs. + * + * As access token is short-lived (usually a single hour), its expiration time is checked + * first. If it's possible to use the access token, it's used; however, if it has expired + * or is close to expiring, the refresh token should be used to generate a new access token + * before the API call is made. Refresh tokens are generally valid for 90 days. + * + * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} + * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. + * It's likely that this error will be rare, but it _can_ be thrown. + */ +interface OAuthToken { + accessToken: string; + expirationTimeIso: string; + refreshToken: string; + scopes: string[]; } -interface ReceiverInitOptions { - eventEmitter: GenericEventManager; - interactiveAuth?: InteractiveAuth | undefined; +declare class OAuth extends InteractiveAuth { + private assertResponseAccessToken; + private fetchAccessToken; + getToken(): Promise; + initRedirectCode(code: string): Promise; + private mapOAuthToken; + private refreshAccessToken; } -interface Receiver { - canInstall(): true | false; - init(options: ReceiverInitOptions): void; - start(...args: any[]): MaybePromise; - stop(...args: any[]): MaybePromise; + +interface RivetError extends Error { + readonly errorCode: ErrorCode; } +declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ + readonly ApiResponseError: "zoom_rivet_api_response_error"; + readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; + readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; + readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; + readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; + readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; + readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; + readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; + readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; + readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; + readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; + readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; + readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; + readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; + readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; + readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; + readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; +}[K]>; +declare const ApiResponseError: Constructor; +declare const AwsReceiverRequestError: Constructor; +declare const ClientCredentialsRawResponseError: Constructor; +declare const S2SRawResponseError: Constructor; +declare const CommonHttpRequestError: Constructor; +declare const ReceiverInconsistentStateError: Constructor; +declare const ReceiverOAuthFlowError: Constructor; +declare const HTTPReceiverConstructionError: Constructor; +declare const HTTPReceiverPortNotNumberError: Constructor; +declare const HTTPReceiverRequestError: Constructor; +declare const OAuthInstallerNotInitializedError: Constructor; +declare const OAuthTokenDoesNotExistError: Constructor; +declare const OAuthTokenFetchFailedError: Constructor; +declare const OAuthTokenRawResponseError: Constructor; +declare const OAuthTokenRefreshFailedError: Constructor; +declare const OAuthStateVerificationFailedError: Constructor; +declare const ProductClientConstructionError: Constructor; + interface AwsLambdaReceiverOptions { webhooksSecretToken: string; } @@ -402,7 +453,7 @@ type ContactGroupsListContactGroupsResponse = { groups?: { group_id?: string; group_name?: string; - group_privacy?: number; + group_privacy?: 1 | 2 | 3; description?: string; }[]; next_page_token?: string; @@ -410,10 +461,10 @@ type ContactGroupsListContactGroupsResponse = { }; type ContactGroupsCreateContactGroupRequestBody = { group_name?: string; - group_privacy?: number; + group_privacy?: 1 | 2 | 3; description?: string; group_members?: { - type?: number; + type?: 1 | 2; id?: string; }[]; }; @@ -421,7 +472,7 @@ type ContactGroupsCreateContactGroupResponse = { group_id?: string; group_name?: string; total_members?: number; - group_privacy?: number; + group_privacy?: 1 | 2 | 3; description?: string; }; type ContactGroupsGetContactGroupPathParams = { @@ -431,7 +482,7 @@ type ContactGroupsGetContactGroupResponse = { group_id?: string; group_name?: string; total_members?: number; - group_privacy?: number; + group_privacy?: 1 | 2 | 3; description?: string; }; type ContactGroupsDeleteContactGroupPathParams = { @@ -442,7 +493,7 @@ type ContactGroupsUpdateContactGroupPathParams = { }; type ContactGroupsUpdateContactGroupRequestBody = { name?: string; - privacy?: number; + privacy?: 1 | 2 | 3; description?: string; }; type ContactGroupsListContactGroupMembersPathParams = { @@ -453,8 +504,8 @@ type ContactGroupsListContactGroupMembersQueryParams = { next_page_token?: string; }; type ContactGroupsListContactGroupMembersResponse = { - members?: { - type?: number; + group_members?: { + type?: 1 | 2; id?: string; name?: string; }[]; @@ -466,7 +517,7 @@ type ContactGroupsAddContactGroupMembersPathParams = { }; type ContactGroupsAddContactGroupMembersRequestBody = { group_members?: { - type?: number; + type?: 1 | 2; id?: string; }[]; }; @@ -479,24 +530,108 @@ type ContactGroupsRemoveMembersInContactGroupPathParams = { type ContactGroupsRemoveMembersInContactGroupQueryParams = { member_ids: string; }; -type GroupsListGroupsResponse = { - groups?: ({ - id?: string; - } & { - name?: string; - total_members?: number; - })[]; +type DivisionsListDivisionsQueryParams = { + next_page_token?: string; + page_size?: number; +}; +type DivisionsListDivisionsResponse = { + next_page_token?: string; + page_size?: number; total_records?: number; + divisions?: { + division_id?: string; + division_name?: string; + division_description?: string; + is_main_division?: boolean; + total_members?: number; + }[]; }; -type GroupsCreateGroupRequestBody = { - name?: string; +type DivisionsCreateDivisionRequestBody = { + division_name: string; + division_description?: string; }; -type GroupsGetGroupPathParams = { - groupId: string; +type DivisionsCreateDivisionResponse = { + division_id?: string; + division_name?: string; + division_description?: string; }; -type GroupsGetGroupResponse = { - id?: string; - name?: string; +type DivisionsGetDivisionPathParams = { + divisionId: string; +}; +type DivisionsGetDivisionResponse = { + division_id?: string; + division_name?: string; + division_description?: string; + is_main_division?: boolean; +}; +type DivisionsDeleteDivisionPathParams = { + divisionId: string; +}; +type DivisionsUpdateDivisionPathParams = { + divisionId: string; +}; +type DivisionsUpdateDivisionRequestBody = { + division_name?: string; + division_description?: string; +}; +type DivisionsListDivisionMembersPathParams = { + divisionId: string; +}; +type DivisionsListDivisionMembersQueryParams = { + next_page_token?: string; + page_size?: number; +}; +type DivisionsListDivisionMembersResponse = { + next_page_token?: string; + page_size?: number; + total_records?: number; + users?: { + user_id?: string; + user_display_name?: string; + user_email?: string; + }[]; +}; +type DivisionsAssignDivisionPathParams = { + divisionId: string; +}; +type DivisionsAssignDivisionRequestBody = { + users?: { + user_email?: string; + user_id?: string; + }[]; +}; +type DivisionsAssignDivisionResponse = { + added_at?: string; + ids?: string; +}; +type GroupsListGroupsQueryParams = { + page_size?: number; + next_page_token?: string; +}; +type GroupsListGroupsResponse = { + groups?: ({ + id?: string; + } & { + name?: string; + total_members?: number; + })[]; + total_records?: number; + next_page_token?: string; +}; +type GroupsCreateGroupRequestBody = { + name?: string; +}; +type GroupsCreateGroupResponse = { + id?: string; + name?: string; + total_members?: number; +}; +type GroupsGetGroupPathParams = { + groupId: string; +}; +type GroupsGetGroupResponse = { + id?: string; + name?: string; total_members?: number; }; type GroupsDeleteGroupPathParams = { @@ -533,6 +668,10 @@ type GroupsAddGroupAdminsRequestBody = { id?: string; }[]; }; +type GroupsAddGroupAdminsResponse = { + added_at?: string; + ids?: string; +}; type GroupsDeleteGroupAdminPathParams = { groupId: string; userId: string; @@ -669,7 +808,7 @@ type GroupsGetLockedSettingsResponse = { auto_security?: boolean; block_user_domain?: boolean; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; only_authenticated_can_join_from_webclient?: boolean; @@ -798,7 +937,7 @@ type GroupsUpdateLockedSettingsRequestBody = { auto_security?: boolean; block_user_domain?: boolean; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; only_authenticated_can_join_from_webclient?: boolean; @@ -839,6 +978,10 @@ type GroupsAddGroupMembersRequestBody = { id?: string; }[]; }; +type GroupsAddGroupMembersResponse = { + added_at?: string; + ids?: string; +}; type GroupsDeleteGroupMemberPathParams = { groupId: string; memberId: string; @@ -848,14 +991,14 @@ type GroupsUpdateGroupMemberPathParams = { memberId: string; }; type GroupsUpdateGroupMemberRequestBody = { - action: string; + action: "move" | "set_primary"; target_group_id?: string; }; type GroupsGetGroupsSettingsPathParams = { groupId: string; }; type GroupsGetGroupsSettingsQueryParams = { - option?: string; + option?: "meeting_authentication" | "recording_authentication" | "meeting_security"; custom_query_fields?: string; }; type GroupsGetGroupsSettingsResponse = { @@ -885,9 +1028,9 @@ type GroupsGetGroupsSettingsResponse = { alert_guest_join?: boolean; allow_users_to_delete_messages_in_meeting_chat?: boolean; allow_live_streaming?: boolean; - allow_participants_chat_with?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; allow_show_zoom_windows?: boolean; - allow_users_save_chats?: number; + allow_users_save_chats?: 1 | 2 | 3; annotation?: boolean; attendee_on_hold?: boolean; auto_answer?: boolean; @@ -908,7 +1051,7 @@ type GroupsGetGroupsSettingsResponse = { custom_data_center_regions?: boolean; custom_live_streaming_service?: boolean; custom_service_instructions?: string; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; e2e_encryption?: boolean; @@ -924,14 +1067,14 @@ type GroupsGetGroupsSettingsResponse = { language_item_pairList?: { trans_lang_config?: { speak_language?: { - name?: string; - code?: string; + name?: "Chinese (Simplified)" | "Dutch" | "English" | "French" | "German" | "Italian" | "Japanese" | "Korean" | "Portuguese" | "Russian" | "Spanish" | "Ukrainian"; + code?: "zh" | "nl" | "en" | "fr" | "de" | "it" | "ja" | "ko" | "pt" | "ru" | "es" | "uk"; }; translate_to?: { all?: boolean; language_config?: { - name?: string; - code?: string; + name?: "English"; + code?: "en"; }[]; }; }[]; @@ -950,7 +1093,7 @@ type GroupsGetGroupsSettingsResponse = { sign_language_interpretation?: { enable?: boolean; enable_sign_language_interpretation_by_default?: boolean; - languages?: string[]; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; custom_languages?: string[]; }; live_streaming_facebook?: boolean; @@ -964,7 +1107,7 @@ type GroupsGetGroupsSettingsResponse = { third_party_captioning_service?: boolean; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; meeting_survey?: boolean; @@ -1003,19 +1146,19 @@ type GroupsGetGroupsSettingsResponse = { }; waiting_room?: boolean; webinar_chat?: { - allow_attendees_chat_with?: number; + allow_attendees_chat_with?: 1 | 2 | 3; allow_auto_save_local_chat_file?: boolean; - allow_panelists_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; - default_attendees_chat_with?: number; + allow_users_save_chats?: 0 | 1 | 2; + default_attendees_chat_with?: 1 | 2; enable?: boolean; }; webinar_live_streaming?: { custom_service_instructions?: string; enable?: boolean; live_streaming_reminder?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; }; meeting_polling?: { enable?: boolean; @@ -1036,9 +1179,9 @@ type GroupsGetGroupsSettingsResponse = { webinar_question_answer?: boolean; webinar_survey?: boolean; whiteboard?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; workplace_by_facebook?: boolean; }; other_options?: { @@ -1067,12 +1210,12 @@ type GroupsGetGroupsSettingsResponse = { chat_with_sender_email?: boolean; video_file?: boolean; chat_with_direct_message?: boolean; - archive_retention?: number; - action_when_archive_failed?: number; - notification_when_archiving_starts?: string; - play_voice_prompt_when_archiving_starts?: string; + archive_retention?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30; + action_when_archive_failed?: 1 | 2; + notification_when_archiving_starts?: "participants" | "guest"; + play_voice_prompt_when_archiving_starts?: "participants" | "guest" | "none"; }; - type?: number; + type?: 1 | 2 | 3; }; auto_recording?: string; cloud_recording?: boolean; @@ -1120,7 +1263,7 @@ type GroupsGetGroupsSettingsResponse = { personal_meeting?: boolean; pstn_password_protected?: boolean; require_password_for_instant_meetings?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "all" | "jbh_only" | "none"; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; upcoming_meeting_reminder?: boolean; @@ -1150,11 +1293,11 @@ type GroupsGetGroupsSettingsResponse = { chat?: { share_files?: { enable?: boolean; - share_option?: string; + share_option?: "anyone" | "account" | "organization"; }; chat_emojis?: { enable?: boolean; - emojis_option?: string; + emojis_option?: "all" | "selected"; }; record_voice_messages?: boolean; record_video_messages?: boolean; @@ -1176,12 +1319,12 @@ type GroupsGetGroupsSettingsResponse = { allow_users_to_search_others_options?: string; allow_users_to_add_contacts?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; allow_users_to_chat_with_others?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; chat_etiquette_tool?: { @@ -1193,8 +1336,8 @@ type GroupsGetGroupsSettingsResponse = { keywords?: string[]; name?: string; regular_expression?: string; - status?: string; - trigger_action?: number; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; }[]; }; send_data_to_third_party_archiving_service?: { @@ -1212,7 +1355,7 @@ type GroupsGetGroupsSettingsResponse = { domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_same_account" | "enforce_login_with_domains"; visible?: boolean; }[]; meeting_authentication?: boolean; @@ -1222,7 +1365,7 @@ type GroupsGetGroupsSettingsResponse = { domains?: string; id?: string; name?: string; - type?: string; + type?: "internally" | "enforce_login" | "enforce_login_with_domains"; visible?: boolean; }[]; recording_authentication?: boolean; @@ -1240,16 +1383,16 @@ type GroupsGetGroupsSettingsResponse = { keywords?: string[]; name?: string; regular_expression?: string; - status?: string; - trigger_action?: number; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; }[]; }; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -1265,8 +1408,8 @@ type GroupsGetGroupsSettingsResponse = { require_password_for_scheduled_webinar?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - users_who_can_admit_participants_from_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; whitelisted_domains_for_waiting_room?: string; }; webinar_password?: boolean; @@ -1276,7 +1419,7 @@ type GroupsUpdateGroupsSettingsPathParams = { groupId: string; }; type GroupsUpdateGroupsSettingsQueryParams = { - option?: string; + option?: "meeting_authentication" | "recording_authentication" | "meeting_security"; }; type GroupsUpdateGroupsSettingsRequestBody = { audio_conferencing?: { @@ -1305,9 +1448,9 @@ type GroupsUpdateGroupsSettingsRequestBody = { alert_guest_join?: boolean; allow_users_to_delete_messages_in_meeting_chat?: boolean; allow_live_streaming?: boolean; - allow_participants_chat_with?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; allow_show_zoom_windows?: boolean; - allow_users_save_chats?: number; + allow_users_save_chats?: 1 | 2 | 3; annotation?: boolean; attendee_on_hold?: boolean; auto_answer?: boolean; @@ -1328,11 +1471,11 @@ type GroupsUpdateGroupsSettingsRequestBody = { custom_data_center_regions?: boolean; custom_live_streaming_service?: boolean; custom_service_instructions?: string; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; e2e_encryption?: boolean; - entry_exit_chime?: string; + entry_exit_chime?: "host" | "all" | "none"; far_end_camera_control?: boolean; feedback?: boolean; file_transfer?: boolean; @@ -1344,14 +1487,14 @@ type GroupsUpdateGroupsSettingsRequestBody = { language_item_pairList?: { trans_lang_config?: { speak_language?: { - name?: string; - code?: string; + name?: "Chinese (Simplified)" | "Dutch" | "English" | "French" | "German" | "Italian" | "Japanese" | "Korean" | "Portuguese" | "Russian" | "Spanish" | "Ukrainian"; + code?: "zh" | "nl" | "en" | "fr" | "de" | "it" | "ja" | "ko" | "pt" | "ru" | "es" | "uk"; }; translate_to?: { all?: boolean; language_config?: { - name?: string; - code?: string; + name?: "English"; + code?: "en"; }[]; }; }[]; @@ -1382,7 +1525,7 @@ type GroupsUpdateGroupsSettingsRequestBody = { third_party_captioning_service?: boolean; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; meeting_survey?: boolean; @@ -1408,19 +1551,19 @@ type GroupsUpdateGroupsSettingsRequestBody = { virtual_background?: boolean; waiting_room?: boolean; webinar_chat?: { - allow_attendees_chat_with?: number; + allow_attendees_chat_with?: 1 | 2 | 3; allow_auto_save_local_chat_file?: boolean; - allow_panelists_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; - default_attendees_chat_with?: number; + allow_users_save_chats?: 0 | 1 | 2; + default_attendees_chat_with?: 1 | 2; enable?: boolean; }; webinar_live_streaming?: { custom_service_instructions?: string; enable?: boolean; live_streaming_reminder?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; }; meeting_polling?: { enable?: boolean; @@ -1441,9 +1584,9 @@ type GroupsUpdateGroupsSettingsRequestBody = { webinar_question_answer?: boolean; webinar_survey?: boolean; whiteboard?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; workplace_by_facebook?: boolean; }; other_options?: { @@ -1472,7 +1615,7 @@ type GroupsUpdateGroupsSettingsRequestBody = { chat_with_sender_email?: boolean; video_file?: boolean; }; - type?: number; + type?: 1 | 2 | 3; }; auto_recording?: string; cloud_recording?: boolean; @@ -1520,7 +1663,7 @@ type GroupsUpdateGroupsSettingsRequestBody = { pstn_password_protected?: boolean; require_password_for_all_meetings?: boolean; require_password_for_instant_meetings?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "all" | "jbh_only" | "none"; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; upcoming_meeting_reminder?: boolean; @@ -1545,11 +1688,11 @@ type GroupsUpdateGroupsSettingsRequestBody = { chat?: { share_files?: { enable?: boolean; - share_option?: string; + share_option?: "anyone" | "account" | "organization"; }; chat_emojis?: { enable?: boolean; - emojis_option?: string; + emojis_option?: "all" | "selected"; }; record_voice_messages?: boolean; record_video_messages?: boolean; @@ -1571,19 +1714,19 @@ type GroupsUpdateGroupsSettingsRequestBody = { allow_users_to_search_others_options?: string; allow_users_to_add_contacts?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; allow_users_to_chat_with_others?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; chat_etiquette_tool?: { enable?: boolean; policies?: { id?: string; - status?: string; + status?: "activated" | "deactivated"; }[]; }; send_data_to_third_party_archiving_service?: { @@ -1596,22 +1739,22 @@ type GroupsUpdateGroupsSettingsRequestBody = { }; } | ({ authentication_option?: { - action?: string; + action?: "update" | "show" | "hide"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_same_account" | "enforce_login_with_domains"; }; meeting_authentication?: boolean; } | { authentication_option?: { - action?: string; + action?: "update" | "show" | "hide"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "internally" | "enforce_login" | "enforce_login_with_domains"; }; recording_authentication?: boolean; }) | { @@ -1623,15 +1766,15 @@ type GroupsUpdateGroupsSettingsRequestBody = { enable?: boolean; policies?: { id?: string; - status?: string; + status?: "activated" | "deactivated"; }[]; }; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -1647,19 +1790,18 @@ type GroupsUpdateGroupsSettingsRequestBody = { require_password_for_scheduled_webinar?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - users_who_can_admit_participants_from_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; whitelisted_domains_for_waiting_room?: string; }; webinar_password?: boolean; }; }; -type GroupsUpdateGroupsSettingsResponse = object; type GroupsGetGroupsWebinarRegistrationSettingsPathParams = { groupId: string; }; type GroupsGetGroupsWebinarRegistrationSettingsQueryParams = { - type?: string; + type?: "webinar"; }; type GroupsGetGroupsWebinarRegistrationSettingsResponse = { options?: { @@ -1669,14 +1811,14 @@ type GroupsGetGroupsWebinarRegistrationSettingsResponse = { show_social_share_buttons?: boolean; }; questions?: { - field_name?: string; + field_name?: "last_name" | "address" | "city" | "country" | "zip" | "state" | "phone" | "industry" | "org" | "job_title" | "purchasing_time_frame" | "role_in_purchase_process" | "no_of_employees" | "comments"; required?: boolean; selected?: boolean; }[]; - approve_type?: number; + approve_type?: 0 | 1; custom_questions?: { title?: string; - type?: string; + type?: "short" | "single_dropdown" | "single_radio" | "multiple"; required?: boolean; selected?: boolean; answers?: string[]; @@ -1686,7 +1828,7 @@ type GroupsUpdateGroupsWebinarRegistrationSettingsPathParams = { groupId: string; }; type GroupsUpdateGroupsWebinarRegistrationSettingsQueryParams = { - type?: string; + type?: "webinar"; }; type GroupsUpdateGroupsWebinarRegistrationSettingsRequestBody = { options?: { @@ -1696,14 +1838,14 @@ type GroupsUpdateGroupsWebinarRegistrationSettingsRequestBody = { show_social_share_buttons?: boolean; }; questions?: { - field_name?: string; + field_name?: "last_name" | "address" | "city" | "country" | "zip" | "state" | "phone" | "industry" | "org" | "job_title" | "purchasing_time_frame" | "role_in_purchase_process" | "no_of_employees" | "comments"; required?: boolean; selected?: boolean; }[]; - approve_type?: number; + approve_type?: 0 | 1; custom_questions?: { title?: string; - type?: string; + type?: "short" | "single_dropdown" | "single_radio" | "multiple"; required?: boolean; selected?: boolean; answers?: string[]; @@ -1729,13 +1871,13 @@ type GroupsDeleteVirtualBackgroundFilesQueryParams = { file_ids?: string; }; type UsersListUsersQueryParams = { - status?: string; + status?: "active" | "inactive" | "pending"; page_size?: number; role_id?: string; page_number?: string; - include_fields?: string; + include_fields?: "custom_attributes" | "host_key"; next_page_token?: string; - license?: string; + license?: "zoom_workforce_management" | "zoom_compliance_management"; }; type UsersListUsersResponse = { next_page_token?: string; @@ -1756,36 +1898,48 @@ type UsersListUsersResponse = { employee_unique_id?: string; first_name?: string; group_ids?: string[]; + division_ids?: string[]; host_key?: string; id?: string; im_group_ids?: string[]; last_client_version?: string; last_login_time?: string; last_name?: string; - plan_united_type?: string; + plan_united_type?: "1" | "2" | "4" | "8" | "16" | "32" | "64" | "128" | "256" | "512" | "1024" | "2048" | "4096" | "8192" | "16384" | "32768" | "65536" | "131072"; pmi?: number; role_id?: string; - status?: string; + status?: "active" | "inactive" | "pending"; timezone?: string; - type: number; - verified?: number; + type: 1 | 2 | 4 | 99; + verified?: 1 | 0; display_name?: string; + license_info_list?: { + license_type?: "MEETING" | "ZOOM_WORKPLACE_BUNDLE"; + license_option?: 2 | 4 | 8 | 16 | 32 | 64 | 128 | 33554432 | 134217728 | 1073741824 | 536870912 | 268435456 | 4398046511104 | 18014398509481984 | 72057594037927940 | 576460752303423500 | 144115188075855870 | 137438953472 | 1099511627776 | 549755813888 | 274877906944 | 2199023255552 | 4294967296 | 34359738368 | 17179869184 | 8589934592 | 68719476736; + subscription_id?: string; + }[]; }[]; }; type UsersCreateUsersRequestBody = { - action: string; + action: "create" | "autoCreate" | "custCreate" | "ssoCreate"; user_info?: { email: string; first_name?: string; last_name?: string; display_name?: string; password?: string; - type: number; + type: 1 | 2 | 4 | 99; + division_ids?: string[]; feature?: { zoom_phone?: boolean; - zoom_one_type?: number; + zoom_one_type?: 16 | 32 | 64 | 128 | 33554432 | 134217728 | 268435456 | 536870912 | 1073741824 | 4398046511104 | 4294967296 | 8589934592 | 17179869184 | 34359738368 | 68719476736 | 137438953472 | 274877906944 | 549755813888 | 1099511627776 | 2199023255552 | 18014398509481984 | 72057594037927940 | 144115188075855870 | 576460752303423500; }; - plan_united_type?: string; + plan_united_type?: "1" | "2" | "4" | "8" | "16" | "32" | "64" | "128" | "256" | "512" | "1024" | "2048" | "4096" | "8192" | "16384" | "32768" | "65536" | "131072"; + license_info_list?: { + license_type: "MEETING" | "ZOOM_WORKPLACE_BUNDLE"; + license_option: 2 | 4 | 8 | 16 | 32 | 64 | 128 | 33554432 | 134217728 | 1073741824 | 536870912 | 268435456 | 4398046511104 | 18014398509481984 | 72057594037927940 | 576460752303423500 | 144115188075855870 | 137438953472 | 1099511627776 | 549755813888 | 274877906944 | 2199023255552 | 4294967296 | 34359738368 | 17179869184 | 8589934592 | 68719476736; + subscription_id: string; + }[]; }; }; type UsersCreateUsersResponse = { @@ -1793,7 +1947,7 @@ type UsersCreateUsersResponse = { first_name?: string; id?: string; last_name?: string; - type?: number; + type?: 1 | 2 | 4 | 99; }; type UsersCheckUserEmailQueryParams = { email: string; @@ -1801,19 +1955,25 @@ type UsersCheckUserEmailQueryParams = { type UsersCheckUserEmailResponse = { existed_email?: boolean; }; -type UsersBulkUpdateFeaturesForUserRequestBody = { - feature_type: string; +type UsersBulkUpdateFeaturesForUsersRequestBody = { + feature_type: "user_type" | "concurrent_meeting" | "large_meeting" | "webinar" | "zoom_events" | "zoom_whiteboard" | "plan_united_type" | "zoom_one_type" | "zoom_iq_for_sales" | "zoom_revenue_accelerator" | "zoom_clips_plus"; feature_value: string; + subscription_id?: string; users: { id?: string; email?: string; }[]; + license_info_list?: { + license_type: "MEETING" | "ZOOM_WORKPLACE_BUNDLE"; + license_option: 2 | 4 | 8 | 16 | 32 | 64 | 128 | 33554432 | 134217728 | 1073741824 | 536870912 | 268435456 | 4398046511104 | 18014398509481984 | 72057594037927940 | 576460752303423500 | 144115188075855870 | 137438953472 | 1099511627776 | 549755813888 | 274877906944 | 2199023255552 | 4294967296 | 34359738368 | 17179869184 | 8589934592 | 68719476736; + subscription_id: string; + }[]; }; -type UsersBulkUpdateFeaturesForUserResponse = { +type UsersBulkUpdateFeaturesForUsersResponse = { success_user_ids?: string[]; fail_details?: { user_ids?: string[]; - reason?: string; + reason?: "Users not found" | "Have upcoming events" | "Unpaid user" | "Not enough seats" | "Can't update for Zoom One users" | "Can't update for free users" | "Can't update for Zoom United users" | "Can't update for Zoom Room users" | "Not allowed to add basic users" | "Can't update for non-SSO users" | "No need to update"; }[]; }; type UsersGetUsersZAKResponse = { @@ -1838,7 +1998,7 @@ type UsersGetUserPathParams = { userId: string; }; type UsersGetUserQueryParams = { - login_type?: number; + login_type?: 0 | 1 | 11 | 21 | 23 | 24 | 27 | 97 | 98 | 99 | 100 | 101; encrypted_email?: boolean; search_by_unique_id?: boolean; }; @@ -1855,7 +2015,7 @@ type UsersGetUserResponse = { pmi?: number; role_name?: string; timezone?: string; - type: number; + type: 1 | 2 | 4 | 99; use_pmi?: boolean; display_name?: string; } & { @@ -1872,12 +2032,14 @@ type UsersGetUserResponse = { }[]; employee_unique_id?: string; group_ids?: string[]; + division_ids?: string[]; im_group_ids?: string[]; jid?: string; job_title?: string; + cost_center?: string; language?: string; location?: string; - login_types?: number[]; + login_types?: (0 | 1 | 11 | 21 | 23 | 24 | 27 | 97 | 98 | 99 | 100 | 101)[]; manager?: string; personal_meeting_url?: string; phone_country?: string; @@ -1885,39 +2047,48 @@ type UsersGetUserResponse = { phone_numbers?: { code?: string; country?: string; - label?: string; + label?: "Mobile" | "Office" | "Home" | "Fax"; number?: string; verified?: boolean; }[]; pic_url?: string; - plan_united_type?: string; + plan_united_type?: "1" | "2" | "4" | "8" | "16" | "32" | "64" | "128" | "256" | "512" | "1024" | "2048" | "4096" | "8192" | "16384" | "32768" | "65536" | "131072"; pronouns?: string; - pronouns_option?: number; + pronouns_option?: 1 | 2 | 3; role_id?: string; - status?: string; + status?: "pending" | "active" | "inactive"; use_pmi?: boolean; vanity_url?: string; verified?: number; cluster?: string; zoom_one_type?: number; + license_info_list?: { + license_type?: "MEETING" | "ZOOM_WORKPLACE_BUNDLE"; + license_option?: 2 | 4 | 8 | 16 | 32 | 64 | 128 | 33554432 | 134217728 | 1073741824 | 536870912 | 268435456 | 4398046511104 | 18014398509481984 | 72057594037927940 | 576460752303423500 | 144115188075855870 | 137438953472 | 1099511627776 | 549755813888 | 274877906944 | 2199023255552 | 4294967296 | 34359738368 | 17179869184 | 8589934592 | 68719476736; + subscription_id?: string; + }[]; }; type UsersDeleteUserPathParams = { userId: string; }; type UsersDeleteUserQueryParams = { encrypted_email?: boolean; - action?: string; + action?: "disassociate" | "delete"; transfer_email?: string; transfer_meeting?: boolean; transfer_webinar?: boolean; transfer_recording?: boolean; transfer_whiteboard?: boolean; + transfer_clipfiles?: boolean; + transfer_notes?: boolean; + transfer_visitors?: boolean; + transfer_docs?: boolean; }; type UsersUpdateUserPathParams = { userId: string; }; type UsersUpdateUserQueryParams = { - login_type?: number; + login_type?: 0 | 1 | 11 | 21 | 23 | 24 | 27 | 97 | 98 | 99 | 100 | 101; remove_tsp_credentials?: boolean; }; type UsersUpdateUserRequestBody = { @@ -1931,8 +2102,10 @@ type UsersUpdateUserRequestBody = { dept?: string; first_name?: string; group_id?: string; + division_ids?: string[]; host_key?: string; job_title?: string; + cost_center?: string; language?: string; last_name?: string; location?: string; @@ -1942,24 +2115,29 @@ type UsersUpdateUserRequestBody = { phone_numbers?: { code?: string; country?: string; - label?: string; + label?: "Mobile" | "Office" | "Home" | "Fax"; number?: string; }[]; pmi?: number; pronouns?: string; - pronouns_option?: number; + pronouns_option?: 1 | 2 | 3; timezone?: string; - type?: number; + type?: 1 | 2 | 4 | 99; use_pmi?: boolean; vanity_name?: string; display_name?: string; - zoom_one_type?: number; - plan_united_type?: string; + zoom_one_type?: 0 | 16 | 32 | 64 | 128 | 33554432 | 134217728 | 268435456 | 536870912 | 1073741824 | 4398046511104 | 4294967296 | 8589934592 | 17179869184 | 34359738368 | 68719476736 | 137438953472 | 274877906944 | 549755813888 | 1099511627776 | 2199023255552 | 18014398509481984 | 72057594037927940 | 144115188075855870 | 576460752303423500; + plan_united_type?: "1" | "2" | "4" | "8" | "16" | "32" | "64" | "128" | "256" | "512" | "1024" | "2048" | "4096" | "8192" | "16384" | "32768" | "65536" | "131072" | "none"; feature?: { zoom_phone?: boolean; }; about_me?: string; linkedin_url?: string; + license_info_list?: { + license_type: "MEETING" | "ZOOM_WORKPLACE_BUNDLE"; + license_option: 2 | 4 | 8 | 16 | 32 | 64 | 128 | 33554432 | 134217728 | 1073741824 | 536870912 | 268435456 | 4398046511104 | 18014398509481984 | 72057594037927940 | 576460752303423500 | 144115188075855870 | 137438953472 | 1099511627776 | 549755813888 | 274877906944 | 2199023255552 | 4294967296 | 34359738368 | 17179869184 | 8589934592 | 68719476736; + subscription_id: string; + }[]; }; type UsersListUserAssistantsPathParams = { userId: string; @@ -1968,6 +2146,7 @@ type UsersListUserAssistantsResponse = { assistants?: { email?: string; id?: string; + can_manage_host_private_event?: boolean; }[]; }; type UsersAddAssistantsPathParams = { @@ -1977,6 +2156,7 @@ type UsersAddAssistantsRequestBody = { assistants?: { email?: string; id?: string; + can_manage_host_private_event?: boolean; }[]; }; type UsersAddAssistantsResponse = { @@ -2000,7 +2180,7 @@ type UsersListUsersCollaborationDevicesResponse = { device_name?: string; room_name?: string; room_user_id?: string; - status?: string; + status?: "Online" | "Offline"; }[]; }; type UsersGetCollaborationDeviceDetailPathParams = { @@ -2012,7 +2192,7 @@ type UsersGetCollaborationDeviceDetailResponse = { device_name?: string; room_name?: string; room_user_id?: string; - status?: string; + status?: "Online" | "Offline"; }; type UsersUpdateUsersEmailPathParams = { userId: string; @@ -2029,13 +2209,13 @@ type UsersGetMeetingTemplateDetailResponse = { name?: string; settings?: { in_meeting?: { - entry_exit_chime?: string; + entry_exit_chime?: "host" | "all" | "none"; feedback?: boolean; polling?: boolean; post_meeting_feedback?: boolean; screen_sharing?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; disable_screen_sharing_for_host_meetings?: boolean; annotation?: boolean; whiteboard?: boolean; @@ -2057,7 +2237,7 @@ type UsersGetMeetingTemplateDetailResponse = { }; }; recording?: { - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; }; schedule_meeting?: { host_video?: boolean; @@ -2095,19 +2275,19 @@ type UsersGetUserPresenceStatusPathParams = { userId: string; }; type UsersGetUserPresenceStatusResponse = { - status: string; + status: "Do_No_Disturb"; end_time: string; remaining_time: number; } | { - status: string; + status: "Away" | "Do_Not_Disturb" | "Available" | "In_Calendar_Event" | "Presenting" | "In_A_Zoom_Meeting" | "On_A_Call" | "Out_of_Office" | "Busy"; }; type UsersUpdateUsersPresenceStatusPathParams = { userId: string; }; type UsersUpdateUsersPresenceStatusRequestBody = { - status: string; + status: "Away" | "Available" | "In_Calendar_Event" | "Presenting" | "In_A_Zoom_Meeting" | "On_A_Call" | "Out_of_Office" | "Busy"; } | { - status: string; + status: "Do_No_Disturb"; duration?: number; }; type UsersListUserSchedulersPathParams = { @@ -2131,8 +2311,8 @@ type UsersGetUserSettingsPathParams = { userId: string; }; type UsersGetUserSettingsQueryParams = { - login_type?: number; - option?: string; + login_type?: 0 | 1 | 11 | 21 | 23 | 24 | 27 | 97 | 98 | 99 | 100 | 101; + option?: "meeting_authentication" | "recording_authentication" | "meeting_security"; custom_query_fields?: string; }; type UsersGetUserSettingsResponse = { @@ -2160,7 +2340,7 @@ type UsersGetUserSettingsResponse = { }; feature?: { cn_meeting?: boolean; - concurrent_meeting?: string; + concurrent_meeting?: "Basic" | "Plus" | "None"; in_meeting?: boolean; large_meeting?: boolean; large_meeting_capacity?: number; @@ -2168,11 +2348,11 @@ type UsersGetUserSettingsResponse = { webinar?: boolean; webinar_capacity?: number; zoom_events?: boolean; - zoom_events_capacity?: number; + zoom_events_capacity?: 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000; zoom_events_unlimited?: boolean; - zoom_events_unlimited_capacities?: number[]; + zoom_events_unlimited_capacities?: (100 | 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000)[]; zoom_sessions_unlimited?: boolean; - zoom_sessions_unlimited_capacities?: number[]; + zoom_sessions_unlimited_capacities?: (100 | 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000)[]; zoom_events_pay_per_attendee?: boolean; zoom_sessions_pay_per_attendee?: boolean; zoom_phone?: boolean; @@ -2189,6 +2369,12 @@ type UsersGetUserSettingsResponse = { zoom_clips_plus?: boolean; zoom_mail_calendar?: boolean; zoom_compliance_management?: boolean; + zoom_docs?: boolean; + license_info_list?: { + license_type?: "ZOOM_WHITEBOARD" | "ZOOM_TRANSLATED_CAPTIONS" | "ZOOM_SCHEDULER" | "ZOOM_CLIPS" | "ZOOM_VISITOR_MANAGEMENT" | "ZOOM_CMK" | "ZOOM_DOCS" | "ZOOM_REVENUE_ACCELERATOR" | "ZOOM_COMPLIANCE_MANAGEMENT" | "ZOOM_WORKFORCE_MANAGEMENT" | "ZOOM_QUALITY_MANAGEMENT" | "ZOOM_HEALTHCARE_CLINICAL_NOTES"; + license_option?: 1 | 2 | 512 | 2048 | 65536 | 131072 | 2147483648 | 549755813888 | 1099511627776 | 2199023255552 | 8796093022208 | 17592186044416 | 281474976710656 | 4503599627370496; + subscription_id?: string; + }[]; }; in_meeting?: { allow_host_to_enable_focus_mode?: boolean; @@ -2196,8 +2382,8 @@ type UsersGetUserSettingsResponse = { allow_live_streaming?: boolean; post_meeting_feedback?: boolean; whiteboard?: boolean; - allow_participants_chat_with?: number; - allow_users_save_chats?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; + allow_users_save_chats?: 1 | 2 | 3; annotation?: boolean; attendee_on_hold?: boolean; attention_mode_focus_mode?: boolean; @@ -2218,11 +2404,11 @@ type UsersGetUserSettingsResponse = { custom_data_center_regions?: boolean; custom_live_streaming_service?: boolean; custom_service_instructions?: string; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; e2e_encryption?: boolean; - entry_exit_chime?: string; + entry_exit_chime?: "host" | "all" | "none"; far_end_camera_control?: boolean; feedback?: boolean; file_transfer?: boolean; @@ -2236,12 +2422,12 @@ type UsersGetUserSettingsResponse = { allow_participants_to_speak_in_listening_channel?: boolean; allow_up_to_25_custom_languages_when_scheduling_meetings?: boolean; enable?: boolean; - languages?: string[]; + languages?: ("English" | "Chinese" | "Japanese" | "German" | "French" | "Russian" | "Portuguese" | "Spanish" | "Korean")[]; }; sign_language_interpretation?: { enable?: boolean; enable_sign_language_interpretation_by_default?: boolean; - languages?: string[]; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; custom_languages?: string[]; }; live_streaming_facebook?: boolean; @@ -2255,7 +2441,7 @@ type UsersGetUserSettingsResponse = { third_party_captioning_service?: boolean; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; meeting_survey?: boolean; @@ -2271,7 +2457,7 @@ type UsersGetUserSettingsResponse = { show_a_join_from_your_browser_link?: boolean; show_meeting_control_toolbar?: boolean; slide_control?: boolean; - unchecked_data_center_regions?: string[]; + unchecked_data_center_regions?: ("EU" | "HK" | "AU" | "IN" | "TY" | "CN" | "US" | "CA" | "DE" | "NL" | "LA")[]; virtual_background?: boolean; virtual_background_settings?: { allow_upload_custom?: boolean; @@ -2287,19 +2473,19 @@ type UsersGetUserSettingsResponse = { }; waiting_room?: boolean; webinar_chat?: { - allow_attendees_chat_with?: number; + allow_attendees_chat_with?: 1 | 2 | 3; allow_auto_save_local_chat_file?: boolean; - allow_panelists_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; - default_attendees_chat_with?: number; + allow_users_save_chats?: 0 | 1 | 2; + default_attendees_chat_with?: 1 | 2; enable?: boolean; }; webinar_live_streaming?: { custom_service_instructions?: string; enable?: boolean; live_streaming_reminder?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; }; meeting_polling?: { advanced_polls?: boolean; @@ -2316,12 +2502,22 @@ type UsersGetUserSettingsResponse = { enable?: boolean; }; webinar_survey?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; workplace_by_facebook?: boolean; transfer_meetings_between_devices?: boolean; allow_show_zoom_windows?: boolean; + meeting_summary_with_ai_companion?: { + enable?: boolean; + auto_enable?: boolean; + who_will_receive_summary?: 1 | 2 | 3 | 4; + }; + ai_companion_questions?: { + enable?: boolean; + auto_enable?: boolean; + who_can_ask_questions?: 1 | 2 | 3 | 4 | 5; + }; }; profile?: { recording_storage_location?: { @@ -2333,7 +2529,7 @@ type UsersGetUserSettingsResponse = { ask_host_to_confirm_disclaimer?: boolean; ask_participants_to_consent_disclaimer?: boolean; auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; record_files_separately?: { active_speaker?: boolean; gallery_view?: boolean; @@ -2346,7 +2542,7 @@ type UsersGetUserSettingsResponse = { save_panelist_chat?: boolean; save_poll_results?: boolean; save_close_caption?: boolean; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; cloud_recording?: boolean; host_pause_stop_recording?: boolean; ip_address_access_control?: { @@ -2375,14 +2571,14 @@ type UsersGetUserSettingsResponse = { show_timestamp?: boolean; }; schedule_meeting?: { - audio_type?: string; + audio_type?: "both" | "telephony" | "voip" | "thirdParty"; default_password_for_scheduled_meetings?: string; embed_password_in_join_link?: boolean; force_pmi_jbh_password?: boolean; host_video?: boolean; join_before_host?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -2396,7 +2592,7 @@ type UsersGetUserSettingsResponse = { pmi_password?: string; pstn_password_protected?: boolean; require_password_for_instant_meetings?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "jbh_only" | "all" | "none"; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; use_pmi_for_instant_meetings?: boolean; @@ -2405,6 +2601,8 @@ type UsersGetUserSettingsResponse = { enable?: boolean; can_add_external_users?: boolean; auto_add_invited_external_users?: boolean; + support_instant_meetings?: boolean; + support_scheduled_meetings?: boolean; }; }; telephony?: { @@ -2434,7 +2632,7 @@ type UsersGetUserSettingsResponse = { domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_domains" | "enforce_login_with_same_account"; visible?: boolean; }[]; meeting_authentication?: boolean; @@ -2445,7 +2643,7 @@ type UsersGetUserSettingsResponse = { domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_domains" | "internally"; visible?: boolean; }[]; recording_authentication?: boolean; @@ -2458,7 +2656,7 @@ type UsersGetUserSettingsResponse = { domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_same_account" | "enforce_login_with_domains"; visible?: boolean; }[]; meeting_authentication?: boolean; @@ -2468,7 +2666,7 @@ type UsersGetUserSettingsResponse = { domains?: string; id?: string; name?: string; - type?: string; + type?: "internally" | "enforce_login" | "enforce_login_with_domains"; visible?: boolean; }[]; recording_authentication?: boolean; @@ -2478,11 +2676,11 @@ type UsersGetUserSettingsResponse = { block_user_domain?: boolean; block_user_domain_list?: string[]; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -2498,8 +2696,8 @@ type UsersGetUserSettingsResponse = { require_password_for_scheduled_webinar?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - users_who_can_admit_participants_from_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; whitelisted_domains_for_waiting_room?: string; }; webinar_password?: boolean; @@ -2509,7 +2707,7 @@ type UsersUpdateUserSettingsPathParams = { userId: string; }; type UsersUpdateUserSettingsQueryParams = { - option?: string; + option?: "meeting_authentication" | "recording_authentication" | "meeting_security"; }; type UsersUpdateUserSettingsRequestBody = { email_notification?: { @@ -2522,18 +2720,18 @@ type UsersUpdateUserSettingsRequestBody = { schedule_for_reminder?: boolean; }; feature?: { - concurrent_meeting?: string; + concurrent_meeting?: "Basic" | "Plus" | "None"; large_meeting?: boolean; large_meeting_capacity?: number; meeting_capacity?: number; webinar?: boolean; - webinar_capacity?: number; + webinar_capacity?: 100 | 500 | 501 | 1000 | 1001 | 3000 | 5000 | 10000; zoom_events?: boolean; - zoom_events_capacity?: number; + zoom_events_capacity?: 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000; zoom_events_unlimited?: boolean; - zoom_events_unlimited_capacities?: number[]; + zoom_events_unlimited_capacities?: (100 | 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000)[]; zoom_sessions_unlimited?: boolean; - zoom_sessions_unlimited_capacities?: number[]; + zoom_sessions_unlimited_capacities?: (100 | 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000)[]; zoom_events_pay_per_attendee?: boolean; zoom_sessions_pay_per_attendee?: boolean; zoom_phone?: boolean; @@ -2550,6 +2748,12 @@ type UsersUpdateUserSettingsRequestBody = { zoom_clips_plus?: boolean; zoom_mail_calendar?: boolean; zoom_compliance_management?: boolean; + zoom_docs?: boolean; + license_info_list?: { + license_type: "ZOOM_WHITEBOARD" | "ZOOM_TRANSLATED_CAPTIONS" | "ZOOM_SCHEDULER" | "ZOOM_CLIPS" | "ZOOM_VISITOR_MANAGEMENT" | "ZOOM_CMK" | "ZOOM_DOCS" | "ZOOM_REVENUE_ACCELERATOR" | "ZOOM_COMPLIANCE_MANAGEMENT" | "ZOOM_WORKFORCE_MANAGEMENT" | "ZOOM_QUALITY_MANAGEMENT" | "ZOOM_HEALTHCARE_CLINICAL_NOTES"; + license_option: 1 | 2 | 512 | 2048 | 65536 | 131072 | 2147483648 | 549755813888 | 1099511627776 | 2199023255552 | 8796093022208 | 17592186044416 | 281474976710656 | 4503599627370496; + subscription_id?: string; + }[]; }; in_meeting?: { allow_host_to_enable_focus_mode?: boolean; @@ -2557,8 +2761,8 @@ type UsersUpdateUserSettingsRequestBody = { allow_live_streaming?: boolean; post_meeting_feedback?: boolean; whiteboard?: boolean; - allow_participants_chat_with?: number; - allow_users_save_chats?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; + allow_users_save_chats?: 1 | 2 | 3; annotation?: boolean; attendee_on_hold?: boolean; attention_mode_focus_mode?: boolean; @@ -2579,11 +2783,11 @@ type UsersUpdateUserSettingsRequestBody = { custom_data_center_regions?: boolean; custom_live_streaming_service?: boolean; custom_service_instructions?: string; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; e2e_encryption?: boolean; - entry_exit_chime?: string; + entry_exit_chime?: "host" | "all" | "none"; far_end_camera_control?: boolean; feedback?: boolean; file_transfer?: boolean; @@ -2601,7 +2805,7 @@ type UsersUpdateUserSettingsRequestBody = { sign_language_interpretation?: { enable?: boolean; enable_sign_language_interpretation_by_default?: boolean; - languages?: string[]; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; custom_languages?: string[]; }; live_streaming_facebook?: boolean; @@ -2615,7 +2819,7 @@ type UsersUpdateUserSettingsRequestBody = { third_party_captioning_service?: boolean; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; meeting_survey?: boolean; @@ -2646,19 +2850,19 @@ type UsersUpdateUserSettingsRequestBody = { }; waiting_room?: boolean; webinar_chat?: { - allow_attendees_chat_with?: number; + allow_attendees_chat_with?: 1 | 2 | 3; allow_auto_save_local_chat_file?: boolean; - allow_panelists_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; - default_attendees_chat_with?: number; + allow_users_save_chats?: 0 | 1 | 2; + default_attendees_chat_with?: 1 | 2; enable?: boolean; }; webinar_live_streaming?: { custom_service_instructions?: string; enable?: boolean; live_streaming_reminder?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; }; meeting_polling?: { advanced_polls?: boolean; @@ -2675,9 +2879,9 @@ type UsersUpdateUserSettingsRequestBody = { enable?: boolean; }; webinar_survey?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; workplace_by_facebook?: boolean; auto_answer?: boolean; allow_show_zoom_windows?: boolean; @@ -2692,7 +2896,7 @@ type UsersUpdateUserSettingsRequestBody = { ask_host_to_confirm_disclaimer?: boolean; ask_participants_to_consent_disclaimer?: boolean; auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; record_files_separately?: { active_speaker?: boolean; gallery_view?: boolean; @@ -2705,7 +2909,7 @@ type UsersUpdateUserSettingsRequestBody = { save_panelist_chat?: boolean; save_poll_results?: boolean; save_close_caption?: boolean; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; cloud_recording?: boolean; host_pause_stop_recording?: boolean; ip_address_access_control?: { @@ -2734,14 +2938,14 @@ type UsersUpdateUserSettingsRequestBody = { show_timestamp?: boolean; }; schedule_meeting?: { - audio_type?: string; + audio_type?: "both" | "telephony" | "voip" | "thirdParty"; default_password_for_scheduled_meetings?: string; embed_password_in_join_link?: boolean; force_pmi_jbh_password?: boolean; host_video?: boolean; join_before_host?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -2755,7 +2959,7 @@ type UsersUpdateUserSettingsRequestBody = { pmi_password?: string; pstn_password_protected?: boolean; require_password_for_instant_meetings?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "jbh_only" | "all" | "none"; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; use_pmi_for_instant_meetings?: boolean; @@ -2776,22 +2980,22 @@ type UsersUpdateUserSettingsRequestBody = { }; } | ({ authentication_option?: { - action?: string; + action?: "update" | "show" | "hide"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_same_account" | "enforce_login_with_domains"; }; meeting_authentication?: boolean; } | { authentication_option?: { - action?: string; + action?: "update" | "show" | "hide"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "internally" | "enforce_login" | "enforce_login_with_domains"; }; recording_authentication?: boolean; }) | { @@ -2800,11 +3004,11 @@ type UsersUpdateUserSettingsRequestBody = { block_user_domain?: boolean; block_user_domain_list?: string[]; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -2820,8 +3024,8 @@ type UsersUpdateUserSettingsRequestBody = { require_password_for_scheduled_webinar?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - users_who_can_admit_participants_from_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; whitelisted_domains_for_waiting_room?: string; }; webinar_password?: boolean; @@ -2838,7 +3042,7 @@ type UsersUploadVirtualBackgroundFilesResponse = { is_default?: boolean; name?: string; size?: number; - type?: string; + type?: "image" | "video"; }; type UsersDeleteVirtualBackgroundFilesPathParams = { userId: string; @@ -2850,14 +3054,15 @@ type UsersUpdateUserStatusPathParams = { userId: string; }; type UsersUpdateUserStatusRequestBody = { - action: string; + action: "activate" | "deactivate" | "clock_in" | "clock_out"; }; type UsersGetUsersTokenPathParams = { userId: string; }; type UsersGetUsersTokenQueryParams = { - type?: string; + type?: "token" | "zak" | "onbehalf"; ttl?: number; + meeting_id?: string; }; type UsersGetUsersTokenResponse = { token?: string; @@ -2900,11 +3105,42 @@ declare class UsersEndpoints extends WebEndpoints { query: ContactGroupsRemoveMembersInContactGroupQueryParams; }) => Promise>; }; + readonly divisions: { + listDivisions: (_: object & { + query?: DivisionsListDivisionsQueryParams; + }) => Promise>; + createDivision: (_: object & { + body: DivisionsCreateDivisionRequestBody; + }) => Promise>; + getDivision: (_: { + path: DivisionsGetDivisionPathParams; + } & object) => Promise>; + deleteDivision: (_: { + path: DivisionsDeleteDivisionPathParams; + } & object) => Promise>; + updateDivision: (_: { + path: DivisionsUpdateDivisionPathParams; + } & { + body?: DivisionsUpdateDivisionRequestBody; + } & object) => Promise>; + listDivisionMembers: (_: { + path: DivisionsListDivisionMembersPathParams; + } & object & { + query?: DivisionsListDivisionMembersQueryParams; + }) => Promise>; + assignDivision: (_: { + path: DivisionsAssignDivisionPathParams; + } & { + body?: DivisionsAssignDivisionRequestBody; + } & object) => Promise>; + }; readonly groups: { - listGroups: (_: object) => Promise>; + listGroups: (_: object & { + query?: GroupsListGroupsQueryParams; + }) => Promise>; createGroup: (_: object & { body?: GroupsCreateGroupRequestBody; - }) => Promise>; + }) => Promise>; getGroup: (_: { path: GroupsGetGroupPathParams; } & object) => Promise>; @@ -2925,7 +3161,7 @@ declare class UsersEndpoints extends WebEndpoints { path: GroupsAddGroupAdminsPathParams; } & { body?: GroupsAddGroupAdminsRequestBody; - } & object) => Promise>; + } & object) => Promise>; deleteGroupAdmin: (_: { path: GroupsDeleteGroupAdminPathParams; } & object) => Promise>; @@ -3055,7 +3291,7 @@ declare class UsersEndpoints extends WebEndpoints { auto_security?: boolean; block_user_domain?: boolean; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; only_authenticated_can_join_from_webclient?: boolean; @@ -3077,7 +3313,7 @@ declare class UsersEndpoints extends WebEndpoints { path: GroupsAddGroupMembersPathParams; } & { body?: GroupsAddGroupMembersRequestBody; - } & object) => Promise>; + } & object) => Promise>; deleteGroupMember: (_: { path: GroupsDeleteGroupMemberPathParams; } & object) => Promise>; @@ -3121,9 +3357,9 @@ declare class UsersEndpoints extends WebEndpoints { alert_guest_join?: boolean; allow_users_to_delete_messages_in_meeting_chat?: boolean; allow_live_streaming?: boolean; - allow_participants_chat_with?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; allow_show_zoom_windows?: boolean; - allow_users_save_chats?: number; + allow_users_save_chats?: 1 | 2 | 3; annotation?: boolean; attendee_on_hold?: boolean; auto_answer?: boolean; @@ -3144,11 +3380,11 @@ declare class UsersEndpoints extends WebEndpoints { custom_data_center_regions?: boolean; custom_live_streaming_service?: boolean; custom_service_instructions?: string; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; e2e_encryption?: boolean; - entry_exit_chime?: string; + entry_exit_chime?: "host" | "all" | "none"; far_end_camera_control?: boolean; feedback?: boolean; file_transfer?: boolean; @@ -3160,14 +3396,14 @@ declare class UsersEndpoints extends WebEndpoints { language_item_pairList?: { trans_lang_config?: { speak_language?: { - name?: string; - code?: string; + name?: "Chinese (Simplified)" | "Dutch" | "English" | "French" | "German" | "Italian" | "Japanese" | "Korean" | "Portuguese" | "Russian" | "Spanish" | "Ukrainian"; + code?: "zh" | "nl" | "en" | "fr" | "de" | "it" | "ja" | "ko" | "pt" | "ru" | "es" | "uk"; }; translate_to?: { all?: boolean; language_config?: { - name?: string; - code?: string; + name?: "English"; + code?: "en"; }[]; }; }[]; @@ -3198,7 +3434,7 @@ declare class UsersEndpoints extends WebEndpoints { third_party_captioning_service?: boolean; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; meeting_survey?: boolean; @@ -3224,19 +3460,19 @@ declare class UsersEndpoints extends WebEndpoints { virtual_background?: boolean; waiting_room?: boolean; webinar_chat?: { - allow_attendees_chat_with?: number; + allow_attendees_chat_with?: 1 | 2 | 3; allow_auto_save_local_chat_file?: boolean; - allow_panelists_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; - default_attendees_chat_with?: number; + allow_users_save_chats?: 0 | 1 | 2; + default_attendees_chat_with?: 1 | 2; enable?: boolean; }; webinar_live_streaming?: { custom_service_instructions?: string; enable?: boolean; live_streaming_reminder?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; }; meeting_polling?: { enable?: boolean; @@ -3257,9 +3493,9 @@ declare class UsersEndpoints extends WebEndpoints { webinar_question_answer?: boolean; webinar_survey?: boolean; whiteboard?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; workplace_by_facebook?: boolean; }; other_options?: { @@ -3288,7 +3524,7 @@ declare class UsersEndpoints extends WebEndpoints { chat_with_sender_email?: boolean; video_file?: boolean; }; - type?: number; + type?: 1 | 2 | 3; }; auto_recording?: string; cloud_recording?: boolean; @@ -3336,7 +3572,7 @@ declare class UsersEndpoints extends WebEndpoints { pstn_password_protected?: boolean; require_password_for_all_meetings?: boolean; require_password_for_instant_meetings?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "all" | "jbh_only" | "none"; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; upcoming_meeting_reminder?: boolean; @@ -3361,11 +3597,11 @@ declare class UsersEndpoints extends WebEndpoints { chat?: { share_files?: { enable?: boolean; - share_option?: string; + share_option?: "anyone" | "account" | "organization"; }; chat_emojis?: { enable?: boolean; - emojis_option?: string; + emojis_option?: "all" | "selected"; }; record_voice_messages?: boolean; record_video_messages?: boolean; @@ -3387,19 +3623,19 @@ declare class UsersEndpoints extends WebEndpoints { allow_users_to_search_others_options?: string; allow_users_to_add_contacts?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; allow_users_to_chat_with_others?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; chat_etiquette_tool?: { enable?: boolean; policies?: { id?: string; - status?: string; + status?: "activated" | "deactivated"; }[]; }; send_data_to_third_party_archiving_service?: { @@ -3414,24 +3650,24 @@ declare class UsersEndpoints extends WebEndpoints { } | { body?: { authentication_option?: { - action?: string; + action?: "update" | "show" | "hide"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_same_account" | "enforce_login_with_domains"; }; meeting_authentication?: boolean; }; } | { body?: { authentication_option?: { - action?: string; + action?: "update" | "show" | "hide"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "internally" | "enforce_login" | "enforce_login_with_domains"; }; recording_authentication?: boolean; }; @@ -3445,15 +3681,15 @@ declare class UsersEndpoints extends WebEndpoints { enable?: boolean; policies?: { id?: string; - status?: string; + status?: "activated" | "deactivated"; }[]; }; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -3469,8 +3705,8 @@ declare class UsersEndpoints extends WebEndpoints { require_password_for_scheduled_webinar?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - users_who_can_admit_participants_from_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; whitelisted_domains_for_waiting_room?: string; }; webinar_password?: boolean; @@ -3478,7 +3714,7 @@ declare class UsersEndpoints extends WebEndpoints { }; }) & { query?: GroupsUpdateGroupsSettingsQueryParams; - })) => Promise>; + })) => Promise>; getGroupsWebinarRegistrationSettings: (_: { path: GroupsGetGroupsWebinarRegistrationSettingsPathParams; } & object & { @@ -3512,9 +3748,9 @@ declare class UsersEndpoints extends WebEndpoints { checkUserEmail: (_: object & { query: UsersCheckUserEmailQueryParams; }) => Promise>; - bulkUpdateFeaturesForUser: (_: object & { - body: UsersBulkUpdateFeaturesForUserRequestBody; - }) => Promise>; + bulkUpdateFeaturesForUsers: (_: object & { + body: UsersBulkUpdateFeaturesForUsersRequestBody; + }) => Promise>; getUsersZAK: (_: object) => Promise>; getUserSummary: (_: object) => Promise>; checkUsersPMRoom: (_: object & { @@ -3588,11 +3824,11 @@ declare class UsersEndpoints extends WebEndpoints { path: UsersUpdateUsersPresenceStatusPathParams; } & (({ body: { - status: string; + status: "Away" | "Available" | "In_Calendar_Event" | "Presenting" | "In_A_Zoom_Meeting" | "On_A_Call" | "Out_of_Office" | "Busy"; }; } | { body: { - status: string; + status: "Do_No_Disturb"; duration?: number; }; }) & object)) => Promise>; @@ -3624,18 +3860,18 @@ declare class UsersEndpoints extends WebEndpoints { schedule_for_reminder?: boolean; }; feature?: { - concurrent_meeting?: string; + concurrent_meeting?: "Basic" | "Plus" | "None"; large_meeting?: boolean; large_meeting_capacity?: number; meeting_capacity?: number; webinar?: boolean; - webinar_capacity?: number; + webinar_capacity?: 100 | 500 | 501 | 1000 | 1001 | 3000 | 5000 | 10000; zoom_events?: boolean; - zoom_events_capacity?: number; + zoom_events_capacity?: 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000; zoom_events_unlimited?: boolean; - zoom_events_unlimited_capacities?: number[]; + zoom_events_unlimited_capacities?: (100 | 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000)[]; zoom_sessions_unlimited?: boolean; - zoom_sessions_unlimited_capacities?: number[]; + zoom_sessions_unlimited_capacities?: (100 | 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000)[]; zoom_events_pay_per_attendee?: boolean; zoom_sessions_pay_per_attendee?: boolean; zoom_phone?: boolean; @@ -3652,6 +3888,12 @@ declare class UsersEndpoints extends WebEndpoints { zoom_clips_plus?: boolean; zoom_mail_calendar?: boolean; zoom_compliance_management?: boolean; + zoom_docs?: boolean; + license_info_list?: { + license_type: "ZOOM_WHITEBOARD" | "ZOOM_TRANSLATED_CAPTIONS" | "ZOOM_SCHEDULER" | "ZOOM_CLIPS" | "ZOOM_VISITOR_MANAGEMENT" | "ZOOM_CMK" | "ZOOM_DOCS" | "ZOOM_REVENUE_ACCELERATOR" | "ZOOM_COMPLIANCE_MANAGEMENT" | "ZOOM_WORKFORCE_MANAGEMENT" | "ZOOM_QUALITY_MANAGEMENT" | "ZOOM_HEALTHCARE_CLINICAL_NOTES"; + license_option: 1 | 2 | 512 | 2048 | 65536 | 131072 | 2147483648 | 549755813888 | 1099511627776 | 2199023255552 | 8796093022208 | 17592186044416 | 281474976710656 | 4503599627370496; + subscription_id?: string; + }[]; }; in_meeting?: { allow_host_to_enable_focus_mode?: boolean; @@ -3659,8 +3901,8 @@ declare class UsersEndpoints extends WebEndpoints { allow_live_streaming?: boolean; post_meeting_feedback?: boolean; whiteboard?: boolean; - allow_participants_chat_with?: number; - allow_users_save_chats?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; + allow_users_save_chats?: 1 | 2 | 3; annotation?: boolean; attendee_on_hold?: boolean; attention_mode_focus_mode?: boolean; @@ -3681,11 +3923,11 @@ declare class UsersEndpoints extends WebEndpoints { custom_data_center_regions?: boolean; custom_live_streaming_service?: boolean; custom_service_instructions?: string; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; e2e_encryption?: boolean; - entry_exit_chime?: string; + entry_exit_chime?: "host" | "all" | "none"; far_end_camera_control?: boolean; feedback?: boolean; file_transfer?: boolean; @@ -3703,7 +3945,7 @@ declare class UsersEndpoints extends WebEndpoints { sign_language_interpretation?: { enable?: boolean; enable_sign_language_interpretation_by_default?: boolean; - languages?: string[]; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; custom_languages?: string[]; }; live_streaming_facebook?: boolean; @@ -3717,7 +3959,7 @@ declare class UsersEndpoints extends WebEndpoints { third_party_captioning_service?: boolean; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; meeting_survey?: boolean; @@ -3748,19 +3990,19 @@ declare class UsersEndpoints extends WebEndpoints { }; waiting_room?: boolean; webinar_chat?: { - allow_attendees_chat_with?: number; + allow_attendees_chat_with?: 1 | 2 | 3; allow_auto_save_local_chat_file?: boolean; - allow_panelists_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; - default_attendees_chat_with?: number; + allow_users_save_chats?: 0 | 1 | 2; + default_attendees_chat_with?: 1 | 2; enable?: boolean; }; webinar_live_streaming?: { custom_service_instructions?: string; enable?: boolean; live_streaming_reminder?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; }; meeting_polling?: { advanced_polls?: boolean; @@ -3777,9 +4019,9 @@ declare class UsersEndpoints extends WebEndpoints { enable?: boolean; }; webinar_survey?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; workplace_by_facebook?: boolean; auto_answer?: boolean; allow_show_zoom_windows?: boolean; @@ -3794,7 +4036,7 @@ declare class UsersEndpoints extends WebEndpoints { ask_host_to_confirm_disclaimer?: boolean; ask_participants_to_consent_disclaimer?: boolean; auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; record_files_separately?: { active_speaker?: boolean; gallery_view?: boolean; @@ -3807,7 +4049,7 @@ declare class UsersEndpoints extends WebEndpoints { save_panelist_chat?: boolean; save_poll_results?: boolean; save_close_caption?: boolean; - auto_recording?: string; + auto_recording?: "local" | "cloud" | "none"; cloud_recording?: boolean; host_pause_stop_recording?: boolean; ip_address_access_control?: { @@ -3836,14 +4078,14 @@ declare class UsersEndpoints extends WebEndpoints { show_timestamp?: boolean; }; schedule_meeting?: { - audio_type?: string; + audio_type?: "both" | "telephony" | "voip" | "thirdParty"; default_password_for_scheduled_meetings?: string; embed_password_in_join_link?: boolean; force_pmi_jbh_password?: boolean; host_video?: boolean; join_before_host?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -3857,7 +4099,7 @@ declare class UsersEndpoints extends WebEndpoints { pmi_password?: string; pstn_password_protected?: boolean; require_password_for_instant_meetings?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "jbh_only" | "all" | "none"; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; use_pmi_for_instant_meetings?: boolean; @@ -3880,24 +4122,24 @@ declare class UsersEndpoints extends WebEndpoints { } | { body?: { authentication_option?: { - action?: string; + action?: "update" | "show" | "hide"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "enforce_login" | "enforce_login_with_same_account" | "enforce_login_with_domains"; }; meeting_authentication?: boolean; }; } | { body?: { authentication_option?: { - action?: string; + action?: "update" | "show" | "hide"; default_option?: boolean; domains?: string; id?: string; name?: string; - type?: string; + type?: "internally" | "enforce_login" | "enforce_login_with_domains"; }; recording_authentication?: boolean; }; @@ -3908,11 +4150,11 @@ declare class UsersEndpoints extends WebEndpoints { block_user_domain?: boolean; block_user_domain_list?: string[]; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -3928,8 +4170,8 @@ declare class UsersEndpoints extends WebEndpoints { require_password_for_scheduled_webinar?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - users_who_can_admit_participants_from_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; whitelisted_domains_for_waiting_room?: string; }; webinar_password?: boolean; @@ -4098,7 +4340,7 @@ type GroupLockSettingsUpdatedEvent = Event<"group.lock_settings_updated"> & { auto_security?: boolean; block_user_domain?: boolean; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; only_authenticated_can_join_from_webclient?: boolean; @@ -4110,6 +4352,127 @@ type GroupLockSettingsUpdatedEvent = Event<"group.lock_settings_updated"> & { }; }; time_stamp?: number; + old_object?: { + id: string; + settings: { + audio_conferencing?: { + toll_free_and_fee_based_toll_call?: boolean; + }; + email_notification?: { + alternative_host_reminder?: boolean; + cancel_meeting_reminder?: boolean; + cloud_recording_available_reminder?: boolean; + jbh_reminder?: boolean; + schedule_for_reminder?: boolean; + }; + in_meeting?: { + alert_guest_join?: boolean; + allow_users_to_delete_messages_in_meeting_chat?: boolean; + allow_live_streaming?: boolean; + allow_show_zoom_windows?: boolean; + annotation?: boolean; + auto_answer?: boolean; + auto_generated_captions?: boolean; + auto_saving_chat?: boolean; + breakout_room?: boolean; + chat?: boolean; + closed_caption?: boolean; + co_host?: boolean; + custom_data_center_regions?: boolean; + disable_screen_sharing_for_host_meetings?: boolean; + disable_screen_sharing_for_in_meeting_guests?: boolean; + e2e_encryption?: boolean; + entry_exit_chime?: boolean; + far_end_camera_control?: boolean; + feedback?: boolean; + file_transfer?: boolean; + full_transcript?: boolean; + group_hd?: boolean; + language_interpretation?: boolean; + sign_language_interpretation?: boolean; + manual_captions?: boolean; + meeting_reactions?: boolean; + webinar_reactions?: boolean; + meeting_survey?: boolean; + non_verbal_feedback?: boolean; + original_audio?: boolean; + polling?: boolean; + post_meeting_feedback?: boolean; + private_chat?: boolean; + remote_control?: boolean; + remote_support?: boolean; + request_permission_to_unmute?: boolean; + save_caption?: boolean; + save_captions?: boolean; + screen_sharing?: boolean; + sending_default_email_invites?: boolean; + show_a_join_from_your_browser_link?: boolean; + show_browser_join_link?: boolean; + show_meeting_control_toolbar?: boolean; + slide_control?: boolean; + stereo_audio?: boolean; + use_html_format_email?: boolean; + virtual_background?: boolean; + waiting_room?: boolean; + webinar_chat?: boolean; + webinar_live_streaming?: boolean; + webinar_polling?: boolean; + webinar_question_answer?: boolean; + meeting_question_answer?: boolean; + webinar_survey?: boolean; + whiteboard?: boolean; + }; + other_options?: { + blur_snapshot?: boolean; + }; + recording?: { + account_user_access_recording?: boolean; + auto_delete_cmr?: boolean; + auto_recording?: boolean; + cloud_recording?: boolean; + cloud_recording_download?: boolean; + host_delete_cloud_recording?: boolean; + ip_address_access_control?: boolean; + local_recording?: boolean; + prevent_host_access_recording?: boolean; + recording_authentication?: boolean; + archive?: boolean; + }; + schedule_meeting?: { + audio_type?: boolean; + embed_password_in_join_link?: boolean; + force_pmi_jbh_password?: boolean; + host_video?: boolean; + join_before_host?: boolean; + meeting_authentication?: boolean; + mute_upon_entry?: boolean; + participant_video?: boolean; + pstn_password_protected?: boolean; + require_password_for_instant_meetings?: boolean; + require_password_for_pmi_meetings?: boolean; + require_password_for_scheduling_new_meetings?: boolean; + upcoming_meeting_reminder?: boolean; + }; + telephony?: { + telephony_regions?: boolean; + third_party_audio?: boolean; + }; + meeting_security?: { + approved_or_denied_countries_or_regions?: boolean; + auto_security?: boolean; + block_user_domain?: boolean; + embed_password_in_join_link?: boolean; + encryption_type?: "enhanced_encryption" | "e2ee"; + end_to_end_encrypted_meetings?: boolean; + meeting_password?: boolean; + only_authenticated_can_join_from_webclient?: boolean; + phone_password?: boolean; + pmi_password?: boolean; + waiting_room?: boolean; + webinar_password?: boolean; + }; + }; + }; }; }; type GroupAdminDeletedEvent = Event<"group.admin_deleted"> & { @@ -4158,7 +4521,7 @@ type UserDeletedEvent = Event<"user.deleted"> & { first_name?: string; last_name?: string; email: string; - type: number; + type: 1 | 2; }; }; }; @@ -4174,7 +4537,7 @@ type UserDeactivatedEvent = Event<"user.deactivated"> & { first_name: string; last_name: string; email: string; - type: number; + type: 1 | 2; }; }; }; @@ -4200,12 +4563,12 @@ type UserSettingsUpdatedEvent = Event<"user.settings_updated"> & { force_pmi_jbh_password?: string; use_pmi_for_scheduled_meetings?: boolean; pstn_password_protected?: string; - jbh_time?: number; + jbh_time?: 0 | 5 | 10 | 15; personal_meeting?: boolean; default_password_for_scheduled_meetings?: boolean; require_password_for_instant_meetings?: boolean; mute_upon_entry?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "jbh_only" | "all" | "none"; use_pmi_for_instant_meetings?: boolean; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; @@ -4218,8 +4581,8 @@ type UserSettingsUpdatedEvent = Event<"user.settings_updated"> & { post_meeting_feedback?: boolean; whiteboard?: boolean; allow_users_to_delete_messages_in_meeting_chat?: boolean; - allow_participants_chat_with?: number; - allow_users_save_chats?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; + allow_users_save_chats?: 1 | 2 | 3; private_chat?: boolean; attention_mode_focus_mode?: boolean; allow_host_to_enable_focus_mode?: boolean; @@ -4245,9 +4608,9 @@ type UserSettingsUpdatedEvent = Event<"user.settings_updated"> & { breakout_room_schedule?: boolean; remote_support?: boolean; screen_sharing?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; closed_caption?: boolean; group_hd?: boolean; far_end_camera_control?: boolean; @@ -4267,14 +4630,14 @@ type UserSettingsUpdatedEvent = Event<"user.settings_updated"> & { }[]; }; custom_data_center_regions?: boolean; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; language_interpretation?: { enable?: boolean; languages?: string[]; custom_languages?: string[]; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; show_a_join_from_your_browser_link?: boolean; @@ -4288,23 +4651,23 @@ type UserSettingsUpdatedEvent = Event<"user.settings_updated"> & { custom_service_instructions?: string; webinar_live_streaming?: { enable?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; custom_service_instructions?: string; live_streaming_reminder?: boolean; }; webinar_chat?: { enable?: boolean; - allow_panelists_chat_with?: number; - allow_attendees_chat_with?: number; - default_attendees_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; + allow_attendees_chat_with?: 1 | 2 | 3; + default_attendees_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; + allow_users_save_chats?: 0 | 1 | 2; allow_auto_save_local_chat_file?: boolean; }; sign_language_interpretation?: { enable?: boolean; enable_sign_language_interpretation_by_default?: boolean; - languages?: string[]; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; custom_languages?: string[]; }; meeting_question_answer?: boolean; @@ -4341,144 +4704,739 @@ type UserSettingsUpdatedEvent = Event<"user.settings_updated"> & { }; recording?: { local_recording?: boolean; - cloud_recording?: boolean; - record_speaker_view?: boolean; - record_gallery_view?: boolean; + cloud_recording?: boolean; + record_speaker_view?: boolean; + record_gallery_view?: boolean; + record_audio_file?: boolean; + save_chat_text?: boolean; + show_timestamp?: boolean; + recording_audio_transcript?: boolean; + auto_recording?: "local" | "cloud" | "none"; + auto_delete_cmr?: boolean; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; + record_files_separately?: { + active_speaker?: boolean; + gallery_view?: boolean; + shared_screen?: boolean; + }; + display_participant_name?: boolean; + recording_thumbnails?: boolean; + optimize_recording_for_3rd_party_video_editor?: boolean; + recording_highlight?: boolean; + save_panelist_chat?: boolean; + save_poll_results?: boolean; + save_close_caption?: boolean; + record_audio_file_each_participant?: boolean; + host_pause_stop_recording?: boolean; + recording_disclaimer?: boolean; + ask_participants_to_consent_disclaimer?: boolean; + ask_host_to_confirm_disclaimer?: boolean; + recording_password_requirement?: { + length?: number; + have_letter?: boolean; + have_number?: boolean; + have_special_character?: boolean; + only_allow_numeric?: boolean; + }; + ip_address_access_control?: { + enable?: boolean; + ip_addresses_or_ranges?: string; + }; + }; + telephony?: { + third_party_audio?: boolean; + audio_conference_info?: string; + show_international_numbers_link?: boolean; + telephony_regions?: { + allowed_values?: string[]; + selection_values?: string; + }; + }; + feature?: { + meeting_capacity?: number; + large_meeting?: boolean; + large_meeting_capacity?: 500 | 1000; + webinar?: boolean; + webinar_capacity?: 100 | 500 | 1000 | 3000 | 5000 | 10000; + zoom_events?: boolean; + zoom_events_capacity?: 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000; + cn_meeting?: boolean; + in_meeting?: boolean; + zoom_phone?: boolean; + concurrent_meeting?: "Basic" | "Plus" | "None"; + }; + meeting_security?: { + auto_security?: boolean; + waiting_room?: boolean; + waiting_room_settings?: { + participants_to_place_in_waiting_room?: 0 | 1 | 2; + whitelisted_domains_for_waiting_room?: string; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; + }; + meeting_password?: boolean; + require_password_for_scheduled_meeting?: boolean; + pmi_password?: boolean; + phone_password?: boolean; + webinar_password?: boolean; + require_password_for_scheduled_webinar?: boolean; + meeting_password_requirement?: { + length?: number; + have_letter?: boolean; + have_number?: boolean; + have_special_character?: boolean; + only_allow_numeric?: boolean; + have_upper_and_lower_characters?: boolean; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; + weak_enhance_detection?: boolean; + }; + embed_password_in_join_link?: boolean; + end_to_end_encrypted_meetings?: boolean; + encryption_type?: "enhanced_encryption" | "e2ee"; + block_user_domain?: boolean; + only_authenticated_can_join_from_webclient?: boolean; + block_user_domain_list?: string[]; + }; + tsp?: { + call_out?: boolean; + call_out_countries?: string[]; + show_international_numbers_link?: boolean; + display_toll_free_numbers?: boolean; + }; + }; + }; + old_object?: { + id?: string; + settings?: { + schedule_meeting?: { + host_video?: boolean; + participants_video?: boolean; + audio_type?: string; + join_before_host?: boolean; + force_pmi_jbh_password?: string; + use_pmi_for_scheduled_meetings?: boolean; + pstn_password_protected?: string; + jbh_time?: 0 | 5 | 10 | 15; + personal_meeting?: boolean; + default_password_for_scheduled_meetings?: boolean; + require_password_for_instant_meetings?: boolean; + mute_upon_entry?: boolean; + require_password_for_pmi_meetings?: "jbh_only" | "all" | "none"; + use_pmi_for_instant_meetings?: boolean; + require_password_for_scheduled_meetings?: boolean; + require_password_for_scheduling_new_meetings?: boolean; + pmi_password?: string; + upcoming_meeting_reminder?: boolean; + }; + in_meeting?: { + e2e_encryption?: boolean; + chat?: boolean; + post_meeting_feedback?: boolean; + whiteboard?: boolean; + allow_users_to_delete_messages_in_meeting_chat?: boolean; + allow_participants_chat_with?: 1 | 2 | 3 | 4; + allow_users_save_chats?: 1 | 2 | 3; + private_chat?: boolean; + attention_mode_focus_mode?: boolean; + allow_host_to_enable_focus_mode?: boolean; + auto_saving_chat?: boolean; + entry_exit_chime?: string; + record_play_voice?: boolean; + file_transfer?: boolean; + feedback?: boolean; + co_host?: boolean; + polling?: boolean; + meeting_polling?: { + enable?: boolean; + advanced_polls?: boolean; + require_answers_to_be_anonymous?: boolean; + allow_alternative_host_to_add_edit?: boolean; + allow_host_to_upload_image?: boolean; + }; + attendee_on_hold?: boolean; + annotation?: boolean; + remote_control?: boolean; + non_verbal_feedback?: boolean; + breakout_room?: boolean; + breakout_room_schedule?: boolean; + remote_support?: boolean; + screen_sharing?: boolean; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; + closed_caption?: boolean; + group_hd?: boolean; + far_end_camera_control?: boolean; + share_dual_camera?: boolean; + waiting_room?: boolean; + virtual_background?: boolean; + virtual_background_settings?: { + enable?: boolean; + allow_videos?: boolean; + allow_upload_custom?: boolean; + files?: { + id?: string; + name?: string; + type?: string; + is_default?: boolean; + size?: number; + }[]; + }; + custom_data_center_regions?: boolean; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; + language_interpretation?: { + enable?: boolean; + languages?: string[]; + custom_languages?: string[]; + }; + meeting_reactions?: boolean; + meeting_reactions_emojis?: "all" | "selected"; + allow_host_panelists_to_use_audible_clap?: boolean; + webinar_reactions?: boolean; + show_a_join_from_your_browser_link?: boolean; + join_from_mobile?: boolean; + join_from_desktop?: boolean; + allow_live_streaming?: boolean; + live_streaming_facebook?: boolean; + workplace_by_facebook?: boolean; + live_streaming_youtube?: boolean; + custom_live_streaming_service?: boolean; + custom_service_instructions?: string; + webinar_live_streaming?: { + enable?: boolean; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; + custom_service_instructions?: string; + live_streaming_reminder?: boolean; + }; + webinar_chat?: { + enable?: boolean; + allow_panelists_chat_with?: 1 | 2; + allow_attendees_chat_with?: 1 | 2 | 3; + default_attendees_chat_with?: 1 | 2; + allow_panelists_send_direct_message?: boolean; + allow_users_save_chats?: 0 | 1 | 2; + allow_auto_save_local_chat_file?: boolean; + }; + sign_language_interpretation?: { + enable?: boolean; + enable_sign_language_interpretation_by_default?: boolean; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; + custom_languages?: string[]; + }; + meeting_question_answer?: boolean; + closed_captioning?: { + enable?: boolean; + third_party_captioning_service?: boolean; + auto_transcribing?: boolean; + view_full_transcript?: boolean; + save_caption?: boolean; + }; + slide_control?: boolean; + meeting_survey?: boolean; + webinar_polling?: { + enable?: boolean; + advanced_polls?: boolean; + require_answers_to_be_anonymous?: boolean; + allow_alternative_host_to_add_edit?: boolean; + allow_host_to_upload_image?: boolean; + }; + webinar_survey?: boolean; + disable_screen_sharing_for_host_meetings?: boolean; + disable_screen_sharing_for_in_meeting_guests?: boolean; + auto_answer?: boolean; + allow_show_zoom_windows?: boolean; + }; + email_notification?: { + cloud_recording_available_reminder?: boolean; + recording_available_reminder_schedulers?: boolean; + recording_available_reminder_alternative_hosts?: boolean; + jbh_reminder?: boolean; + cancel_meeting_reminder?: boolean; + alternative_host_reminder?: boolean; + schedule_for_reminder?: boolean; + }; + recording?: { + local_recording?: boolean; + cloud_recording?: boolean; + record_speaker_view?: boolean; + record_gallery_view?: boolean; + record_audio_file?: boolean; + save_chat_text?: boolean; + show_timestamp?: boolean; + recording_audio_transcript?: boolean; + auto_recording?: "local" | "cloud" | "none"; + auto_delete_cmr?: boolean; + auto_delete_cmr_days?: 30 | 60 | 90 | 120; + record_files_separately?: { + active_speaker?: boolean; + gallery_view?: boolean; + shared_screen?: boolean; + }; + display_participant_name?: boolean; + recording_thumbnails?: boolean; + optimize_recording_for_3rd_party_video_editor?: boolean; + recording_highlight?: boolean; + save_panelist_chat?: boolean; + save_poll_results?: boolean; + save_close_caption?: boolean; + record_audio_file_each_participant?: boolean; + host_pause_stop_recording?: boolean; + recording_disclaimer?: boolean; + ask_participants_to_consent_disclaimer?: boolean; + ask_host_to_confirm_disclaimer?: boolean; + recording_password_requirement?: { + length?: number; + have_letter?: boolean; + have_number?: boolean; + have_special_character?: boolean; + only_allow_numeric?: boolean; + }; + ip_address_access_control?: { + enable?: boolean; + ip_addresses_or_ranges?: string; + }; + }; + telephony?: { + third_party_audio?: boolean; + audio_conference_info?: string; + show_international_numbers_link?: boolean; + telephony_regions?: { + allowed_values?: string[]; + selection_values?: string; + }; + }; + feature?: { + meeting_capacity?: number; + large_meeting?: boolean; + large_meeting_capacity?: 500 | 1000; + webinar?: boolean; + webinar_capacity?: 100 | 500 | 1000 | 3000 | 5000 | 10000; + zoom_events?: boolean; + zoom_events_capacity?: 500 | 1000 | 3000 | 5000 | 10000 | 20000 | 30000 | 50000; + cn_meeting?: boolean; + in_meeting?: boolean; + zoom_phone?: boolean; + concurrent_meeting?: "Basic" | "Plus" | "None"; + }; + meeting_security?: { + auto_security?: boolean; + waiting_room?: boolean; + waiting_room_settings?: { + participants_to_place_in_waiting_room?: 0 | 1 | 2; + whitelisted_domains_for_waiting_room?: string; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; + }; + meeting_password?: boolean; + require_password_for_scheduled_meeting?: boolean; + pmi_password?: boolean; + phone_password?: boolean; + webinar_password?: boolean; + require_password_for_scheduled_webinar?: boolean; + meeting_password_requirement?: { + length?: number; + have_letter?: boolean; + have_number?: boolean; + have_special_character?: boolean; + only_allow_numeric?: boolean; + have_upper_and_lower_characters?: boolean; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; + weak_enhance_detection?: boolean; + }; + embed_password_in_join_link?: boolean; + end_to_end_encrypted_meetings?: boolean; + encryption_type?: "enhanced_encryption" | "e2ee"; + block_user_domain?: boolean; + only_authenticated_can_join_from_webclient?: boolean; + block_user_domain_list?: string[]; + }; + tsp?: { + call_out?: boolean; + call_out_countries?: string[]; + show_international_numbers_link?: boolean; + display_toll_free_numbers?: boolean; + }; + }; + }; + }; +}; +type UserInvitationAcceptedEvent = Event<"user.invitation_accepted"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + id: string; + first_name?: string; + last_name?: string; + email: string; + type: 1 | 2; + }; + }; +}; +type GroupCreatedEvent = Event<"group.created"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id: string; + time_stamp?: number; + object: { + id: string; + name: string; + }; + }; +}; +type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + operator: string; + operator_id: string; + object: { + id: string; + settings: { + audio_conferencing?: { + toll_free_and_fee_based_toll_call?: { + allow_webinar_attendees_dial?: boolean; + enable?: boolean; + numbers?: { + code?: string; + country_code?: string; + country_name?: string; + display_number?: string; + number?: string; + }[]; + }; + }; + email_notification?: { + alternative_host_reminder?: boolean; + cancel_meeting_reminder?: boolean; + cloud_recording_available_reminder?: boolean; + jbh_reminder?: boolean; + recording_available_reminder_alternative_hosts?: boolean; + recording_available_reminder_schedulers?: boolean; + schedule_for_reminder?: boolean; + }; + in_meeting?: { + alert_guest_join?: boolean; + allow_users_to_delete_messages_in_meeting_chat?: boolean; + allow_live_streaming?: boolean; + allow_participants_chat_with?: 1 | 2 | 3 | 4; + allow_show_zoom_windows?: boolean; + allow_users_save_chats?: 1 | 2 | 3; + annotation?: boolean; + auto_answer?: boolean; + auto_saving_chat?: boolean; + breakout_room?: boolean; + breakout_room_schedule?: boolean; + chat?: boolean; + meeting_question_answer?: boolean; + closed_caption?: boolean; + closed_captioning?: { + auto_transcribing?: boolean; + enable?: boolean; + save_caption?: boolean; + third_party_captioning_service?: boolean; + view_full_transcript?: boolean; + }; + co_host?: boolean; + custom_data_center_regions?: boolean; + custom_live_streaming_service?: boolean; + custom_service_instructions?: string; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; + disable_screen_sharing_for_host_meetings?: boolean; + disable_screen_sharing_for_in_meeting_guests?: boolean; + e2e_encryption?: boolean; + entry_exit_chime?: boolean; + far_end_camera_control?: boolean; + feedback?: boolean; + file_transfer?: boolean; + group_hd?: boolean; + join_from_desktop?: boolean; + join_from_mobile?: boolean; + language_interpretation?: { + custom_languages?: string[]; + enable_language_interpretation_by_default?: boolean; + allow_participants_to_speak_in_listening_channel?: boolean; + allow_up_to_25_custom_languages_when_scheduling_meetings?: boolean; + enable?: boolean; + languages?: string[]; + }; + sign_language_interpretation?: { + enable?: boolean; + enable_sign_language_interpretation_by_default?: boolean; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; + custom_languages?: string[]; + }; + live_streaming_facebook?: boolean; + live_streaming_youtube?: boolean; + manual_captioning?: { + allow_to_type?: boolean; + auto_generated_captions?: boolean; + full_transcript?: boolean; + manual_captions?: boolean; + save_captions?: boolean; + third_party_captioning_service?: boolean; + }; + meeting_polling?: { + advanced_polls?: boolean; + require_answers_to_be_anonymous?: boolean; + allow_alternative_host_to_add_edit?: boolean; + manage_saved_polls_and_quizzes?: boolean; + allow_host_to_upload_image?: boolean; + enable?: boolean; + }; + meeting_reactions?: boolean; + meeting_reactions_emojis?: "all" | "selected"; + allow_host_panelists_to_use_audible_clap?: boolean; + webinar_reactions?: boolean; + meeting_survey?: boolean; + non_verbal_feedback?: boolean; + only_host_view_device_list?: boolean; + original_audio?: boolean; + polling?: boolean; + post_meeting_feedback?: boolean; + private_chat?: boolean; + record_play_own_voice?: boolean; + remote_control?: boolean; + remote_support?: boolean; + request_permission_to_unmute?: boolean; + screen_sharing?: boolean; + sending_default_email_invites?: boolean; + show_a_join_from_your_browser_link?: boolean; + show_device_list?: boolean; + show_meeting_control_toolbar?: boolean; + slide_control?: boolean; + stereo_audio?: boolean; + unchecked_data_center_regions?: string[]; + use_html_format_email?: boolean; + virtual_background?: boolean; + virtual_background_settings?: { + allow_upload_custom?: boolean; + allow_videos?: boolean; + enable?: boolean; + files?: { + id?: string; + is_default?: boolean; + name?: string; + size?: number; + type?: string; + }[]; + }; + waiting_room?: boolean; + webinar_chat?: { + allow_attendees_chat_with?: 1 | 2 | 3; + allow_auto_save_local_chat_file?: boolean; + allow_panelists_chat_with?: 1 | 2; + allow_panelists_send_direct_message?: boolean; + allow_users_save_chats?: 0 | 1 | 2; + default_attendees_chat_with?: 1 | 2; + enable?: boolean; + }; + webinar_live_streaming?: { + custom_service_instructions?: string; + enable?: boolean; + live_streaming_reminder?: boolean; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; + }; + webinar_polling?: { + advanced_polls?: boolean; + require_answers_to_be_anonymous?: boolean; + allow_alternative_host_to_add_edit?: boolean; + manage_saved_polls_and_quizzes?: boolean; + allow_host_to_upload_image?: boolean; + enable?: boolean; + }; + webinar_question_answer?: boolean; + webinar_survey?: boolean; + whiteboard?: boolean; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; + workplace_by_facebook?: boolean; + }; + other_options?: { + allow_users_contact_support_via_chat?: boolean; + blur_snapshot?: boolean; + webinar_registration_options?: { + allow_host_to_enable_social_share_buttons?: boolean; + }; + }; + profile?: { + recording_storage_location?: { + allowed_values?: string[]; + value?: string; + }; + }; + recording?: { + account_user_access_recording?: boolean; + archive?: { + enable?: boolean; + settings?: { + audio_file?: boolean; + cc_transcript_file?: boolean; + chat_file?: boolean; + chat_with_sender_email?: boolean; + video_file?: boolean; + }; + type?: 1 | 2 | 3; + }; + auto_recording?: boolean; + cloud_recording?: boolean; + cloud_recording_download?: boolean; + cloud_recording_download_host?: boolean; + display_participant_name?: boolean; + host_delete_cloud_recording?: boolean; + ip_address_access_control?: { + enable?: boolean; + ip_addresses_or_ranges?: string; + }; + local_recording?: boolean; + optimize_recording_for_3rd_party_video_editor?: boolean; + prevent_host_access_recording?: boolean; record_audio_file?: boolean; - save_chat_text?: boolean; - show_timestamp?: boolean; - recording_audio_transcript?: boolean; - auto_recording?: string; - auto_delete_cmr?: boolean; - auto_delete_cmr_days?: number; + record_audio_file_each_participant?: boolean; record_files_separately?: { active_speaker?: boolean; gallery_view?: boolean; shared_screen?: boolean; }; - display_participant_name?: boolean; - recording_thumbnails?: boolean; - optimize_recording_for_3rd_party_video_editor?: boolean; + record_gallery_view?: boolean; + record_speaker_view?: boolean; + recording_audio_transcript?: boolean; recording_highlight?: boolean; + smart_recording?: { + create_recording_highlights?: boolean; + create_smart_chapters?: boolean; + create_next_steps?: boolean; + }; + recording_thumbnails?: boolean; + save_chat_text?: boolean; + save_close_caption?: boolean; save_panelist_chat?: boolean; save_poll_results?: boolean; - save_close_caption?: boolean; - record_audio_file_each_participant?: boolean; - host_pause_stop_recording?: boolean; - recording_disclaimer?: boolean; - ask_participants_to_consent_disclaimer?: boolean; - ask_host_to_confirm_disclaimer?: boolean; - recording_password_requirement?: { - length?: number; - have_letter?: boolean; - have_number?: boolean; - have_special_character?: boolean; - only_allow_numeric?: boolean; + show_timestamp?: boolean; + }; + schedule_meeting?: { + audio_type?: boolean; + embed_password_in_join_link?: boolean; + force_pmi_jbh_password?: boolean; + host_video?: boolean; + join_before_host?: boolean; + mute_upon_entry?: boolean; + participant_video?: boolean; + personal_meeting?: boolean; + pstn_password_protected?: boolean; + require_password_for_instant_meetings?: boolean; + require_password_for_pmi_meetings?: "none" | "all" | "jbh_only"; + require_password_for_scheduled_meetings?: boolean; + require_password_for_scheduling_new_meetings?: boolean; + upcoming_meeting_reminder?: boolean; + use_pmi_for_instant_meetings?: boolean; + use_pmi_for_schedule_meetings?: boolean; + always_display_zoom_meeting_as_topic?: { + enable?: boolean; + display_topic_for_scheduled_meetings?: boolean; }; - ip_address_access_control?: { + always_display_zoom_webinar_as_topic?: { enable?: boolean; - ip_addresses_or_ranges?: string; + display_topic_for_scheduled_webinars?: boolean; }; }; telephony?: { - third_party_audio?: boolean; audio_conference_info?: string; - show_international_numbers_link?: boolean; telephony_regions?: { - allowed_values?: string[]; selection_values?: string; }; - }; - feature?: { - meeting_capacity?: number; - large_meeting?: boolean; - large_meeting_capacity?: number; - webinar?: boolean; - webinar_capacity?: number; - zoom_events?: boolean; - zoom_events_capacity?: number; - cn_meeting?: boolean; - in_meeting?: boolean; - zoom_phone?: boolean; - concurrent_meeting?: string; + third_party_audio?: boolean; }; meeting_security?: { auto_security?: boolean; - waiting_room?: boolean; - waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - whitelisted_domains_for_waiting_room?: string; - users_who_can_admit_participants_from_waiting_room?: number; + block_user_domain?: boolean; + block_user_domain_list?: string[]; + chat_etiquette_tool?: { + enable?: boolean; + policies?: { + description?: string; + id?: string; + is_locked?: boolean; + keywords?: string[]; + name?: string; + regular_expression?: string; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; + }[]; }; + embed_password_in_join_link?: boolean; + encryption_type?: "enhanced_encryption" | "e2ee"; + end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; - require_password_for_scheduled_meeting?: boolean; - pmi_password?: boolean; - phone_password?: boolean; - webinar_password?: boolean; - require_password_for_scheduled_webinar?: boolean; meeting_password_requirement?: { - length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; - only_allow_numeric?: boolean; have_upper_and_lower_characters?: boolean; - consecutive_characters_length?: number; + length?: number; + only_allow_numeric?: boolean; weak_enhance_detection?: boolean; }; - embed_password_in_join_link?: boolean; - end_to_end_encrypted_meetings?: boolean; - encryption_type?: string; - block_user_domain?: boolean; only_authenticated_can_join_from_webclient?: boolean; - block_user_domain_list?: string[]; + phone_password?: boolean; + pmi_password?: boolean; + require_password_for_scheduled_meeting?: boolean; + require_password_for_scheduled_webinar?: boolean; + waiting_room?: boolean; + waiting_room_settings?: { + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; + whitelisted_domains_for_waiting_room?: string; + }; + webinar_password?: boolean; }; - tsp?: { - call_out?: boolean; - call_out_countries?: string[]; - show_international_numbers_link?: boolean; - display_toll_free_numbers?: boolean; + chat?: { + share_files?: { + enable?: boolean; + share_option?: "anyone" | "account" | "organization"; + }; + chat_emojis?: { + enable?: boolean; + emojis_option?: "all" | "selected"; + }; + record_voice_messages?: boolean; + record_video_messages?: boolean; + screen_capture?: boolean; + create_public_channels?: boolean; + create_private_channels?: boolean; + share_links_in_chat?: boolean; + schedule_meetings_in_chat?: boolean; + allow_users_to_search_others_options?: string; + allow_users_to_add_contacts?: { + enable?: boolean; + selected_option?: 1 | 2 | 3 | 4; + user_email_addresses?: string; + }; + allow_users_to_chat_with_others?: { + enable?: boolean; + selected_option?: 1 | 2 | 3 | 4; + user_email_addresses?: string; + }; + chat_etiquette_tool?: { + enable?: boolean; + policies?: { + id?: string; + status?: "activated" | "deactivated"; + }[]; + }; + send_data_to_third_party_archiving_service?: { + enable?: boolean; + }; }; }; }; - }; -}; -type UserInvitationAcceptedEvent = Event<"user.invitation_accepted"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - object: { - id: string; - first_name?: string; - last_name?: string; - email: string; - type: number; - }; - }; -}; -type GroupCreatedEvent = Event<"group.created"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - operator: string; - operator_id: string; time_stamp?: number; - object: { - id: string; - name: string; - }; - }; -}; -type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { - event: string; - event_ts: number; - payload: { - account_id: string; - operator: string; - operator_id: string; - object: { + old_object: { id: string; settings: { audio_conferencing?: { @@ -4507,9 +5465,9 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { alert_guest_join?: boolean; allow_users_to_delete_messages_in_meeting_chat?: boolean; allow_live_streaming?: boolean; - allow_participants_chat_with?: number; + allow_participants_chat_with?: 1 | 2 | 3 | 4; allow_show_zoom_windows?: boolean; - allow_users_save_chats?: number; + allow_users_save_chats?: 1 | 2 | 3; annotation?: boolean; auto_answer?: boolean; auto_saving_chat?: boolean; @@ -4529,7 +5487,7 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { custom_data_center_regions?: boolean; custom_live_streaming_service?: boolean; custom_service_instructions?: string; - data_center_regions?: string[]; + data_center_regions?: ("AU" | "LA" | "CA" | "CN" | "DE" | "HK" | "IN" | "IE" | "TY" | "MX" | "NL" | "SG" | "US")[]; disable_screen_sharing_for_host_meetings?: boolean; disable_screen_sharing_for_in_meeting_guests?: boolean; e2e_encryption?: boolean; @@ -4551,7 +5509,7 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { sign_language_interpretation?: { enable?: boolean; enable_sign_language_interpretation_by_default?: boolean; - languages?: string[]; + languages?: ("American" | "Chinese" | "French" | "German" | "Japanese" | "Russian" | "Brazilian" | "Spanish" | "Mexican" | "British")[]; custom_languages?: string[]; }; live_streaming_facebook?: boolean; @@ -4573,7 +5531,7 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { enable?: boolean; }; meeting_reactions?: boolean; - meeting_reactions_emojis?: string; + meeting_reactions_emojis?: "all" | "selected"; allow_host_panelists_to_use_audible_clap?: boolean; webinar_reactions?: boolean; meeting_survey?: boolean; @@ -4611,19 +5569,19 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { }; waiting_room?: boolean; webinar_chat?: { - allow_attendees_chat_with?: number; + allow_attendees_chat_with?: 1 | 2 | 3; allow_auto_save_local_chat_file?: boolean; - allow_panelists_chat_with?: number; + allow_panelists_chat_with?: 1 | 2; allow_panelists_send_direct_message?: boolean; - allow_users_save_chats?: number; - default_attendees_chat_with?: number; + allow_users_save_chats?: 0 | 1 | 2; + default_attendees_chat_with?: 1 | 2; enable?: boolean; }; webinar_live_streaming?: { custom_service_instructions?: string; enable?: boolean; live_streaming_reminder?: boolean; - live_streaming_service?: string[]; + live_streaming_service?: ("facebook" | "workplace_by_facebook" | "youtube" | "custom_live_streaming_service")[]; }; webinar_polling?: { advanced_polls?: boolean; @@ -4636,9 +5594,9 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { webinar_question_answer?: boolean; webinar_survey?: boolean; whiteboard?: boolean; - who_can_share_screen?: string; - who_can_share_screen_when_someone_is_sharing?: string; - participants_share_simultaneously?: string; + who_can_share_screen?: "host" | "all"; + who_can_share_screen_when_someone_is_sharing?: "host" | "all"; + participants_share_simultaneously?: "multiple" | "one"; workplace_by_facebook?: boolean; }; other_options?: { @@ -4665,7 +5623,7 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { chat_with_sender_email?: boolean; video_file?: boolean; }; - type?: number; + type?: 1 | 2 | 3; }; auto_recording?: boolean; cloud_recording?: boolean; @@ -4714,7 +5672,7 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { personal_meeting?: boolean; pstn_password_protected?: boolean; require_password_for_instant_meetings?: boolean; - require_password_for_pmi_meetings?: string; + require_password_for_pmi_meetings?: "none" | "all" | "jbh_only"; require_password_for_scheduled_meetings?: boolean; require_password_for_scheduling_new_meetings?: boolean; upcoming_meeting_reminder?: boolean; @@ -4749,16 +5707,16 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { keywords?: string[]; name?: string; regular_expression?: string; - status?: string; - trigger_action?: number; + status?: "activated" | "deactivated"; + trigger_action?: 1 | 2; }[]; }; embed_password_in_join_link?: boolean; - encryption_type?: string; + encryption_type?: "enhanced_encryption" | "e2ee"; end_to_end_encrypted_meetings?: boolean; meeting_password?: boolean; meeting_password_requirement?: { - consecutive_characters_length?: number; + consecutive_characters_length?: 0 | 4 | 5 | 6 | 7 | 8; have_letter?: boolean; have_number?: boolean; have_special_character?: boolean; @@ -4774,8 +5732,8 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { require_password_for_scheduled_webinar?: boolean; waiting_room?: boolean; waiting_room_settings?: { - participants_to_place_in_waiting_room?: number; - users_who_can_admit_participants_from_waiting_room?: number; + participants_to_place_in_waiting_room?: 0 | 1 | 2; + users_who_can_admit_participants_from_waiting_room?: 0 | 1; whitelisted_domains_for_waiting_room?: string; }; webinar_password?: boolean; @@ -4783,11 +5741,11 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { chat?: { share_files?: { enable?: boolean; - share_option?: string; + share_option?: "anyone" | "account" | "organization"; }; chat_emojis?: { enable?: boolean; - emojis_option?: string; + emojis_option?: "all" | "selected"; }; record_voice_messages?: boolean; record_video_messages?: boolean; @@ -4799,19 +5757,19 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { allow_users_to_search_others_options?: string; allow_users_to_add_contacts?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; allow_users_to_chat_with_others?: { enable?: boolean; - selected_option?: number; + selected_option?: 1 | 2 | 3 | 4; user_email_addresses?: string; }; chat_etiquette_tool?: { enable?: boolean; policies?: { id?: string; - status?: string; + status?: "activated" | "deactivated"; }[]; }; send_data_to_third_party_archiving_service?: { @@ -4820,7 +5778,6 @@ type GroupSettingsUpdatedEvent = Event<"group.settings_updated"> & { }; }; }; - time_stamp?: number; }; }; type UserDisassociatedEvent = Event<"user.disassociated"> & { @@ -4835,7 +5792,7 @@ type UserDisassociatedEvent = Event<"user.disassociated"> & { first_name: string; last_name: string; email: string; - type: number; + type: 1 | 2; }; }; }; @@ -4851,6 +5808,10 @@ type GroupUpdatedEvent = Event<"group.updated"> & { id: string; name: string; }; + old_object: { + id: string; + name?: string; + }; }; }; type UserPresenceStatusUpdatedEvent = Event<"user.presence_status_updated"> & { @@ -4862,10 +5823,10 @@ type UserPresenceStatusUpdatedEvent = Event<"user.presence_status_updated"> & { date_time: string; email: string; id: string; - presence_status: string; + presence_status: "Available" | "Away" | "Do_Not_Disturb" | "In_Meeting" | "Presenting" | "On_Phone_Call" | "In_Calendar_Event" | "Offline" | "Busy" | "Mobile_signed_in"; app?: { - type: string; - presence_status: string; + type: "desktop" | "mobile" | "pad" | "pzr"; + presence_status: "Available" | "Away" | "Do_Not_Disturb" | "In_Meeting" | "Presenting" | "On_Phone_Call" | "In_Calendar_Event" | "Offline" | "Busy"; }; }; }; @@ -4882,7 +5843,7 @@ type UserActivatedEvent = Event<"user.activated"> & { first_name: string; last_name: string; email: string; - type: number; + type: 1 | 2; }; }; }; @@ -4893,11 +5854,11 @@ type UserSignedInEvent = Event<"user.signed_in"> & { account_id: string; object: { id: string; - client_type: string; + client_type: "browser" | "mac" | "win" | "iphone" | "android" | "ipad" | "chromeos" | "linux"; date_time: string; email: string; version: string; - login_type: number; + login_type: 0 | 1 | 100 | 101; }; }; }; @@ -4908,11 +5869,11 @@ type UserSignedOutEvent = Event<"user.signed_out"> & { account_id: string; object: { id: string; - client_type: string; + client_type: "browser" | "mac" | "win" | "iphone" | "android" | "ipad" | "chromeos" | "linux"; date_time: string; email: string; version: string; - login_type: number; + login_type: 0 | 1 | 100 | 101; }; }; }; @@ -4956,14 +5917,14 @@ type UserUpdatedEvent = Event<"user.updated"> & { account_id: string; operator?: string; operator_id?: string; - operation?: string; + operation?: "change_password" | "sign_out_from_all_devices"; object: { id: string; first_name?: string; last_name?: string; display_name?: string; email?: string; - type?: number; + type?: 1 | 2; phone_number?: string; phone_country?: string; company?: string; @@ -4993,6 +5954,41 @@ type UserUpdatedEvent = Event<"user.updated"> & { primary_group_id?: string; }; time_stamp?: number; + old_object?: { + id: string; + first_name?: string; + last_name?: string; + display_name?: string; + email?: string; + type?: 1 | 2; + phone_number?: string; + phone_country?: string; + company?: string; + pmi?: number; + use_pmi?: boolean; + timezone?: string; + pic_url?: string; + vanity_name?: string; + host_key?: string; + role?: string; + dept?: string; + language?: string; + settings?: { + feature?: { + large_meeting_capacity?: number; + webinar?: boolean; + webinar_capacity?: number; + }; + meeting_capacity?: number; + large_meeting?: string; + }; + custom_attributes?: { + key: string; + name: string; + value: number; + }[]; + primary_group_id?: string; + }; }; }; type GroupDeletedEvent = Event<"group.deleted"> & { @@ -5016,14 +6012,14 @@ type UserCreatedEvent = Event<"user.created"> & { account_id: string; operator: string; operator_id: string; - creation_type: string; + creation_type: "create" | "ssoCreate" | "autoCreate" | "custCreate"; object: { id: string; first_name?: string; last_name?: string; display_name?: string; email: string; - type: number; + type: 1 | 2; }; }; }; @@ -5031,33 +6027,6 @@ type UsersEvents = GroupAdminAddedEvent | GroupLockSettingsUpdatedEvent | GroupA declare class UsersEventProcessor extends EventManager { } -/** - * Credentials for access token & refresh token, which are used to access Zoom's APIs. - * - * As access token is short-lived (usually a single hour), its expiration time is checked - * first. If it's possible to use the access token, it's used; however, if it has expired - * or is close to expiring, the refresh token should be used to generate a new access token - * before the API call is made. Refresh tokens are generally valid for 90 days. - * - * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} - * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. - * It's likely that this error will be rare, but it _can_ be thrown. - */ -interface OAuthToken { - accessToken: string; - expirationTimeIso: string; - refreshToken: string; - scopes: string[]; -} -declare class OAuth extends InteractiveAuth { - private assertResponseAccessToken; - private fetchAccessToken; - getToken(): Promise; - initRedirectCode(code: string): Promise; - private mapOAuthToken; - private refreshAccessToken; -} - type UsersOAuthOptions = CommonClientOptions; declare class UsersOAuthClient = UsersOAuthOptions> extends ProductClient { protected initAuth({ clientId, clientSecret, tokenStore, ...restOptions }: OptionsType): OAuth; @@ -5072,4 +6041,5 @@ declare class UsersS2SAuthClient typeof obj.installerOptions.redirectUri !== "undefined" && typeof obj.installerOptions.stateStore !== "undefined"; @@ -450,7 +453,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -460,14 +466,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -579,9 +591,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -595,6 +604,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? createServer : createServer$1; } @@ -609,8 +631,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -627,69 +661,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -723,18 +772,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -743,87 +798,13 @@ class HttpReceiver { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -852,10 +833,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -931,6 +925,21 @@ class UsersEndpoints extends WebEndpoints { addContactGroupMembers: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ groupId }) => `/contacts/groups/${groupId}/members` }), removeMembersInContactGroup: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ groupId }) => `/contacts/groups/${groupId}/members` }) }; + divisions = { + listDivisions: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/divisions` }), + createDivision: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/divisions` }), + getDivision: this.buildEndpoint({ + method: "GET", + urlPathBuilder: ({ divisionId }) => `/divisions/${divisionId}` + }), + deleteDivision: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ divisionId }) => `/divisions/${divisionId}` + }), + updateDivision: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ divisionId }) => `/divisions/${divisionId}` }), + listDivisionMembers: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ divisionId }) => `/divisions/${divisionId}/users` }), + assignDivision: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ divisionId }) => `/divisions/${divisionId}/users` }) + }; groups = { listGroups: this.buildEndpoint({ method: "GET", @@ -953,10 +962,7 @@ class UsersEndpoints extends WebEndpoints { urlPathBuilder: ({ groupId }) => `/groups/${groupId}` }), listGroupAdmins: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/admins` }), - addGroupAdmins: this.buildEndpoint({ - method: "POST", - urlPathBuilder: ({ groupId }) => `/groups/${groupId}/admins` - }), + addGroupAdmins: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/admins` }), deleteGroupAdmin: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ groupId, userId }) => `/groups/${groupId}/admins/${userId}` @@ -965,10 +971,7 @@ class UsersEndpoints extends WebEndpoints { getLockedSettings: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/lock_settings` }), updateLockedSettings: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/lock_settings` }), listGroupMembers: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/members` }), - addGroupMembers: this.buildEndpoint({ - method: "POST", - urlPathBuilder: ({ groupId }) => `/groups/${groupId}/members` - }), + addGroupMembers: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ groupId }) => `/groups/${groupId}/members` }), deleteGroupMember: this.buildEndpoint({ method: "DELETE", urlPathBuilder: ({ groupId, memberId }) => `/groups/${groupId}/members/${memberId}` @@ -995,7 +998,7 @@ class UsersEndpoints extends WebEndpoints { urlPathBuilder: () => `/users` }), checkUserEmail: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/users/email` }), - bulkUpdateFeaturesForUser: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/users/features` }), + bulkUpdateFeaturesForUsers: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/users/features` }), getUsersZAK: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/users/me/zak` @@ -1169,7 +1172,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -1190,7 +1195,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. diff --git a/videosdk/videosdk.cjs b/videosdk/videosdk.cjs index c7092a2..7dce677 100644 --- a/videosdk/videosdk.cjs +++ b/videosdk/videosdk.cjs @@ -426,6 +426,9 @@ class JwtStateStore { const DEFAULT_INSTALL_PATH = "/zoom/oauth/install"; const DEFAULT_CALLBACK_PATH = "/zoom/oauth/callback"; +const DEFAULT_STATE_COOKIE_NAME = "zoom-oauth-state"; +const DEFAULT_STATE_COOKIE_MAX_AGE = 600; // 10 minutes in seconds +const MAXIMUM_STATE_MAX_AGE = 3600; // 1 hour in seconds const OAUTH_AUTHORIZE_PATH = "/oauth/authorize"; /** * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication @@ -450,7 +453,10 @@ class InteractiveAuth extends Auth { searchParams.set("redirect_uri", this.getFullRedirectUri()); searchParams.set("response_type", "code"); searchParams.set("state", generatedState); - return authUrl.toString(); + return { + fullUrl: authUrl.toString(), + generatedState + }; } getFullRedirectUri() { if (!this.installerOptions?.redirectUri || !this.installerOptions.redirectUriPath) { @@ -460,14 +466,20 @@ class InteractiveAuth extends Auth { } // Don't return a type; we want it to be as narrow as possible (used for ReturnType). // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }) { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }) { const updatedOptions = { directInstall: Boolean(directInstall), installPath: installPath ? prependSlashes(installPath) : DEFAULT_INSTALL_PATH, redirectUri, redirectUriPath: redirectUriPath ? prependSlashes(redirectUriPath) : DEFAULT_CALLBACK_PATH, - stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }) + stateStore: isStateStore(stateStore) ? stateStore : new JwtStateStore({ stateSecret: stateStore }), + stateCookieName: stateCookieName ?? DEFAULT_STATE_COOKIE_NAME, + stateCookieMaxAge: stateCookieMaxAge ?? DEFAULT_STATE_COOKIE_MAX_AGE }; + if (updatedOptions.stateCookieMaxAge > MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -579,9 +591,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -595,6 +604,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? node_https.createServer : node_http.createServer; } @@ -609,8 +631,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -627,69 +661,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, exports.StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, exports.StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, exports.StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, exports.StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, exports.StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, exports.StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, exports.StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, exports.StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -723,18 +772,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(exports.StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -791,7 +846,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -812,7 +869,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. @@ -820,87 +877,13 @@ class ProductClient { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -929,10 +912,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${node_path.basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -1041,14 +1037,24 @@ class VideoSdkEndpoints extends WebEndpoints { getSessionLiveStreamDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/livestream` }), updateSessionLiveStream: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/livestream` }), updateSessionLivestreamStatus: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/livestream/status` }), + getSessionSIPURIWithPasscode: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/sip_dialing` }), updateSessionStatus: this.buildEndpoint({ method: "PUT", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/status` }), + listSessionStreamingIngestions: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/stream_ingestions` }), listSessionUsers: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/users` }), listSessionUsersQoS: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/users/qos` }), getSharingRecordingDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/users/sharing` }), getSessionUserQoS: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId, userId }) => `/videosdk/sessions/${sessionId}/users/${userId}/qos` - }) + }), + listStreamIngestions: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/videosdk/stream_ingestions` }), + createStreamIngestion: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/videosdk/stream_ingestions` }), + getStreamIngestion: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ streamId }) => `/videosdk/stream_ingestions/${streamId}` }), + deleteStreamIngestion: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ streamId }) => `/videosdk/stream_ingestions/${streamId}` + }), + updateStreamIngestion: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ streamId }) => `/videosdk/stream_ingestions/${streamId}` }) }; videoSDKReports = { getCloudRecordingUsageReport: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/videosdk/report/cloud_recording` }), @@ -1058,6 +1064,43 @@ class VideoSdkEndpoints extends WebEndpoints { }; } +const ALL_EVENTS = [ + "session.user_phone_callout_ringing", + "session.user_room_system_callout_ringing", + "session.recording_started", + "session.recording_resumed", + "session.live_streaming_stopped", + "session.stream_ingestion_stopped", + "session.user_room_system_callout_rejected", + "session.alert", + "session.recording_summary_completed", + "session.sharing_ended", + "session.recording_paused", + "session.ended", + "session.started", + "session.stream_ingestion_unbind", + "session.live_streaming_started", + "session.user_room_system_callout_missed", + "session.user_phone_callout_accepted", + "session.user_left", + "session.sharing_started", + "session.user_phone_callout_canceled", + "session.recording_transcript_completed", + "session.recording_deleted", + "session.user_room_system_callout_failed", + "session.recording_completed", + "session.recording_transcript_failed", + "session.recording_trashed", + "session.user_joined", + "session.stream_ingestion_started", + "session.stream_ingestion_connected", + "session.stream_ingestion_disconnected", + "session.recording_recovered", + "session.user_phone_callout_missed", + "session.user_phone_callout_rejected", + "session.user_room_system_callout_accepted", + "session.recording_stopped" +]; class VideoSdkEventProcessor extends EventManager { } @@ -1073,6 +1116,7 @@ class VideoSdkClient extends ProductClient { } } +exports.ALL_EVENTS = ALL_EVENTS; exports.ApiResponseError = ApiResponseError; exports.AwsLambdaReceiver = AwsLambdaReceiver; exports.AwsReceiverRequestError = AwsReceiverRequestError; diff --git a/videosdk/videosdk.d.ts b/videosdk/videosdk.d.ts index 7a83883..40957f2 100644 --- a/videosdk/videosdk.d.ts +++ b/videosdk/videosdk.d.ts @@ -1,88 +1,8 @@ -import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { AxiosResponse } from 'axios'; +import { LambdaFunctionURLResult, LambdaFunctionURLHandler } from 'aws-lambda'; import { Server } from 'node:http'; import { ServerOptions } from 'node:https'; -type AllPropsOptional = Exclude<{ - [P in keyof T]: undefined extends T[P] ? True : False; -}[keyof T], undefined> extends True ? True : False; -type Constructor = new (...args: any[]) => T; -type MaybeArray = T | T[]; -type MaybePromise = T | Promise; -type StringIndexed = Record; - -/** - * {@link StateStore} defines methods for generating and verifying OAuth state. - * - * This interface is implemented internally for the default state store; however, - * it can also be implemented and passed to an OAuth client as well. - */ -interface StateStore { - /** - * Generate a new state string, which is directly appended to the OAuth `state` parameter. - */ - generateState(): MaybePromise; - /** - * Verify that the state received during OAuth callback is valid and not forged. - * - * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. - * - * @param state The state parameter that was received during OAuth callback - */ - verifyState(state: string): MaybePromise; -} -/** - * Guard if an object implements the {@link StateStore} interface — most notably, - * `generateState()` and `verifyState(state: string)`. - */ -declare const isStateStore: (obj: unknown) => obj is StateStore; - -interface TokenStore { - getLatestToken(): MaybePromise; - storeToken(token: Token): MaybePromise; -} - -interface RivetError extends Error { - readonly errorCode: ErrorCode; -} - -declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ - readonly ApiResponseError: "zoom_rivet_api_response_error"; - readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; - readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; - readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; - readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; - readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; - readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; - readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; - readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; - readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; - readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; - readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; - readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; - readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; - readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; - readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; - readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; -}[K]>; -declare const ApiResponseError: Constructor; -declare const AwsReceiverRequestError: Constructor; -declare const ClientCredentialsRawResponseError: Constructor; -declare const S2SRawResponseError: Constructor; -declare const CommonHttpRequestError: Constructor; -declare const ReceiverInconsistentStateError: Constructor; -declare const ReceiverOAuthFlowError: Constructor; -declare const HTTPReceiverConstructionError: Constructor; -declare const HTTPReceiverPortNotNumberError: Constructor; -declare const HTTPReceiverRequestError: Constructor; -declare const OAuthInstallerNotInitializedError: Constructor; -declare const OAuthTokenDoesNotExistError: Constructor; -declare const OAuthTokenFetchFailedError: Constructor; -declare const OAuthTokenRawResponseError: Constructor; -declare const OAuthTokenRefreshFailedError: Constructor; -declare const OAuthStateVerificationFailedError: Constructor; -declare const ProductClientConstructionError: Constructor; - declare enum LogLevel { ERROR = "error", WARN = "warn", @@ -142,6 +62,19 @@ declare class ConsoleLogger implements Logger { private static isMoreOrEqualSevere; } +type AllPropsOptional = Exclude<{ + [P in keyof T]: undefined extends T[P] ? True : False; +}[keyof T], undefined> extends True ? True : False; +type Constructor = new (...args: any[]) => T; +type MaybeArray = T | T[]; +type MaybePromise = T | Promise; +type StringIndexed = Record; + +interface TokenStore { + getLatestToken(): MaybePromise; + storeToken(token: Token): MaybePromise; +} + interface AuthOptions { clientId: string; clientSecret: string; @@ -187,6 +120,21 @@ declare abstract class Auth { }>, "grant_type">): Promise; } +interface ClientCredentialsToken { + accessToken: string; + expirationTimeIso: string; + scopes: string[]; +} + +interface JwtToken { + token: string; + expirationTimeIso: string; +} +declare class JwtAuth extends Auth { + private generateToken; + getToken(): Promise; +} + interface S2SAuthToken { accessToken: string; expirationTimeIso: string; @@ -225,12 +173,31 @@ declare class EventManager { protected withContext, Context>(): ContextListener; } +declare enum StatusCode { + OK = 200, + TEMPORARY_REDIRECT = 302, + BAD_REQUEST = 400, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + INTERNAL_SERVER_ERROR = 500 +} +interface ReceiverInitOptions { + eventEmitter?: GenericEventManager | undefined; + interactiveAuth?: InteractiveAuth | undefined; +} +interface Receiver { + canInstall(): true | false; + init(options: ReceiverInitOptions): void; + start(...args: any[]): MaybePromise; + stop(...args: any[]): MaybePromise; +} + interface HttpReceiverOptions extends Partial { endpoints?: MaybeArray | undefined; + logger?: Logger | undefined; + logLevel?: LogLevel | undefined; port?: number | string | undefined; - webhooksSecretToken: string; - logger?: Logger; - logLevel?: LogLevel; + webhooksSecretToken?: string | undefined; } type SecureServerOptions = { [K in (typeof secureServerOptionKeys)[number]]: ServerOptions[K]; @@ -243,10 +210,15 @@ declare class HttpReceiver implements Receiver { private logger; constructor(options: HttpReceiverOptions); canInstall(): true; + private buildDeletedStateCookieHeader; + private buildStateCookieHeader; + private getRequestCookie; private getServerCreator; private hasEndpoint; private hasSecureOptions; init({ eventEmitter, interactiveAuth }: ReceiverInitOptions): void; + private setResponseCookie; + private areNormalizedUrlsEqual; start(port?: number | string): Promise; stop(): Promise; private writeTemporaryRedirect; @@ -269,6 +241,7 @@ interface WebEndpointOptions { baseUrl?: string | undefined; doubleEncodeUrl?: boolean | undefined; timeout?: number | undefined; + userAgentName?: string | undefined; } type EndpointArguments = (PathSchema extends NoParams ? object : AllPropsOptional extends "t" ? { path?: PathSchema; @@ -290,6 +263,7 @@ declare class WebEndpoints { constructor(options: WebEndpointOptions); protected buildEndpoint({ method, baseUrlOverride, urlPathBuilder, requestMimeType }: BuildEndpointOptions): (_: EndpointArguments) => Promise>; private buildUserAgent; + private getCustomUserAgentName; private getHeaders; private getRequestBody; private isOk; @@ -297,7 +271,7 @@ declare class WebEndpoints { private makeRequest; } -type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & { +type CommonClientOptions = GetAuthOptions & ExtractInstallerOptions & Pick & { disableReceiver?: boolean | undefined; logger?: Logger | undefined; logLevel?: LogLevel | undefined; @@ -305,38 +279,68 @@ type CommonClientOptions = GetAuthOptions interface ClientReceiverOptions { receiver: R; } -type ClientConstructorOptions, R extends Receiver> = IsReceiverDisabled extends true ? O : O & (ClientReceiverOptions | HttpReceiverOptions); +type ClientConstructorOptions, R extends Receiver> = (O & { + disableReceiver: true; +}) | (O & (ClientReceiverOptions | HttpReceiverOptions)); type ExtractInstallerOptions = A extends InteractiveAuth ? [ ReturnType ] extends [true] ? WideInstallerOptions : object : object; type ExtractAuthTokenType = A extends Auth ? T : never; -type GenericClientOptions = CommonClientOptions; type GetAuthOptions = AuthOptions> & (A extends S2SAuth ? S2SAuthOptions : object); -type IsReceiverDisabled> = [ - O["disableReceiver"] -] extends [true] ? true : false; type WideInstallerOptions = { installerOptions: InstallerOptions; }; declare abstract class ProductClient, ReceiverType extends Receiver> { private readonly auth; readonly endpoints: EndpointsType; - readonly webEventConsumer: EventProcessorType; + readonly webEventConsumer?: EventProcessorType | undefined; private readonly receiver?; constructor(options: ClientConstructorOptions); protected abstract initAuth(options: OptionsType): AuthType; protected abstract initEndpoints(auth: AuthType, options: OptionsType): EndpointsType; - protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType; + protected abstract initEventProcessor(endpoints: EndpointsType, options: OptionsType): EventProcessorType | undefined; private initDefaultReceiver; - start(this: IsReceiverDisabled extends true ? never : this): Promise>; + start(): Promise>; +} + +/** + * {@link StateStore} defines methods for generating and verifying OAuth state. + * + * This interface is implemented internally for the default state store; however, + * it can also be implemented and passed to an OAuth client as well. + */ +interface StateStore { + /** + * Generate a new state string, which is directly appended to the OAuth `state` parameter. + */ + generateState(): MaybePromise; + /** + * Verify that the state received during OAuth callback is valid and not forged. + * + * If state verification fails, {@link OAuthStateVerificationFailedError} should be thrown. + * + * @param state The state parameter that was received during OAuth callback + */ + verifyState(state: string): MaybePromise; } +/** + * Guard if an object implements the {@link StateStore} interface — most notably, + * `generateState()` and `verifyState(state: string)`. + */ +declare const isStateStore: (obj: unknown) => obj is StateStore; +interface AuthorizationUrlResult { + fullUrl: string; + generatedState: string; +} interface InstallerOptions { directInstall?: boolean | undefined; installPath?: string | undefined; redirectUri: string; redirectUriPath?: string | undefined; stateStore: StateStore | string; + stateCookieName?: string | undefined; + stateCookieMaxAge?: number | undefined; } /** * {@link InteractiveAuth}, an extension of {@link Auth}, is designed for use cases where authentication @@ -350,36 +354,79 @@ interface InstallerOptions { */ declare abstract class InteractiveAuth extends Auth { installerOptions?: ReturnType; - getAuthorizationUrl(): Promise; + getAuthorizationUrl(): Promise; getFullRedirectUri(): string; - setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore }: InstallerOptions): { + setInstallerOptions({ directInstall, installPath, redirectUri, redirectUriPath, stateStore, stateCookieName, stateCookieMaxAge }: InstallerOptions): { directInstall: boolean; installPath: string; redirectUri: string; redirectUriPath: string; stateStore: StateStore; + stateCookieName: string; + stateCookieMaxAge: number; }; } -declare enum StatusCode { - OK = 200, - TEMPORARY_REDIRECT = 302, - BAD_REQUEST = 400, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - INTERNAL_SERVER_ERROR = 500 -} -interface ReceiverInitOptions { - eventEmitter: GenericEventManager; - interactiveAuth?: InteractiveAuth | undefined; +/** + * Credentials for access token & refresh token, which are used to access Zoom's APIs. + * + * As access token is short-lived (usually a single hour), its expiration time is checked + * first. If it's possible to use the access token, it's used; however, if it has expired + * or is close to expiring, the refresh token should be used to generate a new access token + * before the API call is made. Refresh tokens are generally valid for 90 days. + * + * If neither the access token nor the refresh token is available, {@link OAuthTokenRefreshFailedError} + * shall be thrown, informing the developer that neither value can be used, and the user must re-authorize. + * It's likely that this error will be rare, but it _can_ be thrown. + */ +interface OAuthToken { + accessToken: string; + expirationTimeIso: string; + refreshToken: string; + scopes: string[]; } -interface Receiver { - canInstall(): true | false; - init(options: ReceiverInitOptions): void; - start(...args: any[]): MaybePromise; - stop(...args: any[]): MaybePromise; + +interface RivetError extends Error { + readonly errorCode: ErrorCode; } +declare const isCoreError: (obj: unknown, key?: K | undefined) => obj is RivetError<{ + readonly ApiResponseError: "zoom_rivet_api_response_error"; + readonly AwsReceiverRequestError: "zoom_rivet_aws_receiver_request_error"; + readonly ClientCredentialsRawResponseError: "zoom_rivet_client_credentials_raw_response_error"; + readonly S2SRawResponseError: "zoom_rivet_s2s_raw_response_error"; + readonly CommonHttpRequestError: "zoom_rivet_common_http_request_error"; + readonly ReceiverInconsistentStateError: "zoom_rivet_receiver_inconsistent_state_error"; + readonly ReceiverOAuthFlowError: "zoom_rivet_receiver_oauth_flow_error"; + readonly HTTPReceiverConstructionError: "zoom_rivet_http_receiver_construction_error"; + readonly HTTPReceiverPortNotNumberError: "zoom_rivet_http_receiver_port_not_number_error"; + readonly HTTPReceiverRequestError: "zoom_rivet_http_receiver_request_error"; + readonly OAuthInstallerNotInitializedError: "zoom_rivet_oauth_installer_not_initialized_error"; + readonly OAuthTokenDoesNotExistError: "zoom_rivet_oauth_does_not_exist_error"; + readonly OAuthTokenFetchFailedError: "zoom_rivet_oauth_token_fetch_failed_error"; + readonly OAuthTokenRawResponseError: "zoom_rivet_oauth_token_raw_response_error"; + readonly OAuthTokenRefreshFailedError: "zoom_rivet_oauth_token_refresh_failed_error"; + readonly OAuthStateVerificationFailedError: "zoom_rivet_oauth_state_verification_failed_error"; + readonly ProductClientConstructionError: "zoom_rivet_product_client_construction_error"; +}[K]>; +declare const ApiResponseError: Constructor; +declare const AwsReceiverRequestError: Constructor; +declare const ClientCredentialsRawResponseError: Constructor; +declare const S2SRawResponseError: Constructor; +declare const CommonHttpRequestError: Constructor; +declare const ReceiverInconsistentStateError: Constructor; +declare const ReceiverOAuthFlowError: Constructor; +declare const HTTPReceiverConstructionError: Constructor; +declare const HTTPReceiverPortNotNumberError: Constructor; +declare const HTTPReceiverRequestError: Constructor; +declare const OAuthInstallerNotInitializedError: Constructor; +declare const OAuthTokenDoesNotExistError: Constructor; +declare const OAuthTokenFetchFailedError: Constructor; +declare const OAuthTokenRawResponseError: Constructor; +declare const OAuthTokenRefreshFailedError: Constructor; +declare const OAuthStateVerificationFailedError: Constructor; +declare const ProductClientConstructionError: Constructor; + interface AwsLambdaReceiverOptions { webhooksSecretToken: string; } @@ -394,15 +441,6 @@ declare class AwsLambdaReceiver implements Receiver { stop(): Promise; } -interface JwtToken { - token: string; - expirationTimeIso: string; -} -declare class JwtAuth extends Auth { - private generateToken; - getToken(): Promise; -} - type ByosStorageUpdateBringYourOwnStorageSettingsRequestBody = { bring_our_own_storage: boolean; storage_location_id?: string; @@ -505,6 +543,7 @@ type CloudRecordingListRecordingsOfAccountResponse = { file_type?: string; file_size?: number; download_url?: string; + external_storage_url?: string; status?: "completed"; deleted_time?: string; recording_type?: string; @@ -549,6 +588,7 @@ type CloudRecordingListSessionsRecordingsResponse = ({ file_type?: string; file_size?: number; download_url?: string; + external_storage_url?: string; status?: "completed"; deleted_time?: string; recording_type?: string; @@ -652,10 +692,14 @@ type SessionsListSessionsResponse = { has_pstn?: boolean; session_key?: string; has_session_summary?: boolean; + audio_quality?: "good" | "fair" | "poor" | "bad"; + video_quality?: "good" | "fair" | "poor" | "bad"; + screen_share_quality?: "good" | "fair" | "poor" | "bad"; }[]; }; type SessionsCreateSessionRequestBody = { session_name: string; + session_password?: string; settings?: { auto_recording?: "cloud" | "none"; }; @@ -664,6 +708,7 @@ type SessionsCreateSessionResponse = { session_id?: string; session_number?: number; session_name: string; + session_password?: string; passcode?: string; created_at?: string; settings?: { @@ -710,6 +755,10 @@ type SessionsGetSessionDetailsResponse = { type?: "toll" | "tollfree" | "premium"; }[]; }; + audio_quality?: "good" | "fair" | "poor" | "bad"; + video_quality?: "good" | "fair" | "poor" | "bad"; + screen_share_quality?: "good" | "fair" | "poor" | "bad"; + total_minutes?: number; }; type SessionsDeleteSessionPathParams = { sessionId: string; @@ -718,7 +767,7 @@ type SessionsUseInSessionEventsControlsPathParams = { sessionId: string; }; type SessionsUseInSessionEventsControlsRequestBody = { - method?: "recording.start" | "recording.stop" | "recording.pause" | "recording.resume" | "user.invite.callout" | "user.invite.room_system_callout" | "audio.block" | "audio.unblock" | "video.block" | "video.unblock" | "share.block" | "share.unblock" | "user.remove"; + method?: "recording.start" | "recording.stop" | "recording.pause" | "recording.resume" | "user.invite.callout" | "user.invite.room_system_callout" | "stream_ingestion.bind" | "stream_ingestion.unbind" | "stream_ingestion.send" | "stream_ingestion.stop" | "audio.block" | "audio.unblock" | "video.block" | "video.unblock" | "share.block" | "share.unblock" | "user.remove"; params?: { invitee_name?: string; phone_number?: string; @@ -742,6 +791,7 @@ type SessionsUseInSessionEventsControlsRequestBody = { }[]; }; participant_uuid?: string; + stream_ingestion_stream_id?: string; }; }; type SessionsGetSessionLiveStreamDetailsPathParams = { @@ -774,12 +824,31 @@ type SessionsUpdateSessionLivestreamStatusRequestBody = { close_caption?: "burnt-in" | "embedded" | "off"; }; }; +type SessionsGetSessionSIPURIWithPasscodePathParams = { + sessionId: string; +}; +type SessionsGetSessionSIPURIWithPasscodeRequestBody = { + passcode?: string; +}; +type SessionsGetSessionSIPURIWithPasscodeResponse = { + sip_dialing?: string; + participant_identifier_code?: string; + expire_in?: number; +}; type SessionsUpdateSessionStatusPathParams = { sessionId: string; }; type SessionsUpdateSessionStatusRequestBody = { action?: "end"; }; +type SessionsListSessionStreamingIngestionsPathParams = { + sessionId: string; +}; +type SessionsListSessionStreamingIngestionsResponse = { + stream_id?: string; + rtmp_connection_status?: 0 | 1; + rtmp_stream_push_status?: 0 | 1; +}[]; type SessionsListSessionUsersPathParams = { sessionId: string; }; @@ -813,6 +882,14 @@ type SessionsListSessionUsersResponse = { }[]; participant_uuid?: string; client?: string; + os?: string; + os_version?: string; + browser_name?: string; + browser_version?: string; + audio_quality?: "good" | "fair" | "poor" | "bad"; + video_quality?: "good" | "fair" | "poor" | "bad"; + screen_share_quality?: "good" | "fair" | "poor" | "bad"; + is_original_host?: boolean; }[]; }; type SessionsListSessionUsersQoSPathParams = { @@ -830,6 +907,11 @@ type SessionsListSessionUsersQoSResponse = { id?: string; name?: string; device?: "Phone" | "H.323/SIP" | "Windows" | "Mac" | "iOS" | "Android"; + client?: string; + os?: string; + os_version?: string; + browser_name?: string; + browser_version?: string; ip_address?: string; location?: string; network_type?: "Wired" | "Wifi" | "PPP" | "Cellular" | "Others"; @@ -840,6 +922,8 @@ type SessionsListSessionUsersQoSResponse = { connection_type?: string; join_time?: string; leave_time?: string; + user_key?: string; + is_original_host?: boolean; user_qos?: { date_time?: string; audio_input?: { @@ -942,6 +1026,12 @@ type SessionsListSessionUsersQoSResponse = { resolution?: string; frame_rate?: string; }; + wifi_rssi?: { + max_rssi?: number; + avg_rssi?: number; + min_rssi?: number; + rssi_unit?: string; + }; cpu_usage?: { zoom_min_cpu_usage?: string; zoom_avg_cpu_usage?: string; @@ -965,6 +1055,7 @@ type SessionsGetSharingRecordingDetailsResponse = { users?: { id?: string; name?: string; + user_key?: string; details?: { content?: "local_recording" | "cloud_recording" | "desktop" | "application" | "whiteboard" | "airplay" | "camera" | "video_sdk"; start_time?: string; @@ -983,6 +1074,11 @@ type SessionsGetSessionUserQoSResponse = { id?: string; name?: string; device?: "Phone" | "H.323/SIP" | "Windows" | "Mac" | "iOS" | "Android"; + os?: string; + os_version?: string; + browser_name?: string; + browser_version?: string; + client?: string; ip_address?: string; location?: string; network_type?: "Wired" | "Wifi" | "PPP" | "Cellular" | "Others"; @@ -993,6 +1089,8 @@ type SessionsGetSessionUserQoSResponse = { connection_type?: string; join_time?: string; leave_time?: string; + user_key?: string; + is_original_host?: boolean; user_qos?: { date_time?: string; audio_input?: { @@ -1095,6 +1193,12 @@ type SessionsGetSessionUserQoSResponse = { resolution?: string; frame_rate?: string; }; + wifi_rssi?: { + max_rssi?: number; + avg_rssi?: number; + min_rssi?: number; + rssi_unit?: string; + }; cpu_usage?: { zoom_min_cpu_usage?: string; zoom_avg_cpu_usage?: string; @@ -1103,6 +1207,55 @@ type SessionsGetSessionUserQoSResponse = { }; }[]; }; +type SessionsListStreamIngestionsQueryParams = { + page_size?: number; + next_page_token?: string; +}; +type SessionsListStreamIngestionsResponse = { + page_size?: number; + next_page_token?: string; + stream_ingestions?: { + stream_id?: string; + stream_name?: string; + stream_description?: string; + stream_key?: string; + stream_url?: string; + backup_stream_url?: string; + }[]; +}; +type SessionsCreateStreamIngestionRequestBody = { + stream_name: string; + stream_description?: string; +}; +type SessionsCreateStreamIngestionResponse = { + stream_id?: string; + stream_name?: string; + stream_description?: string; + stream_key?: string; + stream_url?: string; + backup_stream_url?: string; +}; +type SessionsGetStreamIngestionPathParams = { + streamId: string; +}; +type SessionsGetStreamIngestionResponse = { + stream_id?: string; + stream_name?: string; + stream_description?: string; + stream_key?: string; + stream_url?: string; + backup_stream_url?: string; +}; +type SessionsDeleteStreamIngestionPathParams = { + streamId: string; +}; +type SessionsUpdateStreamIngestionPathParams = { + streamId: string; +}; +type SessionsUpdateStreamIngestionRequestBody = { + stream_name?: string; + stream_description?: string; +}; type VideoSDKReportsGetCloudRecordingUsageReportQueryParams = { from: string; to: string; @@ -1262,11 +1415,19 @@ declare class VideoSdkEndpoints extends WebEndpoints { } & { body?: SessionsUpdateSessionLivestreamStatusRequestBody; } & object) => Promise>; + getSessionSIPURIWithPasscode: (_: { + path: SessionsGetSessionSIPURIWithPasscodePathParams; + } & { + body?: SessionsGetSessionSIPURIWithPasscodeRequestBody; + } & object) => Promise>; updateSessionStatus: (_: { path: SessionsUpdateSessionStatusPathParams; } & { body?: SessionsUpdateSessionStatusRequestBody; } & object) => Promise>; + listSessionStreamingIngestions: (_: { + path: SessionsListSessionStreamingIngestionsPathParams; + } & object) => Promise>; listSessionUsers: (_: { path: SessionsListSessionUsersPathParams; } & object & { @@ -1287,6 +1448,23 @@ declare class VideoSdkEndpoints extends WebEndpoints { } & object & { query?: SessionsGetSessionUserQoSQueryParams; }) => Promise>; + listStreamIngestions: (_: object & { + query?: SessionsListStreamIngestionsQueryParams; + }) => Promise>; + createStreamIngestion: (_: object & { + body: SessionsCreateStreamIngestionRequestBody; + }) => Promise>; + getStreamIngestion: (_: { + path: SessionsGetStreamIngestionPathParams; + } & object) => Promise>; + deleteStreamIngestion: (_: { + path: SessionsDeleteStreamIngestionPathParams; + } & object) => Promise>; + updateStreamIngestion: (_: { + path: SessionsUpdateStreamIngestionPathParams; + } & { + body?: SessionsUpdateStreamIngestionRequestBody; + } & object) => Promise>; }; readonly videoSDKReports: { getCloudRecordingUsageReport: (_: object & { @@ -1314,10 +1492,13 @@ type SessionUserPhoneCalloutRingingEvent = Event<"session.user_phone_callout_rin uuid?: string; session_id: string; session_name: string; + session_key: string; + user_key: string; host_id: string; participant: { invitee_name: string; phone_number: number; + from_number: number; }; }; }; @@ -1402,6 +1583,26 @@ type SessionLiveStreamingStoppedEvent = Event<"session.live_streaming_stopped"> }; }; }; +type SessionStreamIngestionStoppedEvent = Event<"session.stream_ingestion_stopped"> & { + event: "session.stream_ingestion_stopped"; + event_ts: number; + payload: { + account_id: string; + object: { + session_id: string; + session_name: string; + session_key?: string; + stream_ingestion: { + stream_id: string; + stream_name: string; + stream_description?: string; + stream_key: string; + stream_url: string; + backup_stream_url: string; + }; + }; + }; +}; type SessionUserRoomSystemCalloutRejectedEvent = Event<"session.user_room_system_callout_rejected"> & { event: string; event_ts: number; @@ -1436,6 +1637,34 @@ type SessionAlertEvent = Event<"session.alert"> & { }; }; }; +type SessionRecordingSummaryCompletedEvent = Event<"session.recording_summary_completed"> & { + event: "session.recording_summary_completed"; + event_ts: number; + download_token: string; + payload: { + account_id: string; + object: { + session_id: string; + session_name: string; + session_key: string; + start_time: string; + timezone: string; + recording_files: { + id?: string; + recording_start?: string; + recording_end?: string; + file_name?: string; + file_path?: string; + file_type?: "SUMMARY"; + file_size?: number; + file_extension?: "JSON"; + download_url?: string; + status?: "completed"; + recording_type?: "summary"; + }[]; + }; + }; +}; type SessionSharingEndedEvent = Event<"session.sharing_ended"> & { event: "session.sharing_ended"; event_ts: number; @@ -1505,6 +1734,26 @@ type SessionStartedEvent = Event<"session.started"> & { }; }; }; +type SessionStreamIngestionUnbindEvent = Event<"session.stream_ingestion_unbind"> & { + event: "session.stream_ingestion_unbind"; + event_ts: number; + payload: { + account_id: string; + object: { + session_id: string; + session_name: string; + session_key?: string; + stream_ingestion: { + stream_id: string; + stream_name: string; + stream_description?: string; + stream_key: string; + stream_url: string; + backup_stream_url: string; + }; + }; + }; +}; type SessionLiveStreamingStartedEvent = Event<"session.live_streaming_started"> & { event: "session.live_streaming_started"; event_ts: number; @@ -1559,10 +1808,13 @@ type SessionUserPhoneCalloutAcceptedEvent = Event<"session.user_phone_callout_ac uuid?: string; session_id: string; session_name: string; + session_key: string; + user_key: string; host_id: string; participant: { invitee_name: string; phone_number: number; + from_number: number; }; }; }; @@ -1584,6 +1836,7 @@ type SessionUserLeftEvent = Event<"session.user_left"> & { leave_reason?: string; user_key?: string; phone_number?: string; + participant_uuid: string; }; }; }; @@ -1610,6 +1863,24 @@ type SessionSharingStartedEvent = Event<"session.sharing_started"> & { }; }; }; +type SessionUserPhoneCalloutCanceledEvent = Event<"session.user_phone_callout_canceled"> & { + event: string; + event_ts: number; + payload: { + account_id: string; + object: { + session_id: string; + session_name: string; + session_key: string; + user_key: string; + participant: { + invitee_name: string; + phone_number: number; + from_number: number; + }; + }; + }; +}; type SessionRecordingTranscriptCompletedEvent = Event<"session.recording_transcript_completed"> & { event: "session.recording_transcript_completed"; event_ts: number; @@ -1725,7 +1996,6 @@ type SessionRecordingCompletedEvent = Event<"session.recording_completed"> & { file_size?: number; file_extension?: "MP4"; download_url?: string; - play_url?: string; status?: "completed"; recording_type?: "individual_user" | "individual_shared_screen"; user_id?: string; @@ -1780,6 +2050,67 @@ type SessionUserJoinedEvent = Event<"session.user_joined"> & { join_time: string; user_key?: string; phone_number?: string; + participant_uuid: string; + }; + }; + }; +}; +type SessionStreamIngestionStartedEvent = Event<"session.stream_ingestion_started"> & { + event: "session.stream_ingestion_started"; + event_ts: number; + payload: { + account_id: string; + object: { + session_id: string; + session_name: string; + session_key?: string; + stream_ingestion: { + stream_id: string; + stream_name: string; + stream_description?: string; + stream_key: string; + stream_url: string; + backup_stream_url: string; + }; + }; + }; +}; +type SessionStreamIngestionConnectedEvent = Event<"session.stream_ingestion_connected"> & { + event: "session.stream_ingestion_connected"; + event_ts: number; + payload: { + account_id: string; + object: { + session_id: string; + session_name: string; + session_key?: string; + stream_ingestion: { + stream_id: string; + stream_name: string; + stream_description?: string; + stream_key: string; + stream_url: string; + backup_stream_url: string; + }; + }; + }; +}; +type SessionStreamIngestionDisconnectedEvent = Event<"session.stream_ingestion_disconnected"> & { + event: "session.stream_ingestion_disconnected"; + event_ts: number; + payload: { + account_id: string; + object: { + session_id: string; + session_name: string; + session_key?: string; + stream_ingestion: { + stream_id: string; + stream_name: string; + stream_description?: string; + stream_key: string; + stream_url: string; + backup_stream_url: string; }; }; }; @@ -1810,10 +2141,13 @@ type SessionUserPhoneCalloutMissedEvent = Event<"session.user_phone_callout_miss uuid?: string; session_id: string; session_name: string; + session_key: string; + user_key: string; host_id: string; participant: { invitee_name: string; phone_number: number; + from_number: number; }; }; }; @@ -1828,10 +2162,13 @@ type SessionUserPhoneCalloutRejectedEvent = Event<"session.user_phone_callout_re uuid?: string; session_id: string; session_name: string; + session_key: string; + user_key: string; host_id: string; participant: { invitee_name: string; phone_number: number; + from_number: number; }; }; }; @@ -1874,7 +2211,8 @@ type SessionRecordingStoppedEvent = Event<"session.recording_stopped"> & { }; }; }; -type VideoSdkEvents = SessionUserPhoneCalloutRingingEvent | SessionUserRoomSystemCalloutRingingEvent | SessionRecordingStartedEvent | SessionRecordingResumedEvent | SessionLiveStreamingStoppedEvent | SessionUserRoomSystemCalloutRejectedEvent | SessionAlertEvent | SessionSharingEndedEvent | SessionRecordingPausedEvent | SessionEndedEvent | SessionStartedEvent | SessionLiveStreamingStartedEvent | SessionUserRoomSystemCalloutMissedEvent | SessionUserPhoneCalloutAcceptedEvent | SessionUserLeftEvent | SessionSharingStartedEvent | SessionRecordingTranscriptCompletedEvent | SessionRecordingDeletedEvent | SessionUserRoomSystemCalloutFailedEvent | SessionRecordingCompletedEvent | SessionRecordingTranscriptFailedEvent | SessionRecordingTrashedEvent | SessionUserJoinedEvent | SessionRecordingRecoveredEvent | SessionUserPhoneCalloutMissedEvent | SessionUserPhoneCalloutRejectedEvent | SessionUserRoomSystemCalloutAcceptedEvent | SessionRecordingStoppedEvent; +declare const ALL_EVENTS: string[]; +type VideoSdkEvents = SessionUserPhoneCalloutRingingEvent | SessionUserRoomSystemCalloutRingingEvent | SessionRecordingStartedEvent | SessionRecordingResumedEvent | SessionLiveStreamingStoppedEvent | SessionStreamIngestionStoppedEvent | SessionUserRoomSystemCalloutRejectedEvent | SessionAlertEvent | SessionRecordingSummaryCompletedEvent | SessionSharingEndedEvent | SessionRecordingPausedEvent | SessionEndedEvent | SessionStartedEvent | SessionStreamIngestionUnbindEvent | SessionLiveStreamingStartedEvent | SessionUserRoomSystemCalloutMissedEvent | SessionUserPhoneCalloutAcceptedEvent | SessionUserLeftEvent | SessionSharingStartedEvent | SessionUserPhoneCalloutCanceledEvent | SessionRecordingTranscriptCompletedEvent | SessionRecordingDeletedEvent | SessionUserRoomSystemCalloutFailedEvent | SessionRecordingCompletedEvent | SessionRecordingTranscriptFailedEvent | SessionRecordingTrashedEvent | SessionUserJoinedEvent | SessionStreamIngestionStartedEvent | SessionStreamIngestionConnectedEvent | SessionStreamIngestionDisconnectedEvent | SessionRecordingRecoveredEvent | SessionUserPhoneCalloutMissedEvent | SessionUserPhoneCalloutRejectedEvent | SessionUserRoomSystemCalloutAcceptedEvent | SessionRecordingStoppedEvent; declare class VideoSdkEventProcessor extends EventManager { } @@ -1885,4 +2223,5 @@ declare class VideoSdkClient MAXIMUM_STATE_MAX_AGE) { + // This method is always called from ProductClient, so this should be fine. + throw new ProductClientConstructionError(`stateCookieMaxAge cannot be greater than ${MAXIMUM_STATE_MAX_AGE.toString()} seconds.`); + } this.installerOptions = updatedOptions; return updatedOptions; } @@ -577,9 +589,6 @@ class HttpReceiver { server; logger; constructor(options) { - if (!options.webhooksSecretToken) { - throw new HTTPReceiverConstructionError("webhooksSecretToken is a required constructor option."); - } this.options = mergeDefaultOptions(options, { endpoints: HttpReceiver.DEFAULT_ENDPOINT }); this.options.endpoints = prependSlashes(this.options.endpoints); this.logger = @@ -593,6 +602,19 @@ class HttpReceiver { canInstall() { return true; } + buildDeletedStateCookieHeader(name) { + return `${name}=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Path=/; Secure;`; + } + buildStateCookieHeader(name, value, maxAge) { + return `${name}=${value}; HttpOnly; Max-Age=${maxAge.toString()}; Path=/; Secure;`; + } + getRequestCookie(req, name) { + return req.headers.cookie + ?.split(";") + .find((cookie) => cookie.trim().startsWith(name)) + ?.split("=")[1] + ?.trim(); + } getServerCreator() { return this.hasSecureOptions() ? createServer : createServer$1; } @@ -607,8 +629,20 @@ class HttpReceiver { this.eventEmitter = eventEmitter; this.interactiveAuth = interactiveAuth; } + setResponseCookie(res, cookie) { + const existingCookies = res.getHeader("Set-Cookie") ?? []; + const cookiesArray = Array.isArray(existingCookies) ? existingCookies + : typeof existingCookies === "string" ? [existingCookies] + : [existingCookies.toString()]; + res.setHeader("Set-Cookie", [...cookiesArray, cookie]); + } + areNormalizedUrlsEqual(firstUrl, secondUrl) { + const normalizedFirstUrl = firstUrl.endsWith("/") ? firstUrl.slice(0, -1) : firstUrl; + const normalizedSecondUrl = secondUrl.endsWith("/") ? secondUrl.slice(0, -1) : secondUrl; + return normalizedFirstUrl == normalizedSecondUrl; + } start(port) { - if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port) { + if (typeof port !== "number" && isNaN(Number(port)) && !this.options.port && this.options.port !== 0) { const errorMessage = "HTTP receiver must have number-coercible port found in constructor option or method call."; this.logger.error(errorMessage); throw new HTTPReceiverPortNotNumberError(errorMessage); @@ -625,69 +659,84 @@ class HttpReceiver { // Handle interactive OAuth flow, if user is going to installPath or redirectUriPath if (interactiveAuth && interactiveAuth instanceof InteractiveAuth && interactiveAuth.installerOptions) { const { installerOptions } = interactiveAuth; - if (pathname == installerOptions.installPath) { - const authUrl = await Promise.resolve(interactiveAuth.getAuthorizationUrl()); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.installPath)) { + const { fullUrl, generatedState } = await interactiveAuth.getAuthorizationUrl(); + const stateCookie = this.buildStateCookieHeader(installerOptions.stateCookieName, generatedState, installerOptions.stateCookieMaxAge); await (installerOptions.directInstall ? - this.writeTemporaryRedirect(res, authUrl) - : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(authUrl))); + this.writeTemporaryRedirect(res, fullUrl, stateCookie) + : this.writeResponse(res, StatusCode.OK, defaultInstallTemplate(fullUrl), stateCookie)); return; } // The user has navigated to the redirect page; init the code - if (pathname === installerOptions.redirectUriPath) { - const authCode = searchParams.get("code"); - const stateCode = searchParams.get("state"); + if (this.areNormalizedUrlsEqual(pathname, installerOptions.redirectUriPath)) { + const authCodeParam = searchParams.get("code"); + const stateCodeParam = searchParams.get("state"); + const stateCodeCookie = this.getRequestCookie(req, installerOptions.stateCookieName); try { - if (!authCode || !stateCode) { + // Can't proceed if no auth code or state code in search parameters + if (!authCodeParam || !stateCodeParam) { const errorMessage = "OAuth callback did not include code and/or state in request."; this.logger.error(errorMessage); throw new ReceiverOAuthFlowError(errorMessage); } - // Wrapped in `await Promise.resolve(...)`, as method may return a `Promise` or may not. - await Promise.resolve(installerOptions.stateStore.verifyState(stateCode)); - await Promise.resolve(interactiveAuth.initRedirectCode(authCode)); - await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate()); + // Ensure that the state token is verified, according to our state store + await installerOptions.stateStore.verifyState(stateCodeParam); + // Ensure that the state token we received (in search parameters) IS THE SAME as the state cookie + if (!stateCodeCookie || stateCodeCookie !== stateCodeParam) { + const errorMessage = "The state parameter is not from this browser session."; + this.logger.error(errorMessage); + throw new ReceiverOAuthFlowError(errorMessage); + } + await interactiveAuth.initRedirectCode(authCodeParam); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.OK, defaultCallbackSuccessTemplate(), deletionStateCookie); return; } catch (err) { const htmlTemplate = isCoreError(err) ? defaultCallbackKnownErrorTemplate(err.name, err.message) : defaultCallbackUnknownErrorTemplate(); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate); + const deletionStateCookie = this.buildDeletedStateCookieHeader(installerOptions.stateCookieName); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, htmlTemplate, deletionStateCookie); return; } } } - // We currently only support a single endpoint, though this will change in the future. - if (!this.hasEndpoint(pathname)) { - await this.writeResponse(res, StatusCode.NOT_FOUND); - return; - } - // We currently only support POST requests, as that's what Zoom sends. - if (req.method !== "post" && req.method !== "POST") { - await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); - return; - } - try { - const { webhooksSecretToken } = this.options; - const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); - const processedEvent = request.processEvent(); - if (isHashedUrlValidation(processedEvent)) { - await this.writeResponse(res, StatusCode.OK, processedEvent); + // This section is only applicable if we have a webhooks secret token—if we don't, then this + // receiver is, in effect, just for OAuth usage, meaning installing and validating. + if (this.options.webhooksSecretToken) { + // We currently only support a single endpoint, though this will change in the future. + if (!this.hasEndpoint(pathname)) { + await this.writeResponse(res, StatusCode.NOT_FOUND); + return; } - else { - await this.eventEmitter?.emit(processedEvent.event, processedEvent); - await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + // We currently only support POST requests, as that's what Zoom sends. + if (req.method !== "post" && req.method !== "POST") { + await this.writeResponse(res, StatusCode.METHOD_NOT_ALLOWED); + return; } - } - catch (err) { - if (isCoreError(err, "CommonHttpRequestError")) { - await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + try { + const { webhooksSecretToken } = this.options; + const request = await CommonHttpRequest.buildFromIncomingMessage(req, webhooksSecretToken); + const processedEvent = request.processEvent(); + if (isHashedUrlValidation(processedEvent)) { + await this.writeResponse(res, StatusCode.OK, processedEvent); + } + else { + await this.eventEmitter?.emit(processedEvent.event, processedEvent); + await this.writeResponse(res, StatusCode.OK, { message: "Zoom event processed successfully." }); + } } - else { - console.error(err); - await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { - error: "An unknown error occurred. Please try again later." - }); + catch (err) { + if (isCoreError(err, "CommonHttpRequestError")) { + await this.writeResponse(res, StatusCode.BAD_REQUEST, { error: err.message }); + } + else { + console.error(err); + await this.writeResponse(res, StatusCode.INTERNAL_SERVER_ERROR, { + error: "An unknown error occurred. Please try again later." + }); + } } } })()); @@ -721,18 +770,24 @@ class HttpReceiver { resolve(); }); } - writeTemporaryRedirect(res, location) { + writeTemporaryRedirect(res, location, setCookie) { return new Promise((resolve) => { + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(StatusCode.TEMPORARY_REDIRECT, { Location: location }); res.end(() => { resolve(); }); }); } - writeResponse(res, statusCode, bodyContent) { + writeResponse(res, statusCode, bodyContent, setCookie) { return new Promise((resolve) => { const mimeType = typeof bodyContent === "object" ? "application/json" : "text/html"; bodyContent = typeof bodyContent === "object" ? JSON.stringify(bodyContent) : bodyContent; + if (setCookie) { + this.setResponseCookie(res, setCookie); + } res.writeHead(statusCode, { "Content-Type": mimeType }); res.end(bodyContent, () => { resolve(); @@ -789,7 +844,9 @@ class ProductClient { // Only create an instance of `this.receiver` if the developer did not explicitly disable it. if (!isReceiverDisabled(options)) { // Throw error if receiver enabled, but no explicit receiver or a webhooks secret token provided. - if (!hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { + // This is mainly applicable for products where we expect webhooks to be used; in events where webhooks are not + // expected, then it's perfectly fine for the developer to not provide a receiver of a webhooks secret token. + if (this.webEventConsumer && !hasExplicitReceiver(options) && !hasWebhooksSecretToken(options)) { throw new ProductClientConstructionError("Options must include a custom receiver, or a webhooks secret token."); } this.receiver = (hasExplicitReceiver(options) ? @@ -810,7 +867,7 @@ class ProductClient { } async start() { if (!this.receiver) { - throw new ReceiverInconsistentStateError("Receiver not constructed. Was disableReceiver set to true?"); + throw new ReceiverInconsistentStateError("Receiver failed to construct. Was disableReceiver set to true?"); } // Method call is wrapped in `await` and `Promise.resolve()`, as the call // may or may not return a promise. This is not required when implementing `Receiver`. @@ -818,87 +875,13 @@ class ProductClient { } } -const type = "module"; -const name = "@zoom/rivet"; -const author = "Zoom Communications, Inc."; -const contributors = [ - { - name: "James Coon", - email: "james.coon@zoom.us", - url: "https://www.npmjs.com/~jcoon97" - }, - { - name: "Will Ezrine", - email: "will.ezrine@zoom.us", - url: "https://www.npmjs.com/~wezrine" - }, - { - name: "Tommy Gaessler", - email: "tommy.gaessler@zoom.us", - url: "https://www.npmjs.com/~tommygaessler" - } -]; -const packageManager = "pnpm@9.9.0"; -const version = "0.2.2"; -const scripts = { - test: "vitest", - "test:coverage": "vitest --coverage", - "export": "rollup --config ./rollup.config.mjs", - prepare: "husky", - lint: "eslint './packages/**/*.ts' --ignore-pattern '**/*{Endpoints,EventProcessor}.ts' --ignore-pattern '**/*.{spec,test,test-d}.ts'" -}; -const devDependencies = { - "@eslint/js": "^9.12.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.0", - "@tsconfig/recommended": "^1.0.7", - "@tsconfig/strictest": "^2.0.5", - "@types/eslint__js": "^8.42.3", - "@types/node": "^22.7.5", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "2.1.3", - dotenv: "^16.4.5", - eslint: "^9.12.0", - "eslint-plugin-n": "^17.11.1", - "eslint-plugin-promise": "^7.1.0", - "get-port": "^7.1.0", - husky: "^9.1.6", - "lint-staged": "^15.2.10", - nock: "^13.5.5", - prettier: "^3.3.3", - "prettier-plugin-organize-imports": "^4.1.0", - rollup: "^4.24.0", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - semver: "^7.6.3", - supertest: "^7.0.0", - "ts-node": "^10.9.2", - tslib: "^2.7.0", - typescript: "^5.6.3", - "typescript-eslint": "^8.8.1", - vitest: "2.1.3" -}; +const version = "0.4.0"; var packageJson = { - type: type, - name: name, - author: author, - contributors: contributors, - packageManager: packageManager, - version: version, - scripts: scripts, - devDependencies: devDependencies, - "lint-staged": { - "*": "prettier --ignore-unknown --write", - "*.ts !*{Endpoints,EventProcessor}.ts !*.{spec,test,test-d}.ts": [ - "eslint --fix", - "eslint" - ] -} -}; + version: version}; +// eslint-disable-next-line no-control-regex +const ASCII_CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/; +const NON_ASCII_CHARACTERS_PATTERN = /[^\x20-\x7E]/; class WebEndpoints { /** @internal */ static DEFAULT_BASE_URL = "https://api.zoom.us/v2"; @@ -927,10 +910,23 @@ class WebEndpoints { return (async ({ path, body, query }) => await this.makeRequest(method, baseUrlOverride, urlPathBuilder(path), requestMimeType ?? WebEndpoints.DEFAULT_MIME_TYPE, body, query)).bind(this); } buildUserAgent() { - return (`rivet/${packageJson.version} ` + + const customUserAgentName = this.getCustomUserAgentName(); + const userAgent = `rivet/${packageJson.version}${customUserAgentName ? ` (${customUserAgentName})` : ""}`; + return (`${userAgent} ` + `${basename(process.title)}/${process.version.replace("v", "")} ` + `${os.platform()}/${os.release()}`); } + getCustomUserAgentName() { + const { userAgentName } = this.options; + if (!userAgentName || typeof userAgentName !== "string") { + return null; + } + return userAgentName + .replace(new RegExp(ASCII_CONTROL_CHARACTERS_PATTERN, "g"), "") + .replace(new RegExp(NON_ASCII_CHARACTERS_PATTERN, "g"), "") + .trim() + .slice(0, 100); + } getHeaders(bearerToken, contentType) { return { Accept: "application/json", @@ -1039,14 +1035,24 @@ class VideoSdkEndpoints extends WebEndpoints { getSessionLiveStreamDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/livestream` }), updateSessionLiveStream: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/livestream` }), updateSessionLivestreamStatus: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/livestream/status` }), + getSessionSIPURIWithPasscode: this.buildEndpoint({ method: "POST", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/sip_dialing` }), updateSessionStatus: this.buildEndpoint({ method: "PUT", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/status` }), + listSessionStreamingIngestions: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/stream_ingestions` }), listSessionUsers: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/users` }), listSessionUsersQoS: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/users/qos` }), getSharingRecordingDetails: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId }) => `/videosdk/sessions/${sessionId}/users/sharing` }), getSessionUserQoS: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ sessionId, userId }) => `/videosdk/sessions/${sessionId}/users/${userId}/qos` - }) + }), + listStreamIngestions: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/videosdk/stream_ingestions` }), + createStreamIngestion: this.buildEndpoint({ method: "POST", urlPathBuilder: () => `/videosdk/stream_ingestions` }), + getStreamIngestion: this.buildEndpoint({ method: "GET", urlPathBuilder: ({ streamId }) => `/videosdk/stream_ingestions/${streamId}` }), + deleteStreamIngestion: this.buildEndpoint({ + method: "DELETE", + urlPathBuilder: ({ streamId }) => `/videosdk/stream_ingestions/${streamId}` + }), + updateStreamIngestion: this.buildEndpoint({ method: "PATCH", urlPathBuilder: ({ streamId }) => `/videosdk/stream_ingestions/${streamId}` }) }; videoSDKReports = { getCloudRecordingUsageReport: this.buildEndpoint({ method: "GET", urlPathBuilder: () => `/videosdk/report/cloud_recording` }), @@ -1056,6 +1062,43 @@ class VideoSdkEndpoints extends WebEndpoints { }; } +const ALL_EVENTS = [ + "session.user_phone_callout_ringing", + "session.user_room_system_callout_ringing", + "session.recording_started", + "session.recording_resumed", + "session.live_streaming_stopped", + "session.stream_ingestion_stopped", + "session.user_room_system_callout_rejected", + "session.alert", + "session.recording_summary_completed", + "session.sharing_ended", + "session.recording_paused", + "session.ended", + "session.started", + "session.stream_ingestion_unbind", + "session.live_streaming_started", + "session.user_room_system_callout_missed", + "session.user_phone_callout_accepted", + "session.user_left", + "session.sharing_started", + "session.user_phone_callout_canceled", + "session.recording_transcript_completed", + "session.recording_deleted", + "session.user_room_system_callout_failed", + "session.recording_completed", + "session.recording_transcript_failed", + "session.recording_trashed", + "session.user_joined", + "session.stream_ingestion_started", + "session.stream_ingestion_connected", + "session.stream_ingestion_disconnected", + "session.recording_recovered", + "session.user_phone_callout_missed", + "session.user_phone_callout_rejected", + "session.user_room_system_callout_accepted", + "session.recording_stopped" +]; class VideoSdkEventProcessor extends EventManager { } @@ -1071,4 +1114,4 @@ class VideoSdkClient extends ProductClient { } } -export { ApiResponseError, AwsLambdaReceiver, AwsReceiverRequestError, ClientCredentialsRawResponseError, CommonHttpRequestError, ConsoleLogger, HTTPReceiverConstructionError, HTTPReceiverPortNotNumberError, HTTPReceiverRequestError, HttpReceiver, LogLevel, OAuthInstallerNotInitializedError, OAuthStateVerificationFailedError, OAuthTokenDoesNotExistError, OAuthTokenFetchFailedError, OAuthTokenRawResponseError, OAuthTokenRefreshFailedError, ProductClientConstructionError, ReceiverInconsistentStateError, ReceiverOAuthFlowError, S2SRawResponseError, StatusCode, VideoSdkClient, VideoSdkEndpoints, VideoSdkEventProcessor, isCoreError, isStateStore }; +export { ALL_EVENTS, ApiResponseError, AwsLambdaReceiver, AwsReceiverRequestError, ClientCredentialsRawResponseError, CommonHttpRequestError, ConsoleLogger, HTTPReceiverConstructionError, HTTPReceiverPortNotNumberError, HTTPReceiverRequestError, HttpReceiver, LogLevel, OAuthInstallerNotInitializedError, OAuthStateVerificationFailedError, OAuthTokenDoesNotExistError, OAuthTokenFetchFailedError, OAuthTokenRawResponseError, OAuthTokenRefreshFailedError, ProductClientConstructionError, ReceiverInconsistentStateError, ReceiverOAuthFlowError, S2SRawResponseError, StatusCode, VideoSdkClient, VideoSdkEndpoints, VideoSdkEventProcessor, isCoreError, isStateStore };