diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index de29ef053a..b8de1a8192 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,3 +21,8 @@ jobs: - name: Lint run: yarn lint + + # `tsc` over src, the build scripts, and the compile-time assertions in `test/types/` (which pin + # the `TimestampNS` brand and its `new Date` guard). Nothing else in PR CI typechecks. + - name: Types + run: yarn types diff --git a/CLAUDE.md b/CLAUDE.md index 457d896752..6851dfc470 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,6 +20,7 @@ Companion docs that apply to all agents: `AGENTS.md` (general agent rules) and ` | Watch dev build | `yarn start` | | Typecheck only | `yarn types` | | Typecheck the build scripts only | `yarn types:scripts` | +| Type assertions (src / built dist) | `yarn types:tests` / `yarn types:dist` | | Lint (prettier + eslint, zero warnings) | `yarn lint` | | Auto-fix lint/format | `yarn lint-fix` | | Unit tests (Vitest) | `yarn test` (alias for `yarn test-unit`) | @@ -43,6 +44,12 @@ Single test runs use Vitest's CLI directly: `yarn test-unit path/to/file.test.ts behind `stream-chat/i18n` now ships as **`@stream-io/i18n`** — but adding a new entry point without declaring its boundary in `ENTRY_BOUNDARIES` is still an error, which is the point of keeping it. +When both finish, `yarn build` runs `types:dist`: the compile-time assertions in `test/types/` checked +against the built `dist/types`, resolved through `package.json#exports` exactly as a consumer would. +It is what fails a build whose published types lost the `DateConstructor` guard +(`src/gen/models/timestamp-guard.ts`, which reaches consumers only through the type-only re-export in +`src/index.ts`). The same files run against `src/` as `types:tests`, part of `yarn types`. + `package.json#exports` routes consumers to the right bundle by condition: `node` → node-cjs, `browser`/`react-native` → browser-cjs (require) or esm (import), default → esm. The `react-native` + `require` branch must stay pointed at CJS — React Native's Jest runs CJS with `customConditions: ["react-native"]` and does not transform `node_modules`, so an `.mjs` there is a syntax error across every RN suite that touches the module. `typesVersions` mirrors the subpaths for consumers still on `moduleResolution: "node"`. There is **no `package.json#browser` field** — it used to zero Node-only deps (`crypto`, `https`, `jsonwebtoken`, `ws`, `zlib`) for browser/RN builds, but the SDK no longer imports any of them (`src/index.ts` is platform-agnostic: global `WebSocket`, global `FormData`, global `atob`). `scripts/bundle.mts` keeps a `browserIgnoreModules` hook, currently an empty array, for the day that changes. Prefer a platform global or a browser-safe dep over reintroducing a Node-only one. esbuild `define` injects two compile-time constants: `process.env.PKG_VERSION` (read from `package.json`) and `process.env.CLIENT_BUNDLE` (one of `node-cjs`, `browser-cjs`, `browser-esm`). Both are consumed by `StreamChat.getUserAgent()` to produce a bundle-aware UA string. **`tsc`-only code paths do not get this substitution** — these env vars only resolve in the esbuild bundles, so don't gate runtime logic on them in code that callers might import directly via `src/`. @@ -199,7 +206,7 @@ GitHub workflows in `.github/workflows/`: | Workflow | Trigger | What it runs | | -------------- | ------------------- | -------------------------------------------------------------- | -| `lint.yml` | PR | `yarn lint` | +| `lint.yml` | PR | `yarn lint`, then `yarn types` | | `unit.yml` | PR | `yarn test-coverage` | | `size.yml` | PR (excludes tests) | `preactjs/compressed-size-action` — reports bundle-size diff | | `pr-check.yml` | PR title change | `commitlint` on the PR title | diff --git a/dev/burst-simulator-extension/simulator.js b/dev/burst-simulator-extension/simulator.js index 3d7dfa4c9a..88eaf4e747 100644 --- a/dev/burst-simulator-extension/simulator.js +++ b/dev/burst-simulator-extension/simulator.js @@ -342,10 +342,15 @@ return text; }; + // Wire timestamps are unix nanoseconds, as the API sends them (stream-chat v10+). An ISO string here + // never becomes a finite timestamp, so it would break exactly the ordering and formatting paths a + // burst is meant to exercise. + const nowNs = () => Date.now() * 1e6; + const buildHtml = (text) => `

${escapeHtml(text).replace(/\n/g, '
')}

\n`; const buildUserPool = (size) => { - const now = new Date().toISOString(); + const now = nowNs(); const users = []; for (let i = 0; i < size; i++) { const id = `sim_user_${i}`; @@ -374,7 +379,7 @@ }; const buildMessage = (channel, user) => { - const now = new Date().toISOString(); + const now = nowNs(); const text = generateText(); return { id: uuid(), @@ -410,7 +415,7 @@ const buildMessageNewEvent = (channel, message) => ({ type: 'message.new', - created_at: new Date().toISOString(), + created_at: nowNs(), cid: channel.cid, channel_type: channel.type, channel_id: channel.id, @@ -421,7 +426,7 @@ }); const buildReactionNewEvent = (channel, message, user) => { - const now = new Date().toISOString(); + const now = nowNs(); const emoji = pick(EMOJIS); const reactionType = `emoji-${emoji.code}`; diff --git a/package.json b/package.json index d2041c29ba..d64444bcb7 100644 --- a/package.json +++ b/package.json @@ -78,9 +78,9 @@ "yaml": "^2.8.4" }, "scripts": { - "build": "rm -rf dist && concurrently 'tsc' './scripts/bundle.mts'", + "build": "rm -rf dist && concurrently 'tsc' './scripts/bundle.mts' && yarn run types:dist", "start": "concurrently 'tsc --watch' './scripts/bundle.mts --watch'", - "types": "tsc --noEmit && yarn run types:scripts", + "types": "tsc --noEmit && yarn run types:scripts && yarn run types:tests", "lint": "yarn run prettier && yarn run eslint", "lint-fix": "yarn run eslint-fix; yarn run prettier-fix", "prettier": "prettier '**/*.{json,js,mjs,mts,cjs,ts,yml,md}' --check", @@ -95,7 +95,9 @@ "postinstall": "node -e \"require('fs').existsSync('scripts/install-husky.mjs') && import('./scripts/install-husky.mjs')\"", "prepare": "yarn run build", "generate-client": "./scripts/generate-client.sh", - "types:scripts": "tsc -p tsconfig.scripts.json" + "types:scripts": "tsc -p tsconfig.scripts.json", + "types:tests": "tsc -p tsconfig.types-test.json", + "types:dist": "tsc -p tsconfig.types-test.dist.json" }, "engines": { "node": ">=22.18.0" diff --git a/scripts/generate-client.sh b/scripts/generate-client.sh index df399c7b92..e66972bbd4 100755 --- a/scripts/generate-client.sh +++ b/scripts/generate-client.sh @@ -8,9 +8,8 @@ rm -rf $OUTPUT_DIR ( cd $CHAT_DIR ; make openapi ; make -C projects/chat-manager build; build/chat-manager openapi generate-client --language ts --spec releases/v2/chat-clientside-api.yaml --output $OUTPUT_DIR --opt typed_filters=true --opt with_request_options=true --opt response_dates_as_number=true) -# apply-custom-data-types matches `export interface` / `}` anchored to column 0, -# but the generator emits them indented — format first so it can track which -# interface each `custom` field belongs to. +# apply-custom-data-types matches `export interface` / `}` anchored to column 0. +# The generator emits them there already; formatting first keeps that true. yarn exec prettier --write $OUTPUT_DIR/models/index.ts node ./scripts/apply-custom-data-types.mts -i $OUTPUT_DIR/models/index.ts diff --git a/src/CooldownTimer.ts b/src/CooldownTimer.ts index e2caca6176..48b0dcc839 100644 --- a/src/CooldownTimer.ts +++ b/src/CooldownTimer.ts @@ -1,7 +1,7 @@ import { StateStore } from '@stream-io/state-store'; import { getMessageCreatedAtTimestamp } from './pagination/paginators/MessageIntervalPaginator'; import { nowNs, nsToMs } from './utils/time'; -import type { ChannelResponse, LocalMessage } from './types'; +import type { ChannelResponse, LocalMessage, TimestampNS } from './types'; import { WithSubscriptions } from './utils/WithSubscriptions'; import type { Channel } from './channel'; @@ -18,7 +18,7 @@ export type CooldownTimerState = { * Creation timestamp of the latest message authored by the current user in this channel, in unix * nanoseconds as the API sends it. Change reported via message.new WS event. */ - ownLatestMessageTimestamp?: number; + ownLatestMessageTimestamp?: TimestampNS; /** * Remaining cooldown in whole seconds (rounded). */ @@ -141,7 +141,7 @@ export class CooldownTimer extends WithSubscriptions { * * @param timestamp - Unix nanoseconds, as the API sends it. */ - public setOwnLatestMessageTimestamp = (timestamp: number | undefined) => { + public setOwnLatestMessageTimestamp = (timestamp: TimestampNS | undefined) => { this.state.partialNext({ ownLatestMessageTimestamp: timestamp }); this.recalculate(); }; @@ -155,11 +155,11 @@ export class CooldownTimer extends WithSubscriptions { messages, }: { messages: LocalMessage[]; - }): number | undefined { + }): TimestampNS | undefined { const ownUserId = this.getOwnUserId(); if (!ownUserId) return undefined; - let latest: number | undefined; + let latest: TimestampNS | undefined; for (let i = messages.length - 1; i >= 0; i -= 1) { const message = messages[i]; if (message.user?.id !== ownUserId) continue; diff --git a/src/api-client.ts b/src/api-client.ts index 4839329586..5c5e03d607 100644 --- a/src/api-client.ts +++ b/src/api-client.ts @@ -152,8 +152,11 @@ export class ApiClient { const remaining = headers['x-ratelimit-remaining'] as string | undefined; if (remaining) rateLimit.rate_limit_remaining = parseInt(remaining, 10); - const reset = headers['x-ratelimit-reset'] as string | undefined; - if (reset) rateLimit.rate_limit_reset = new Date(reset); + // Unix SECONDS (`strconv.FormatInt(info.Reset, 10)` server-side). `new Date('1700000000')` parses the + // string as a date and is Invalid Date, so convert the number explicitly. + const reset = Number(headers['x-ratelimit-reset']); + if (Number.isFinite(reset) && reset > 0) + rateLimit.rate_limit_reset = new Date(reset * 1000); return { response_headers: headers as Record, diff --git a/src/channel.ts b/src/channel.ts index bbd50917a4..1993418058 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -20,7 +20,7 @@ import { logChatPromiseExecution, sanitizeOutgoingAttachments, } from './utils'; -import { msToNs, nowNs } from './utils/time'; +import { asTimestampNS, msToNs, nowNs } from './utils/time'; import { normalizeUploadFile } from './upload-utils'; import type { StreamChat } from './client'; import { chatLoggerSystem } from './logger'; @@ -62,6 +62,7 @@ import type { SharedLocation, StreamRequestOptions, StreamResponse, + TimestampNS, UnBanUserOptions, UpdateChannelPartialRequest, UpdateLiveLocationRequest, @@ -1153,7 +1154,7 @@ export class Channel extends WithMessageOperations(ChannelApi) { /** * Returns the mute status for the current channel. * - * @returns An object of the form `{ muted: true | false, createdAt: number | null, expiresAt: number | null }`, + * @returns An object of the form `{ muted: true | false, createdAt: TimestampNS | null, expiresAt: TimestampNS | null }`, * where the timestamps are unix nanoseconds as the API sends them. */ muteStatus() { @@ -1733,7 +1734,7 @@ export class Channel extends WithMessageOperations(ChannelApi) { * defaults to the current user's read state). * @returns Unread count. */ - countUnread(lastRead?: number | null) { + countUnread(lastRead?: TimestampNS | null) { // Nullish, not truthy: `0` is a legitimate wire timestamp (the epoch). if (lastRead == null) return this.state.unreadCount; let count = 0; @@ -2523,7 +2524,7 @@ export class Channel extends WithMessageOperations(ChannelApi) { // every "no last read" consumer already treats a missing value. if (ownUserId && countsAsOwnUnread && !currentReadState[ownUserId]) { nextReadState[ownUserId] = { - last_read: 0, + last_read: asTimestampNS(0), unread_messages: 1, user: (client.user ?? { id: ownUserId }) as UserResponse, }; diff --git a/src/channel_state.ts b/src/channel_state.ts index cae7882f48..35920c15c2 100644 --- a/src/channel_state.ts +++ b/src/channel_state.ts @@ -6,6 +6,7 @@ import type { LocalMessage, MessageResponse, PendingMessageResponse, + TimestampNS, UserResponse, } from './types'; import { AIStates } from './types'; @@ -17,13 +18,13 @@ type ChannelReadStatus = Record< string, { /** Unix nanoseconds, as the API sends it. */ - last_read: number; + last_read: TimestampNS; unread_messages: number; user: UserResponse; first_unread_message_id?: string; last_read_message_id?: string; /** Unix nanoseconds, as the API sends it. */ - last_delivered_at?: number; + last_delivered_at?: TimestampNS; last_delivered_message_id?: string; } >; @@ -110,9 +111,9 @@ export type ChannelDataState = { export type ChannelMuteStatus = { muted: boolean; /** Unix nanoseconds, as the API sends it. */ - createdAt: number | null; + createdAt: TimestampNS | null; /** Unix nanoseconds, as the API sends it. */ - expiresAt: number | null; + expiresAt: TimestampNS | null; }; /** diff --git a/src/client.ts b/src/client.ts index f757da4563..aed7c243ee 100644 --- a/src/client.ts +++ b/src/client.ts @@ -52,6 +52,7 @@ import type { SdkIdentifier, StreamChatOptions, StreamRequestOptions, + TimestampNS, TokenOrProvider, UnBanUserOptions, UpdateUserPartialRequest, @@ -122,6 +123,13 @@ export type ChannelConfigsState = { export type ClientUser = PartializeAllBut & { anon?: boolean }; +/** + * `T`, unless it is a server-sent {@link TimestampNS}: for parameters where a bare `number` means a + * relative offset rather than an instant. `any` passes through (`0 extends 1 & T`), so untyped + * callers are not suddenly rejected. + */ +type NotATimestampNS = 0 extends 1 & T ? T : T extends TimestampNS ? never : T; + export class StreamChat extends ChatApi { private static _instance?: unknown | StreamChat; // type is undefined|StreamChat, unknown is due to TS limitations with statics messageDeliveryReporter: MessageDeliveryReporter; @@ -2011,6 +2019,10 @@ export class StreamChat extends ChatApi { /** * Pins the message. * + * A `number` is a relative offset in SECONDS, so a server-sent timestamp (`message.pinned_at`, + * `message.pin_expires`) is rejected at compile time rather than read as ~1.79e18 seconds from now. + * Hand one back as a date with `nsToDate(timestamp)`. + * * @param messageOrMessageId - MessageRequest object or message ID. * @param timeoutOrExpirationDate - Expiration date or timeout. Use `number` to set the timeout * in seconds, `string` or `Date` to set the exact expiration date (optional). @@ -2021,10 +2033,13 @@ export class StreamChat extends ChatApi { * into the request (optional). * @returns The updated message response. */ - pinMessage( + pinMessage< + Expiration extends null | number | string | Date | undefined = undefined, + PinnedAt extends number | string | Date | undefined = undefined, + >( messageOrMessageId: string | { id: string }, - timeoutOrExpirationDate?: null | number | string | Date, - pinnedAt?: number | string | Date, + timeoutOrExpirationDate?: NotATimestampNS, + pinnedAt?: NotATimestampNS, requestOptions?: StreamRequestOptions, ) { const id = this._validateAndGetMessageId( diff --git a/src/gen/chat/ChatApi.ts b/src/gen/chat/ChatApi.ts index 2a27d60884..81cef6433c 100644 --- a/src/gen/chat/ChatApi.ts +++ b/src/gen/chat/ChatApi.ts @@ -109,6 +109,7 @@ import type { SyncRequest, SyncResponse, TranslateMessageRequest, + TranslateMessageResponse, TruncateChannelRequest, TruncateChannelResponse, UnblockUsersRequest, @@ -1478,7 +1479,7 @@ export class ChatApi { async translateMessage( request: TranslateMessageRequest & { id: string }, requestOptions?: StreamRequestOptions, - ): Promise> { + ): Promise> { const pathParams = { id: request?.id, }; @@ -1487,7 +1488,7 @@ export class ChatApi { }; const response = await this.apiClient.sendRequest< - StreamResponse + StreamResponse >( 'POST', '/api/v2/chat/messages/{id}/translate', @@ -2122,6 +2123,7 @@ export class ChatApi { id: request?.id, is_closed: request?.is_closed, max_votes_allowed: request?.max_votes_allowed, + team: request?.team, voting_visibility: request?.voting_visibility, options: request?.options, custom: request?.custom, diff --git a/src/gen/models/index.ts b/src/gen/models/index.ts index 1b3e375154..c66a0fbe87 100644 --- a/src/gen/models/index.ts +++ b/src/gen/models/index.ts @@ -10,6 +10,32 @@ import type { CustomThreadData, CustomUserData, } from '../../custom_types'; +declare const timestampNsBrand: unique symbol; + +/** + * A unix-nanosecond timestamp, exactly as the server sends it. + * + * NOT milliseconds. `new Date(t)` is out of range and yields an Invalid Date, whose + * `.toISOString()` then throws; `models/timestamp-guard.ts` turns that into a + * compile error. Comparing or sorting two timestamps is fine -- same unit, plain + * numbers. Everything else should go through this SDK's time helpers: + * + * nsToDate(t) a Date + * convertTimestampToDate(t) a Date, or undefined when the value is absent or NaN + * nsToMs(t) epoch ms, for arithmetic against Date.now() + * nsToRfc3339(t) an RFC3339 string keeping sub-millisecond precision + * dateToNs(d), msToNs(ms) back to a wire timestamp + * nowNs() the local clock, wire-comparable + * asTimestampNS(n) brand a number already in ns (DB rows, fixtures) -- no conversion + * + * A request date field is typed `Date`, not this -- pass `nsToDate(t)` when handing a + * server-sent timestamp back to the API, or `nsToRfc3339(t)` where precision matters. + * + * Values above `Number.MAX_SAFE_INTEGER` are quantised to ~256ns, so ordering holds + * but exact equality after a JSON round-trip does not. + */ +export type TimestampNS = number & { readonly [timestampNsBrand]: true }; + export type Filters< FilterConditions extends Record, > = QueryFilters<{ @@ -91,35 +117,26 @@ export type RequireOnlyOne = Omit & }[Keys]; export interface AIAudioConfigRequest { profile?: string; - rules?: Array; } export interface AIAudioConfigResponse { enabled: boolean; - profile: string; - rules: Array; } export interface AIImageConfig { enabled: boolean; - ocr_rules: Array; - rules: Array; - async?: boolean; } export interface AIImageLabelDefinition { description: string; - group: string; - key: string; - label: string; } @@ -127,62 +144,50 @@ export interface AIIndicatorClearEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "ai_indicator.clear" in this case */ type: string; - /** * The ID of the channel */ channel_id?: string; - /** * The type of the channel */ channel_type?: string; - /** * The CID of the channel */ cid?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface AIIndicatorStopEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "ai_indicator.stop" in this case */ type: string; - /** * The ID of the channel */ channel_id?: string; - /** * The type of the channel */ channel_type?: string; - /** * The CID of the channel */ cid?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface AIIndicatorUpdateEvent { @@ -190,64 +195,55 @@ export interface AIIndicatorUpdateEvent { * The state of the AI indicator */ ai_state: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The ID of the message */ message_id: string; - custom: CustomEventData; - /** * The type of event: "ai_indicator.update" in this case */ type: string; - /** * Optional message from the AI */ ai_message?: string; - /** * The ID of the channel */ channel_id?: string; - /** * The type of the channel */ channel_type?: string; - /** * The CID of the channel */ cid?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface AITextConfig { enabled: boolean; - profile: string; - rules: Array; - severity_rules: Array; + async?: boolean; +} +export interface AIVideoConfigRequest { async?: boolean; + enabled?: boolean; + rules?: Array; } -export interface AIVideoConfig { +export interface AIVideoConfigResponse { enabled: boolean; - rules: Array; - async?: boolean; } @@ -256,37 +252,30 @@ export interface APIError { * API error code */ code: number; - /** * Request duration */ duration: string; - /** * Message describing an error */ message: string; - /** * URL with additional information */ more_info: string; - /** * Response HTTP status code */ status_code: number; - /** * Additional error-specific information */ details: Array; - /** * Flag that indicates if the error is unrecoverable, requests that return unrecoverable errors should not be retried, this error only applies to the request that caused it */ unrecoverable?: boolean; - /** * Additional error info */ @@ -295,23 +284,16 @@ export interface APIError { export interface AWSRekognitionRule { action: 'flag' | 'shadow' | 'remove' | 'bounce' | 'bounce_flag' | 'bounce_remove'; - label: string; - min_confidence: number; - subclassifications?: Record; } export interface Action { name: string; - text: string; - type: string; - style?: string; - value?: string; } @@ -319,52 +301,41 @@ export interface ActionLogResponse { /** * Timestamp when the action was taken */ - created_at: number; - + created_at: TimestampNS; /** * Unique identifier of the action log */ id: string; - /** * Reason for the moderation action */ reason: string; - /** * Classification of who triggered the action (e.g. user, moderator, automod, api_integration) */ reporter_type: string; - /** * ID of the user who was the target of the action */ target_user_id: string; - /** * Type of moderation action */ type: string; - /** * ID of the user who performed the action */ user_id: string; - ai_providers: Array; - /** * Additional metadata about the action */ custom: Record; - review_queue_item?: ReviewQueueItemResponse; - /** * User response object */ target_user?: UserResponse; - /** * User response object */ @@ -373,17 +344,11 @@ export interface ActionLogResponse { export interface ActionSequence { action: string; - blur: boolean; - cooldown_period: number; - threshold: number; - time_window: number; - warning: boolean; - warning_text: string; } @@ -392,18 +357,15 @@ export interface AddUserGroupMembersRequest { * List of user IDs to add as members */ member_ids: Array; - /** * Whether to add the members as group admins. Defaults to false */ as_admin?: boolean; - team_id?: string; } export interface AddUserGroupMembersResponse { duration: string; - user_group?: UserGroupResponse; } @@ -412,35 +374,25 @@ export interface AppEventResponse { * boolean */ auto_translation_enabled: boolean; - /** * string */ name: string; - /** * boolean */ async_url_enrich_enabled?: boolean; - file_upload_config?: FileUploadConfig; - image_upload_config?: FileUploadConfig; } export interface AppResponseFields { async_url_enrich_enabled: boolean; - auto_translation_enabled: boolean; - id: number; - name: string; - placement: string; - file_upload_config: FileUploadConfig; - image_upload_config: FileUploadConfig; } @@ -448,18 +400,14 @@ export interface AppUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; app: AppEventResponse; - custom: CustomEventData; - /** * The type of event: "app.updated" in this case */ type: string; - - received_at?: number; + received_at?: TimestampNS; } export interface AppealItemResponse { @@ -467,100 +415,86 @@ export interface AppealItemResponse { * Reason Text of the Appeal Item */ appeal_reason: string; - /** * When the flag was created */ - created_at: number; - + created_at: TimestampNS; /** * ID of the entity */ entity_id: string; - /** * Type of entity */ entity_type: string; - id: string; - /** * Status of the Appeal Item */ status: string; - /** * When the flag was last updated */ - updated_at: number; - + updated_at: TimestampNS; /** * Text severity level assigned by the AI provider */ ai_text_severity?: string; - /** - * CID of the channel the entity belongs to, if applicable + * Detected language of the appeal_reason text itself + */ + appeal_reason_language?: string; + /** + * CID of the channel the entity belongs to (content appeals), or of the channel ban being appealed (stream:user appeals). Empty for a global ban appeal. */ channel_cid?: string; - /** * Moderation policy key that was applied */ config_key?: string; - /** * Decision Reason of the Appeal Item */ decision_reason?: string; - /** * Action recommended by the automated moderation system (e.g. flag, remove, shadow) */ recommended_action?: string; - /** * ID of the review queue item linked to this appeal, if the appeal was submitted with one */ review_queue_item_id?: string; - /** * Overall content severity score (1–100) */ severity?: number; - /** * Full chronological history of all moderation actions on the review queue item */ actions?: Array; - /** * Attachments(e.g. Images) of the Appeal Item */ attachments?: Array; - /** * Classification labels from automated and manual review */ flag_labels?: Array; - /** * Types of flags applied to the entity (e.g. user_report, bodyguard) */ flag_types?: Array; - /** * Per-provider flag records explaining why the action was taken */ flags?: Array; - + /** + * Detected languages in the content + */ + languages?: Array; entity_content?: ModerationPayload; - moderation_action?: ActionLogResponse; - original_moderation_action?: ActionLogResponse; - /** * User response object */ @@ -572,22 +506,22 @@ export interface AppealRequest { * Explanation for why the content is being appealed */ appeal_reason: string; - /** * Unique identifier of the entity being appealed */ entity_id: string; - /** * Type of entity being appealed (e.g., message, user) */ entity_type: string; - + /** + * CID of the channel ban being appealed. Only used when entity_type is stream:user; omit to appeal the global ban. + */ + channel_cid?: string; /** * ID of the review queue item (flagged message) that triggered the ban. Applicable only for user ban appeals. */ review_queue_item_id?: string; - /** * Array of Attachment URLs(e.g., images) */ @@ -599,68 +533,42 @@ export interface AppealResponse { * Unique identifier of the created Appeal item */ appeal_id: string; - duration: string; } export interface Attachment { custom: CustomAttachmentData; - asset_url?: string; - author_icon?: string; - author_link?: string; - author_name?: string; - color?: string; - fallback?: string; - footer?: string; - footer_icon?: string; - image_url?: string; - og_scrape_url?: string; - original_height?: number; - original_width?: number; - pretext?: string; - text?: string; - thumb_url?: string; - title?: string; - title_link?: string; - /** * Attachment type (e.g. image, video, url) */ type?: string; - actions?: Array; - fields?: Array; - giphy?: Images; } export interface AutomodDetailsResponse { action?: string; - original_message_type?: string; - image_labels?: Array; - message_details?: FlagMessageDetailsResponse; - /** * Result of the message moderation */ @@ -669,41 +577,31 @@ export interface AutomodDetailsResponse { export interface AutomodPlatformCircumventionConfig { enabled: boolean; - rules: Array; - async?: boolean; } export interface AutomodRule { action: 'flag' | 'shadow' | 'remove' | 'bounce' | 'bounce_flag' | 'bounce_remove'; - label: string; - threshold: number; } export interface AutomodSemanticFiltersConfig { enabled: boolean; - rules: Array; - async?: boolean; } export interface AutomodSemanticFiltersRule { action: 'flag' | 'shadow' | 'remove' | 'bounce' | 'bounce_flag' | 'bounce_remove'; - name: string; - threshold: number; } export interface AutomodToxicityConfig { enabled: boolean; - rules: Array; - async?: boolean; } @@ -712,40 +610,31 @@ export interface BanActionRequestPayload { * Also ban user from all channels this moderator creates in the future */ ban_from_future_channels?: boolean; - /** * Ban only from specific channel */ channel_ban_only?: boolean; - channel_cid?: string; - /** * Message deletion mode: soft, pruning, or hard */ - delete_messages?: 'soft' | 'pruning' | 'hard'; - /** * Whether to ban by IP address */ ip_ban?: boolean; - /** * Reason for the ban */ reason?: string; - /** * Whether this is a shadow ban */ shadow?: boolean; - /** * Optional: ban user directly without review item */ target_user_id?: string; - /** * Duration of ban in minutes */ @@ -756,35 +645,28 @@ export interface BanInfoResponse { /** * When the ban was created */ - created_at: number; - + created_at: TimestampNS; /** * The channel this ban applies to. Empty if this is an app-wide (global) ban rather than a per-channel ban. */ channel_cid?: string; - /** * When the ban expires */ - expires?: number; - + expires?: TimestampNS; /** * Reason for the ban */ reason?: string; - /** * Whether this is a shadow ban */ shadow?: boolean; - channel?: ChannelMetadata; - /** * User response object */ created_by?: UserResponse; - /** * User response object */ @@ -793,13 +675,9 @@ export interface BanInfoResponse { export interface BanOptions { delete_messages?: 'soft' | 'pruning' | 'hard'; - duration?: number; - ip_ban?: boolean; - reason?: string; - shadow_ban?: boolean; } @@ -808,29 +686,23 @@ export interface BanRequest { * ID of the user to ban */ target_user_id: string; - /** * Channel where the ban applies */ channel_cid?: string; - delete_messages?: 'soft' | 'pruning' | 'hard'; - /** * Whether to ban the user's IP address */ ip_ban?: boolean; - /** * Optional explanation for the ban */ reason?: string; - /** * Whether this is a shadow ban */ shadow?: boolean; - /** * Duration of the ban in minutes */ @@ -838,24 +710,18 @@ export interface BanRequest { } export interface BanResponse { - created_at: number; - - expires?: number; - + created_at: TimestampNS; + expires?: TimestampNS; reason?: string; - shadow?: boolean; - /** * User response object */ banned_by?: UserResponse; - /** * Represents channel in chat */ channel?: ChannelResponse; - /** * User response object */ @@ -871,11 +737,8 @@ export interface BlockActionRequestPayload { export interface BlockListConfig { enabled: boolean; - rules: Array; - async?: boolean; - match_substring?: boolean; } @@ -883,9 +746,7 @@ export interface BlockListOptions { /** * Blocklist behavior. One of: flag, block, shadow_block */ - behavior: 'flag' | 'block' | 'shadow_block'; - /** * Blocklist name */ @@ -894,43 +755,32 @@ export interface BlockListOptions { export interface BlockListResponse { is_confusable_folding_enabled: boolean; - is_leet_check_enabled: boolean; - is_plural_check_enabled: boolean; - is_substring_matching_enabled: boolean; - /** * Block list name */ name: string; - /** * Block list type. One of: regex, domain, domain_allowlist, email, email_allowlist, word */ type: string; - /** * List of words to block */ words: Array; - /** * Date/time of creation */ - created_at?: number; - + created_at?: TimestampNS; id?: string; - owner_user_id?: string; - team?: string; - /** * Date/time of the last update */ - updated_at?: number; + updated_at?: TimestampNS; } export interface BlockListRule { @@ -943,9 +793,7 @@ export interface BlockListRule { | 'bounce' | 'bounce_flag' | 'bounce_remove'; - name: string; - team: string; } @@ -961,17 +809,14 @@ export interface BlockUsersResponse { * User id who blocked another user */ blocked_by_user_id: string; - /** * User id who got blocked */ blocked_user_id: string; - /** * Timestamp when the user was blocked */ - created_at: number; - + created_at: TimestampNS; /** * Duration of the request in milliseconds */ @@ -983,19 +828,15 @@ export interface BlockedUserResponse { * ID of the user who got blocked */ blocked_user_id: string; - - created_at: number; - + created_at: TimestampNS; /** * ID of the user who blocked another user */ user_id: string; - /** * User response object */ blocked_user: UserResponse; - /** * User response object */ @@ -1004,9 +845,7 @@ export interface BlockedUserResponse { export interface BodyguardProfileSummary { name: string; - display_name?: string; - text_type?: string; } @@ -1021,9 +860,7 @@ export interface BodyguardRule { | 'bounce' | 'bounce_flag' | 'bounce_remove'; - label: string; - severity_rules: Array; } @@ -1037,7 +874,6 @@ export interface BodyguardSeverityRule { | 'bounce' | 'bounce_flag' | 'bounce_remove'; - severity: 'low' | 'medium' | 'high' | 'critical'; } @@ -1045,34 +881,27 @@ export interface BulkActionAppealsRequest { /** * Action to apply: unban, restore, unblock, mark_reviewed, or reject_appeal */ - action_type: 'unban' | 'restore' | 'unblock' | 'mark_reviewed' | 'reject_appeal'; - /** * List of appeal UUIDs to process */ appeal_ids: Array; - /** * Configuration for mark reviewed action */ mark_reviewed?: MarkReviewedRequestPayload; - /** * Configuration for rejecting an appeal */ reject_appeal?: RejectAppealRequestPayload; - /** * Configuration for restore action. State-aware: reverses whichever of a delete, a block, or a shadow block currently applies to the content (including both a delete and a block/shadow block at once). */ restore?: RestoreActionRequestPayload; - /** * Configuration for unban moderation action */ unban?: UnbanActionRequestPayload; - /** * Deprecated: use restore instead — it now also reverses a block or shadow block. Configuration for unblock action. */ @@ -1081,12 +910,10 @@ export interface BulkActionAppealsRequest { export interface BulkActionAppealsResponse { duration: string; - /** * Appeals that could not be processed, with per-item error messages */ errors: Array; - /** * Successfully processed appeals */ @@ -1095,13 +922,11 @@ export interface BulkActionAppealsResponse { export interface BulkAppealError { appeal_id: string; - error: string; } export interface BulkAppealResult { appeal_id: string; - appeal_item?: AppealItemResponse; } @@ -1117,7 +942,6 @@ export interface BulkDeleteActionConfigResponse { * Number of action configs deleted */ deleted: number; - duration: string; } @@ -1130,7 +954,6 @@ export interface BulkUpsertActionConfigRequest { export interface BulkUpsertActionConfigResponse { duration: string; - /** * The created or updated action configs in the same order as the request */ @@ -1143,31 +966,22 @@ export interface BypassActionRequest { export interface CallActionOptions { duration?: number; - flag_reason?: string; - kick_reason?: string; - mute_audio?: boolean; - mute_video?: boolean; - reason?: string; - warning_text?: string; } export interface CallCustomPropertyParameters { operator?: string; - property_key?: string; } export interface CallRuleActionSequence { violation_number?: number; - actions?: Array; - call_options?: CallActionOptions; } @@ -1177,7 +991,6 @@ export interface CallTypeRuleParameters { export interface CallViolationCountParameters { threshold?: number; - time_window?: string; } @@ -1187,153 +1000,100 @@ export interface CastPollVoteRequest { export interface ChannelConfigOverrides { blocklist?: string; - blocklist_behavior?: 'flag' | 'block'; - /** * Enable/disable message counting */ count_messages?: boolean; - /** * Overrides max message length */ max_message_length?: number; - /** * Overrides the push notification level for this channel */ - push_level?: 'all' | 'all_mentions' | 'mentions' | 'direct_mentions' | 'none'; - /** * Enables message quotes */ quotes?: boolean; - /** * Enables or disables reactions */ reactions?: boolean; - /** * Enables message replies (threads) */ replies?: boolean; - /** * Enable/disable shared locations */ shared_locations?: boolean; - /** * Enables or disables typing events */ typing_events?: boolean; - /** * Enables or disables file uploads */ uploads?: boolean; - /** * Enables or disables URL enrichment */ url_enrichment?: boolean; - /** * Enable/disable user message reminders */ user_message_reminders?: boolean; - /** * List of commands that channel supports */ commands?: Array; - chat_preferences?: ChatPreferences; - grants?: Record>; } export interface ChannelConfigWithInfo { automod: 'disabled' | 'simple' | 'AI'; - automod_behavior: 'flag' | 'block' | 'shadow_block'; - connect_events: boolean; - count_messages: boolean; - - created_at: number; - + created_at: TimestampNS; custom_events: boolean; - delivery_events: boolean; - mark_messages_pending: boolean; - max_message_length: number; - message_retention: string; - mutes: boolean; - name: string; - polls: boolean; - push_notifications: boolean; - quotes: boolean; - reactions: boolean; - read_events: boolean; - reminders: boolean; - replies: boolean; - search: boolean; - shared_locations: boolean; - skip_last_msg_update_for_system_msgs: boolean; - typing_events: boolean; - - updated_at: number; - + updated_at: TimestampNS; uploads: boolean; - url_enrichment: boolean; - user_message_reminders: boolean; - commands: Array; - blocklist?: string; - blocklist_behavior?: 'flag' | 'block' | 'shadow_block'; - partition_size?: number; - partition_ttl?: string; - push_level?: 'all' | 'all_mentions' | 'mentions' | 'direct_mentions' | 'none'; - allowed_flag_reasons?: Array; - blocklists?: Array; - /** * Sets thresholds for AI moderation */ automod_thresholds?: Thresholds; - chat_preferences?: ChatPreferences; - grants?: Record>; } @@ -1342,17 +1102,14 @@ export interface ChannelContextResponse { * Channel CID (:) */ cid: string; - /** * Channel ID */ id: string; - /** * Channel type */ type: string; - /** * User response object */ @@ -1363,51 +1120,39 @@ export interface ChannelCreatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * The type of event: "channel.created" in this case */ type: string; - /** * The ID of the channel which was created */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was created */ channel_type?: string; - /** * The CID of the channel which was created */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } @@ -1415,51 +1160,39 @@ export interface ChannelDeletedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * The type of event: "channel.deleted" in this case */ type: string; - /** * The ID of the channel which was deleted */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was deleted */ channel_type?: string; - /** * The CID of the channel which was deleted */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } @@ -1467,31 +1200,25 @@ export interface ChannelFrozenEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "channel.frozen" in this case */ type: string; - /** * The ID of the channel which was frozen */ channel_id?: string; - /** * The type of the channel which was frozen */ channel_type?: string; - /** * The CID of the channel which was frozen */ cid?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface ChannelGetOrCreateRequest { @@ -1499,35 +1226,26 @@ export interface ChannelGetOrCreateRequest { * Whether this channel will be hidden for the user who created the channel or not */ hide_for_creator?: boolean; - /** * Fetch user presence info */ presence?: boolean; - /** * Refresh channel state */ state?: boolean; - thread_unread_counts?: boolean; - /** * Start watching the channel */ watch?: boolean; - /** * Top-level keys of the message sender's channel-member custom data to include under member.custom (max 8 keys, 64 chars each) */ member_custom_include?: Array; - data?: ChannelInput; - members?: PaginationParams; - messages?: MessagePaginationParams; - watchers?: PaginationParams; } @@ -1536,55 +1254,42 @@ export interface ChannelHiddenEvent { * Whether the history was cleared */ clear_history: boolean; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * The type of event: "channel.hidden" in this case */ type: string; - /** * The ID of the channel which was hidden */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was hidden */ channel_type?: string; - /** * The CID of the channel which was hidden */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } @@ -1593,72 +1298,51 @@ export interface ChannelInput { * Enable or disable auto translation */ auto_translation_enabled?: boolean; - /** * Language (or comma-separated list of languages) to translate to when auto translation is active */ auto_translation_language?: string; - created_by_id?: string; - disabled?: boolean; - /** * Freeze or unfreeze the channel */ frozen?: boolean; - /** * Team the channel belongs to (if multi-tenant mode is enabled) */ team?: string; - truncated_by_id?: string; - filter_tags?: Array; - invites?: Array; - members?: Array; - /** * Channel configuration overrides */ config_overrides?: ChannelConfigOverrides; - /** * User request object */ created_by?: UserRequest; - custom?: CustomChannelData; } export interface ChannelInputRequest { auto_translation_enabled?: boolean; - auto_translation_language?: string; - disabled?: boolean; - frozen?: boolean; - team?: string; - invites?: Array; - members?: Array; - /** * Channel configuration overrides */ config_overrides?: ConfigOverridesRequest; - /** * User request object */ created_by?: UserRequest; - custom?: CustomChannelData; } @@ -1666,44 +1350,34 @@ export interface ChannelKickedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "channel.kicked" in this case */ type: string; - /** * The ID of the channel which was kicked */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was kicked */ channel_type?: string; - /** * The CID of the channel which was kicked */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; } @@ -1712,12 +1386,10 @@ export interface ChannelMemberPartialResponse { * Role of the member in the channel */ channel_role: string; - /** * Whether the user muted notifications for this channel */ notifications_muted: boolean; - /** * Channel-member custom fields projected via `member_custom_include` */ @@ -1729,11 +1401,8 @@ export interface ChannelMemberRequest { * Role of the member in the channel */ channel_role?: string; - user_id?: string; - custom?: CustomMemberData; - user?: MemberUserRequest; } @@ -1742,83 +1411,62 @@ export interface ChannelMemberResponse { * Whether member is banned this channel or not */ banned: boolean; - /** * Role of the member in the channel */ channel_role: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; notifications_muted: boolean; - /** * Whether member is shadow banned in this channel or not */ shadow_banned: boolean; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; custom: CustomMemberData; - - archived_at?: number; - + archived_at?: TimestampNS; /** * Expiration date of the ban */ - ban_expires?: number; - + ban_expires?: TimestampNS; /** * Whether the member's ban also applies to channels the channel's creator will create in the future (an active future channel ban by the creator targets this member) */ ban_from_future_channels?: boolean; - - deleted_at?: number; - + deleted_at?: TimestampNS; /** * Expiration date of the future channel ban; absent when the future channel ban is permanent */ - future_channel_ban_expires?: number; - + future_channel_ban_expires?: TimestampNS; /** * Date when invite was accepted */ - invite_accepted_at?: number; - + invite_accepted_at?: TimestampNS; /** * Date when invite was rejected */ - invite_rejected_at?: number; - + invite_rejected_at?: TimestampNS; /** * Whether member was invited or not */ invited?: boolean; - /** * Whether member is channel moderator or not */ is_moderator?: boolean; - - pinned_at?: number; - + pinned_at?: TimestampNS; /** * Permission level of the member in the channel (DEPRECATED: use channel_role instead). One of: member, moderator, admin, owner */ role?: string; - status?: string; - user_id?: string; - deleted_messages?: Array; - /** * User response object */ @@ -1827,27 +1475,18 @@ export interface ChannelMemberResponse { export interface ChannelMessageCountRuleParameters { operator?: string; - threshold?: number; } export interface ChannelMetadata { cid: string; - id: string; - type: string; - custom: CustomChannelData; - - last_message_at?: number; - + last_message_at?: TimestampNS; member_count?: number; - message_count?: number; - push_level?: string; - team?: string; } @@ -1855,23 +1494,19 @@ export interface ChannelMute { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; /** * Date/time of mute expiration */ - expires?: number; - + expires?: TimestampNS; /** * Represents channel in chat */ channel?: ChannelResponse; - /** * User response object */ @@ -1928,9 +1563,7 @@ export type ChannelOwnCapability = export interface ChannelPushPreferencesResponse { chat_level?: string; - - disabled_until?: number; - + disabled_until?: TimestampNS; chat_preferences?: ChatPreferencesResponse; } @@ -1939,131 +1572,104 @@ export interface ChannelResponse { * Channel CID (:) */ cid: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; disabled: boolean; - /** * Whether channel is frozen or not */ frozen: boolean; - /** * Channel unique ID */ id: string; - /** * Type of the channel */ type: string; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; /** * Custom data for this object */ custom: CustomChannelData; - /** * Whether auto translation is enabled or not */ auto_translation_enabled?: boolean; - /** * Language (or comma-separated list of languages) to translate to when auto translation is active */ auto_translation_language?: string; - /** * Whether this channel is blocked by current user or not */ blocked?: boolean; - /** * Cooldown period after sending each message */ cooldown?: number; - /** * Date/time of deletion */ - deleted_at?: number; - + deleted_at?: TimestampNS; /** * Whether this channel is hidden by current user or not */ hidden?: boolean; - /** * Date since when the message history is accessible */ - hide_messages_before?: number; - + hide_messages_before?: TimestampNS; /** * Date of the last message sent */ - last_message_at?: number; - + last_message_at?: TimestampNS; /** * Number of members in the channel */ member_count?: number; - /** * Number of messages in the channel */ message_count?: number; - /** * Date of mute expiration */ - mute_expires_at?: number; - + mute_expires_at?: TimestampNS; /** * Whether this channel is muted or not */ muted?: boolean; - /** * Team the channel belongs to (multi-tenant only) */ team?: string; - /** * Date of the latest truncation of the channel */ - truncated_at?: number; - + truncated_at?: TimestampNS; /** * List of filter tags associated with the channel */ filter_tags?: Array; - /** * List of channel members (max 100) */ members?: Array; - /** * List of channel capabilities of authenticated user */ own_capabilities?: Array; - config?: ChannelConfigWithInfo; - /** * User response object */ created_by?: UserResponse; - /** * User response object */ @@ -2072,38 +1678,23 @@ export interface ChannelResponse { export interface ChannelStateResponse { duration: string; - members: Array; - messages: Array; - pinned_messages: Array; - threads: Array; - hidden?: boolean; - - hide_messages_before?: number; - + hide_messages_before?: TimestampNS; watcher_count?: number; - active_live_locations?: Array; - pending_messages?: Array; - read?: Array; - watchers?: Array; - /** * Represents channel in chat */ channel?: ChannelResponse; - draft?: DraftResponse; - membership?: ChannelMemberResponse; - push_preferences?: ChannelPushPreferencesResponse; } @@ -2112,63 +1703,49 @@ export interface ChannelStateResponseFields { * List of channel members */ members: Array; - /** * List of channel messages */ messages: Array; - /** * List of pinned messages in the channel */ pinned_messages: Array; - threads: Array; - /** * Whether this channel is hidden or not */ hidden?: boolean; - /** * Messages before this date are hidden from the user */ - hide_messages_before?: number; - + hide_messages_before?: TimestampNS; /** * Number of channel watchers */ watcher_count?: number; - /** * Active live locations in the channel */ active_live_locations?: Array; - /** * Pending messages that this user has sent */ pending_messages?: Array; - /** * List of read states */ read?: Array; - /** * List of user who is watching the channel */ watchers?: Array; - /** * Represents channel in chat */ channel?: ChannelResponse; - draft?: DraftResponse; - membership?: ChannelMemberResponse; - push_preferences?: ChannelPushPreferencesResponse; } @@ -2178,58 +1755,44 @@ export interface ChannelTruncatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * The type of event: "channel.truncated" in this case */ type: string; - /** * The ID of the channel which was truncated */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was truncated */ channel_type?: string; - /** * The CID of the channel which was truncated */ cid?: string; - message_id?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - /** * Represents any chat message */ message?: MessageResponse; - user?: UserResponseCommonFields; } @@ -2237,89 +1800,69 @@ export interface ChannelUnFrozenEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "channel.unfrozen" in this case */ type: string; - /** * The ID of the channel which was unfrozen */ channel_id?: string; - /** * The type of the channel which was unfrozen */ channel_type?: string; - /** * The CID of the channel which was unfrozen */ cid?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface ChannelUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * The type of event: "channel.updated" in this case */ type: string; - /** * The ID of the channel which was updated */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was updated */ channel_type?: string; - /** * The CID of the channel which was updated */ cid?: string; - message_id?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - /** * Represents any chat message */ message?: MessageResponse; - user?: UserResponseCommonFields; } @@ -2327,287 +1870,178 @@ export interface ChannelVisibleEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * The type of event: "channel.visible" in this case */ type: string; - /** * The ID of the channel which was shown */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was shown */ channel_type?: string; - /** * The CID of the channel which was shown */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } export interface ChatDraftPayloadResponse { id: string; - text: string; - custom: CustomMessageData; - html?: string; - mml?: string; - parent_id?: string; - poll_id?: string; - quoted_message_id?: string; - show_in_channel?: boolean; - silent?: boolean; - type?: string; - attachments?: Array; - mentioned_users?: Array; } export interface ChatDraftResponse { channel_cid: string; - - created_at: number; - + created_at: TimestampNS; message: ChatDraftPayloadResponse; - parent_id?: string; - parent_message?: ChatMessageResponse; - quoted_message?: ChatMessageResponse; } export interface ChatMessageResponse { cid: string; - - created_at: number; - + created_at: TimestampNS; deleted_reply_count: number; - html: string; - id: string; - mentioned_channel: boolean; - mentioned_here: boolean; - pinned: boolean; - reply_count: number; - shadowed: boolean; - silent: boolean; - text: string; - type: string; - - updated_at: number; - + updated_at: TimestampNS; attachments: Array; - latest_reactions: Array; - mentioned_users: Array; - own_reactions: Array; - restricted_visibility: Array; - custom: CustomMessageData; - reaction_counts: Record; - reaction_scores: Record; - /** * User response object */ user: UserResponse; - command?: string; - - deleted_at?: number; - + deleted_at?: TimestampNS; deleted_for_me?: boolean; - - message_text_updated_at?: number; - + message_text_updated_at?: TimestampNS; mml?: string; - parent_id?: string; - - pin_expires?: number; - - pinned_at?: number; - + pin_expires?: TimestampNS; + pinned_at?: TimestampNS; poll_id?: string; - quoted_message_id?: string; - show_in_channel?: boolean; - mentioned_group_ids?: Array; - mentioned_groups?: Array; - mentioned_roles?: Array; - thread_participants?: Array; - draft?: ChatDraftResponse; - i18n?: Record; - image_labels?: Record>; - member?: ChannelMemberPartialResponse; - moderation?: ChatModerationV2Response; - /** * User response object */ pinned_by?: UserResponse; - poll?: PollResponseData; - quoted_message?: ChatMessageResponse; - reaction_groups?: Record; - reminder?: ChatReminderResponseData; - shared_location?: ChatSharedLocationResponseData; } export interface ChatModerationV2Response { action: string; - original_text: string; - blocklist_matched?: string; - platform_circumvented?: boolean; - semantic_filter_matched?: string; - blocklists_matched?: Array; - image_harms?: Array; - text_harms?: Array; } export interface ChatPreferences { channel_mentions?: string; - default_preference?: string; - direct_mentions?: string; - distinct_channel_messages?: string; - group_mentions?: string; - here_mentions?: string; - role_mentions?: string; - thread_replies?: string; } export interface ChatPreferencesInput { channel_mentions?: 'all' | 'none'; - default_preference?: 'all' | 'none'; - direct_mentions?: 'all' | 'none'; - group_mentions?: 'all' | 'none'; - here_mentions?: 'all' | 'none'; - role_mentions?: 'all' | 'none'; - thread_replies?: 'all' | 'none'; } export interface ChatPreferencesResponse { channel_mentions?: string; - default_preference?: string; - direct_mentions?: string; - group_mentions?: string; - here_mentions?: string; - role_mentions?: string; - thread_replies?: string; } export interface ChatReactionGroupResponse { count: number; - - first_reaction_at: number; - - last_reaction_at: number; - + first_reaction_at: TimestampNS; + last_reaction_at: TimestampNS; sum_scores: number; - latest_reactions_by: Array; } export interface ChatReactionGroupUserResponse { - created_at: number; - + created_at: TimestampNS; user_id: string; - /** * User response object */ @@ -2615,20 +2049,13 @@ export interface ChatReactionGroupUserResponse { } export interface ChatReactionResponse { - created_at: number; - + created_at: TimestampNS; message_id: string; - score: number; - type: string; - - updated_at: number; - + updated_at: TimestampNS; user_id: string; - custom: CustomReactionData; - /** * User response object */ @@ -2637,19 +2064,12 @@ export interface ChatReactionResponse { export interface ChatReminderResponseData { channel_cid: string; - - created_at: number; - + created_at: TimestampNS; message_id: string; - - updated_at: number; - + updated_at: TimestampNS; user_id: string; - - remind_at?: number; - + remind_at?: TimestampNS; message?: ChatMessageResponse; - /** * User response object */ @@ -2658,35 +2078,22 @@ export interface ChatReminderResponseData { export interface ChatSharedLocationResponseData { channel_cid: string; - - created_at: number; - + created_at: TimestampNS; created_by_device_id: string; - latitude: number; - longitude: number; - message_id: string; - - updated_at: number; - + updated_at: TimestampNS; user_id: string; - - end_at?: number; - + end_at?: TimestampNS; message?: ChatMessageResponse; } export interface ClosedCaptionRuleParameters { severity?: string; - threshold?: number; - time_window?: string; - harm_labels?: Array; - llm_harm_labels?: Record; } @@ -2695,31 +2102,26 @@ export interface Command { * Arguments help text, shown in commands auto-completion */ args: string; - /** * Description, shown in commands auto-completion */ description: string; - /** * Unique command name */ name: string; - /** * Set name used for grouping commands */ set: string; - /** * Date/time of creation */ - created_at?: number; - + created_at?: TimestampNS; /** * Date/time of the last update */ - updated_at?: number; + updated_at?: TimestampNS; } export interface ConfigOverridesRequest { @@ -2727,72 +2129,56 @@ export interface ConfigOverridesRequest { * Blocklist name */ blocklist?: string; - /** * Blocklist behavior. One of: flag, block */ - blocklist_behavior?: 'flag' | 'block'; - /** * Enable/disable message counting */ count_messages?: boolean; - /** * Maximum message length */ max_message_length?: number; - push_level?: 'all' | 'all_mentions' | 'mentions' | 'direct_mentions' | 'none'; - /** * Enable/disable quotes */ quotes?: boolean; - /** * Enable/disable reactions */ reactions?: boolean; - /** * Enable/disable replies */ replies?: boolean; - /** * Enable/disable shared locations */ shared_locations?: boolean; - /** * Enable/disable typing events */ typing_events?: boolean; - /** * Enable/disable uploads */ uploads?: boolean; - /** * Enable/disable URL enrichment */ url_enrichment?: boolean; - /** * Enable/disable user message reminders */ user_message_reminders?: boolean; - /** * List of available commands */ commands?: Array; - chat_preferences?: ChatPreferences; - /** * Permission grants modifiers */ @@ -2804,104 +2190,73 @@ export interface ConfigResponse { * Whether moderation should be performed asynchronously */ async: boolean; - /** * When the configuration was created */ - created_at: number; - + created_at: TimestampNS; /** * Unique identifier for the moderation configuration */ key: string; - /** * Team associated with the configuration */ team: string; - /** * When the configuration was last updated */ - updated_at: number; - + updated_at: TimestampNS; supported_video_call_harm_types: Array; - /** * Configurable image moderation label definitions for dashboard rendering */ ai_image_label_definitions?: Array; - /** * Names of Bodyguard credential profiles registered on this app. The dashboard uses this list to render the profile picker on the AI Text section. */ available_bodyguard_profiles?: Array; - ai_audio_config?: AIAudioConfigResponse; - ai_image_config?: AIImageConfig; - /** * Available L2 subclassifications per L1 image moderation label, based on the active provider */ ai_image_subclassifications?: Record>; - ai_text_config?: AITextConfig; - - ai_video_config?: AIVideoConfig; - + ai_video_config?: AIVideoConfigResponse; automod_platform_circumvention_config?: AutomodPlatformCircumventionConfig; - automod_semantic_filters_config?: AutomodSemanticFiltersConfig; - automod_toxicity_config?: AutomodToxicityConfig; - block_list_config?: BlockListConfig; - flood_config?: FloodConfig; - llm_config?: LLMConfig; - velocity_filter_config?: VelocityFilterConfig; - video_call_rule_config?: VideoCallRuleConfig; } export interface ConnectUserDetailsRequest { id: string; - image?: string; - invisible?: boolean; - language?: string; - name?: string; - custom?: CustomUserData; - privacy_settings?: PrivacySettingsResponse; } export interface ContentCountRuleParameters { threshold?: number; - time_window?: string; } export interface ContentCustomPropertyCountParameters { operator?: string; - property_key?: string; - threshold?: number; - time_window?: string; } export interface ContentCustomPropertyParameters { operator?: string; - property_key?: string; } @@ -2910,26 +2265,18 @@ export interface CreateBlockListRequest { * Block list name */ name: string; - /** * List of words to block */ words: Array; - is_confusable_folding_enabled?: boolean; - is_leet_check_enabled?: boolean; - is_plural_check_enabled?: boolean; - is_substring_matching_enabled?: boolean; - team?: string; - /** * Block list type. One of: regex, domain, domain_allowlist, email, email_allowlist, word */ - type?: 'regex' | 'domain' | 'domain_allowlist' | 'email' | 'email_allowlist' | 'word'; } @@ -2938,7 +2285,6 @@ export interface CreateBlockListResponse { * Duration of the request in milliseconds */ duration: string; - /** * Block list contains restricted words */ @@ -2950,23 +2296,18 @@ export interface CreateDeviceRequest { * Device ID */ id: string; - /** * Push provider */ - push_provider: 'firebase' | 'apn' | 'huawei' | 'xiaomi'; - /** * Stable physical device identifier used to deduplicate pushes across push providers (e.g. APNs VoIP and Firebase on the same iOS device). Distinct from 'id', which is the push token. */ hardware_id?: string; - /** * Push provider name */ push_provider_name?: string; - /** * When true the token is for Apple VoIP push notifications */ @@ -2985,7 +2326,6 @@ export interface CreateDraftResponse { * Duration of the request in milliseconds */ duration: string; - draft: DraftResponse; } @@ -3001,12 +2341,10 @@ export interface CreateGuestResponse { * the access token to authenticate the user */ access_token: string; - /** * Duration of the request in milliseconds */ duration: string; - /** * User response object */ @@ -3018,7 +2356,6 @@ export interface CreatePollOptionRequest { * Option text */ text: string; - /** * Custom data for this object */ @@ -3030,40 +2367,31 @@ export interface CreatePollRequest { * The name of the poll */ name: string; - /** * Indicates whether users can suggest user defined answers */ allow_answers?: boolean; - allow_user_suggested_options?: boolean; - /** * A description of the poll */ description?: string; - /** * Indicates whether users can cast multiple votes */ enforce_unique_vote?: boolean; - id?: string; - /** * Indicates whether the poll is open for voting */ is_closed?: boolean; - /** * Indicates the maximum amount of votes a user can cast */ max_votes_allowed?: number; - + team?: string; voting_visibility?: 'anonymous' | 'public'; - options?: Array; - /** * Custom data for this object */ @@ -3072,13 +2400,9 @@ export interface CreatePollRequest { export interface CreateQueueRequest { name: string; - type: 'personal_view' | 'operational_queue'; - description?: string; - sort?: Array>; - filters?: Record; } @@ -3091,7 +2415,6 @@ export interface CreateReminderResponse { * Duration of the request in milliseconds */ duration: string; - reminder: ReminderResponseData; } @@ -3100,22 +2423,18 @@ export interface CreateUserGroupRequest { * The user friendly name of the user group */ name: string; - /** * An optional description for the group */ description?: string; - /** * Optional user group ID. If not provided, a UUID v7 will be generated */ id?: string; - /** * Optional team ID to scope the group to a team */ team_id?: string; - /** * Optional initial list of user IDs to add as members */ @@ -3124,7 +2443,6 @@ export interface CreateUserGroupRequest { export interface CreateUserGroupResponse { duration: string; - user_group?: UserGroupResponse; } @@ -3133,7 +2451,6 @@ export interface CustomActionRequestPayload { * Custom action identifier */ id?: string; - /** * Custom action options */ @@ -3141,13 +2458,10 @@ export interface CustomActionRequestPayload { } export interface CustomEvent { - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - type: string; - - received_at?: number; + received_at?: TimestampNS; } export interface Data { @@ -3159,7 +2473,6 @@ export interface DeleteActionConfigResponse { * Number of action configs deleted (0 or 1) */ deleted: number; - duration: string; } @@ -3168,17 +2481,14 @@ export interface DeleteActivityRequestPayload { * ID of the activity to delete (alternative to item_id) */ entity_id?: string; - /** * Type of the entity (required for delete_activity to distinguish v2 vs v3) */ entity_type?: string; - /** * Whether to permanently delete the activity */ hard_delete?: boolean; - /** * Reason for deletion */ @@ -3190,7 +2500,6 @@ export interface DeleteChannelResponse { * Duration of the request in milliseconds */ duration: string; - /** * Represents channel in chat */ @@ -3202,9 +2511,8 @@ export interface DeleteChannelsRequest { * All channels that should be deleted */ cids: Array; - /** - * Specify if channels and all ressources should be hard deleted + * Server-side only. When true, the channels and all their resources are permanently deleted instead of soft-deleted. */ hard_delete?: boolean; } @@ -3214,9 +2522,7 @@ export interface DeleteChannelsResponse { * Duration of the request in milliseconds */ duration: string; - task_id?: string; - /** * Map of channel IDs and their deletion results */ @@ -3225,7 +2531,6 @@ export interface DeleteChannelsResponse { export interface DeleteChannelsResultResponse { status: string; - error?: string; } @@ -3234,17 +2539,14 @@ export interface DeleteCommentRequestPayload { * ID of the comment to delete (alternative to item_id) */ entity_id?: string; - /** * Type of the entity */ entity_type?: string; - /** * Whether to permanently delete the comment */ hard_delete?: boolean; - /** * Reason for deletion */ @@ -3256,17 +2558,14 @@ export interface DeleteMessageRequestPayload { * ID of the message to delete (alternative to item_id) */ entity_id?: string; - /** * Type of the entity */ entity_type?: string; - /** * Whether to permanently delete the message */ hard_delete?: boolean; - /** * Reason for deletion */ @@ -3278,7 +2577,6 @@ export interface DeleteMessageResponse { * Duration of the request in milliseconds */ duration: string; - /** * Represents any chat message */ @@ -3296,17 +2594,14 @@ export interface DeleteReactionRequestPayload { * ID of the reaction to delete (alternative to item_id) */ entity_id?: string; - /** * Type of the entity */ entity_type?: string; - /** * Whether to permanently delete the reaction */ hard_delete?: boolean; - /** * Reason for deletion */ @@ -3318,12 +2613,10 @@ export interface DeleteReactionResponse { * Duration of the request in milliseconds */ duration: string; - /** * Represents any chat message */ message: MessageResponse; - reaction: ReactionResponse; } @@ -3338,29 +2631,23 @@ export interface DeleteUserMessagesRequestPayload { /** * Message deletion mode: soft, pruning, or hard */ - delete_messages: 'soft' | 'pruning' | 'hard'; - /** * Optional: scope deletion to a single channel (alternative to app-wide deletion) */ channel_cid?: string; - /** * Whether to also delete the user's reactions on other users' messages */ delete_reactions?: boolean; - /** * ID of the user whose messages should be deleted (alternative to item_id) */ entity_id?: string; - /** * Type of the entity */ entity_type?: string; - /** * Reason for the deletion */ @@ -3372,32 +2659,26 @@ export interface DeleteUserRequestPayload { * Also delete all user conversations */ delete_conversation_channels?: boolean; - /** * Delete flagged feeds content */ delete_feeds_content?: boolean; - /** * ID of the user to delete (alternative to item_id) */ entity_id?: string; - /** * Type of the entity */ entity_type?: string; - /** * Whether to permanently delete the user */ hard_delete?: boolean; - /** * Also delete all user messages */ mark_messages_deleted?: boolean; - /** * Reason for deletion */ @@ -3406,7 +2687,6 @@ export interface DeleteUserRequestPayload { export interface DeliveredMessagePayload { cid?: string; - id?: string; } @@ -3418,43 +2698,35 @@ export interface DeviceResponse { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Device ID */ id: string; - /** * Push provider */ push_provider: string; - /** * User ID */ user_id: string; - /** * Whether device is disabled or not */ disabled?: boolean; - /** * Reason explaining why device had been disabled */ disabled_reason?: string; - /** * Stable physical device identifier used to deduplicate pushes across push providers */ hardware_id?: string; - /** * Push provider name */ push_provider_name?: string; - /** * When true the token is for Apple VoIP push notifications */ @@ -3465,27 +2737,21 @@ export interface DraftDeletedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "draft.deleted" in this case */ type: string; - /** * The CID of the channel where the draft was created */ cid?: string; - /** * The ID of the parent message */ parent_id?: string; - - received_at?: number; - + received_at?: TimestampNS; draft?: DraftResponse; } @@ -3494,56 +2760,44 @@ export interface DraftPayloadResponse { * Message ID is unique string identifier of the message */ id: string; - /** * Text of the message */ text: string; - custom: CustomMessageData; - /** * Contains HTML markup of the message */ html?: string; - /** * MML content of the message */ mml?: string; - /** * ID of parent message (thread) */ parent_id?: string; - /** * Identifier of the poll to include in the message */ poll_id?: string; - quoted_message_id?: string; - /** * Whether thread reply should be shown in the channel as well */ show_in_channel?: boolean; - /** * Whether message is silent or not */ silent?: boolean; - /** * Contains type of the message. One of: regular, system */ type?: string; - /** * Array of message attachments */ attachments?: Array; - /** * List of mentioned users */ @@ -3552,26 +2806,20 @@ export interface DraftPayloadResponse { export interface DraftResponse { channel_cid: string; - - created_at: number; - + created_at: TimestampNS; /** * Contains the draft message content */ message: DraftPayloadResponse; - parent_id?: string; - /** * Represents channel in chat */ channel?: ChannelResponse; - /** * Represents any chat message */ parent_message?: MessageResponse; - /** * Represents any chat message */ @@ -3582,81 +2830,52 @@ export interface DraftUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "draft.updated" in this case */ type: string; - /** * The CID of the channel where the draft was created/updated */ cid?: string; - /** * The ID of the parent message */ parent_id?: string; - - received_at?: number; - + received_at?: TimestampNS; draft?: DraftResponse; } export interface EnrichedActivity { foreign_id?: string; - id?: string; - score?: number; - verb?: string; - to?: Array; - actor?: Data; - latest_reactions?: Record>; - object?: Data; - origin?: Data; - own_reactions?: Record>; - reaction_counts?: Record; - target?: Data; } export interface EnrichedReaction { activity_id: string; - kind: string; - user_id: string; - id?: string; - parent?: string; - target_feeds?: Array; - children_counts?: Record; - created_at?: Time; - data?: Record; - latest_children?: Record>; - own_children?: Record>; - updated_at?: Time; - user?: Data; } @@ -3665,51 +2884,31 @@ export interface EntityCreatorResponse { * Number of minor actions performed on the user */ ban_count: number; - banned: boolean; - - created_at: number; - + created_at: TimestampNS; /** * Number of major actions performed on the user */ deleted_content_count: number; - /** * Number of flag actions performed on the user */ flagged_count: number; - id: string; - language: string; - online: boolean; - role: string; - - updated_at: number; - + updated_at: TimestampNS; blocked_user_ids: Array; - teams: Array; - custom: CustomUserData; - avg_response_time?: number; - - deactivated_at?: number; - - deleted_at?: number; - + deactivated_at?: TimestampNS; + deleted_at?: TimestampNS; image?: string; - - last_active?: number; - + last_active?: TimestampNS; name?: string; - - revoke_tokens_issued_before?: number; - + revoke_tokens_issued_before?: TimestampNS; teams_role?: Record; } @@ -3718,12 +2917,10 @@ export interface EscalatePayload { * Additional context for the reviewer */ notes?: string; - /** * Priority of the escalation (low, medium, high) */ priority?: string; - /** * Reason for the escalation (from configured escalation_reasons) */ @@ -3732,17 +2929,13 @@ export interface EscalatePayload { export interface EscalationMetadata { notes?: string; - priority?: string; - reason?: string; } export interface EventRequest { type: string; - parent_id?: string; - custom?: CustomEventData; } @@ -3751,7 +2944,6 @@ export interface EventResponse { * Duration of the request in milliseconds */ duration: string; - /** * The discriminator object for all websocket events, it maps events' payload to the final type */ @@ -3760,141 +2952,92 @@ export interface EventResponse { export interface FeedsActivityLocation { lat: number; - lng: number; } export interface FeedsBookmarkResponse { - created_at: number; - + created_at: TimestampNS; object_id: string; - object_type: string; - - updated_at: number; - + updated_at: TimestampNS; /** * User response object */ user: UserResponse; - activity_id?: string; - custom?: Record; } export interface FeedsEnrichedCollectionResponse { - created_at: number; - + created_at: TimestampNS; id: string; - name: string; - status: string; - - updated_at: number; - + updated_at: TimestampNS; user_id: string; - custom: Record; } export interface FeedsFeedResponse { activity_count: number; - - created_at: number; - + created_at: TimestampNS; description: string; - feed: string; - follower_count: number; - following_count: number; - group_id: string; - id: string; - member_count: number; - name: string; - pin_count: number; - - updated_at: number; - + updated_at: TimestampNS; /** * User response object */ created_by: UserResponse; - - deleted_at?: number; - + deleted_at?: TimestampNS; visibility?: string; - filter_tags?: Array; - custom?: Record; - location?: FeedsActivityLocation; } export interface FeedsNotificationComment { comment: string; - id: string; - user_id: string; - attachments?: Array; } export interface FeedsNotificationContext { target?: FeedsNotificationTarget; - trigger?: FeedsNotificationTrigger; } export interface FeedsNotificationParentActivity { id: string; - text?: string; - type?: string; - user_id?: string; - attachments?: Array; } export interface FeedsNotificationTarget { id: string; - name?: string; - text?: string; - type?: string; - user_id?: string; - attachments?: Array; - comment?: FeedsNotificationComment; - custom?: Record; - parent_activity?: FeedsNotificationParentActivity; } export interface FeedsNotificationTrigger { text: string; - type: string; - comment?: FeedsNotificationComment; - custom?: Record; } @@ -3902,45 +3045,31 @@ export interface FeedsPreferences { /** * Push notification preference for comments on user's activities. One of: all, none */ - comment?: 'all' | 'none'; - /** * Push notification preference for mentions in comments. One of: all, none */ - comment_mention?: 'all' | 'none'; - /** * Push notification preference for reactions on comments. One of: all, none */ - comment_reaction?: 'all' | 'none'; - /** * Push notification preference for replies to comments. One of: all, none */ - comment_reply?: 'all' | 'none'; - /** * Push notification preference for new followers. One of: all, none */ - follow?: 'all' | 'none'; - /** * Push notification preference for mentions in activities. One of: all, none */ - mention?: 'all' | 'none'; - /** * Push notification preference for reactions on user's activities or comments. One of: all, none */ - reaction?: 'all' | 'none'; - /** * Push notification preferences for custom activity types. Map of activity type to preference (all or none) */ @@ -3949,54 +3078,37 @@ export interface FeedsPreferences { export interface FeedsPreferencesResponse { comment?: string; - comment_mention?: string; - comment_reaction?: string; - comment_reply?: string; - follow?: string; - mention?: string; - reaction?: string; - custom_activity_types?: Record; } export interface FeedsReactionGroupResponse { count: number; - - first_reaction_at: number; - - last_reaction_at: number; + first_reaction_at: TimestampNS; + last_reaction_at: TimestampNS; } export interface FeedsReactionResponse { activity_id: string; - - created_at: number; - + created_at: TimestampNS; type: string; - - updated_at: number; - + updated_at: TimestampNS; /** * User response object */ user: UserResponse; - comment_id?: string; - custom?: Record; } export interface FeedsShareResponse { activity_id: string; - - created_at: number; - + created_at: TimestampNS; /** * User response object */ @@ -4005,185 +3117,104 @@ export interface FeedsShareResponse { export interface FeedsV3ActivityResponse { bookmark_count: number; - comment_count: number; - - created_at: number; - + created_at: TimestampNS; hidden: boolean; - id: string; - popularity: number; - preview: boolean; - reaction_count: number; - restrict_replies: string; - score: number; - share_count: number; - type: string; - - updated_at: number; - + updated_at: TimestampNS; visibility: string; - attachments: Array; - comments: Array; - feeds: Array; - filter_tags: Array; - interest_tags: Array; - latest_reactions: Array; - mentioned_users: Array; - own_bookmarks: Array; - own_reactions: Array; - collections: Record; - custom: Record; - reaction_groups: Record; - search_data: Record; - /** * User response object */ user: UserResponse; - - deleted_at?: number; - - edited_at?: number; - - expires_at?: number; - + deleted_at?: TimestampNS; + edited_at?: TimestampNS; + expires_at?: TimestampNS; friend_reaction_count?: number; - is_read?: boolean; - is_seen?: boolean; - is_watched?: boolean; - moderation_action?: string; - selector_source?: string; - text?: string; - visibility_tag?: string; - friend_reactions?: Array; - latest_shares?: Array; - current_feed?: FeedsFeedResponse; - i18n?: Record; - location?: FeedsActivityLocation; - metrics?: Record; - moderation?: ModerationV2Response; - notification_context?: FeedsNotificationContext; - parent?: FeedsV3ActivityResponse; - poll?: PollResponseData; - score_vars?: Record; } export interface FeedsV3CommentResponse { bookmark_count: number; - confidence_score: number; - - created_at: number; - + created_at: TimestampNS; downvote_count: number; - id: string; - object_id: string; - object_type: string; - reaction_count: number; - reply_count: number; - score: number; - status: string; - - updated_at: number; - + updated_at: TimestampNS; upvote_count: number; - mentioned_users: Array; - own_reactions: Array; - /** * User response object */ user: UserResponse; - controversy_score?: number; - - deleted_at?: number; - - edited_at?: number; - + deleted_at?: TimestampNS; + edited_at?: TimestampNS; parent_id?: string; - text?: string; - attachments?: Array; - latest_reactions?: Array; - custom?: Record; - i18n?: Record; - moderation?: ModerationV2Response; - reaction_groups?: Record; } export interface Field { short: boolean; - title: string; - value: string; } export interface FileUploadConfig { size_limit: number; - allowed_file_extensions: Array; - allowed_mime_types: Array; - blocked_file_extensions: Array; - blocked_mime_types: Array; } @@ -4192,7 +3223,6 @@ export interface FileUploadRequest { * file field */ file?: string; - user?: OnlyUserID; } @@ -4201,12 +3231,10 @@ export interface FileUploadResponse { * Duration of the request in milliseconds */ duration: string; - /** * URL to the uploaded asset. Should be used to put to `asset_url` attachment field */ file?: string; - /** * URL of the file thumbnail for supported file formats. Should be put to `thumb_url` attachment field */ @@ -4218,27 +3246,26 @@ export interface FilterConfigResponse { * LLM moderation labels available as filter values */ llm_labels: Array; - /** * AI image moderation labels available as filter values. Reflects the app's effective image taxonomy: custom Bodyguard taxonomy when enabled, otherwise the standard L1 label set. */ ai_image_labels?: Array; - /** * AI text moderation labels available as filter values */ ai_text_labels?: Array; - /** * Moderation config keys present in the queue, available as filter values */ config_keys?: Array; - /** * The moderation_payload.custom keys the app has configured as review-queue filter chips (via moderation_dashboard_preferences.filterable_custom_keys). Discovery hint for the dashboard only — the filter accepts any custom key regardless of this list. */ filterable_custom_keys?: Array; - + /** + * Names of the app's moderation rules, available as filter values on the `label` field when filtering rule-flagged content. Includes disabled rules, since items flagged before a rule was turned off still carry its name; excludes deleted ones. Scoped to the caller's teams on a multi-tenant app. Capped at 30 names, ordered by name, so an app above that cap is listed partially. + */ + rule_names?: Array; /** * AI image moderation labels available as filter values, as a map of L1 label to its L2 sub-labels. Reflects the app's effective image taxonomy: custom Bodyguard taxonomy when enabled, otherwise the standard catalogue of the org's enabled image providers. */ @@ -4251,23 +3278,18 @@ export interface FlagCountRuleParameters { export interface FlagDetailsResponse { original_text: string; - automod?: AutomodDetailsResponse; - extra?: Record; } export interface FlagFeedbackResponse { - created_at: number; - + created_at: TimestampNS; message_id: string; - labels: Array; } export interface FlagItemResponse { duration: string; - /** * Unique identifier of the created moderation item */ @@ -4276,11 +3298,8 @@ export interface FlagItemResponse { export interface FlagMessageDetailsResponse { pin_changed?: boolean; - should_enrich?: boolean; - skip_push?: boolean; - updated_by_id?: string; } @@ -4289,27 +3308,22 @@ export interface FlagRequest { * Unique identifier of the entity being flagged */ entity_id: string; - /** * Type of entity being flagged (e.g., message, user) */ entity_type: string; - /** * ID of the user who created the flagged entity */ entity_creator_id?: string; - /** * Optional explanation for why the content is being flagged */ reason?: string; - /** * Additional metadata about the flag */ custom?: Record; - moderation_payload?: ModerationPayload; } @@ -4319,128 +3333,82 @@ export interface FlagUserOptions { export interface FloodConfig { allowlist?: Array; - identical?: FloodIdenticalConfig; - similar?: FloodSimilarConfig; } export interface FloodIdenticalConfig { action: string; - enabled: boolean; - threshold: number; - time_window: string; } export interface FloodIdenticalRuleParameters { + min_text_length?: number; threshold?: number; - time_window?: string; - + track_across_users?: boolean; allowlist?: Array; } export interface FloodSimilarConfig { action: string; - enabled: boolean; - similarity_distance: number; - threshold: number; - time_window: string; } export interface FloodSimilarRuleParameters { similarity_distance?: number; - threshold?: number; - time_window?: string; - allowlist?: Array; } export interface FullUserResponse { banned: boolean; - - created_at: number; - + created_at: TimestampNS; id: string; - invisible: boolean; - language: string; - online: boolean; - role: string; - shadow_banned: boolean; - total_unread_count: number; - unread_channels: number; - unread_count: number; - unread_threads: number; - - updated_at: number; - + updated_at: TimestampNS; blocked_user_ids: Array; - channel_mutes: Array; - devices: Array; - mutes: Array; - teams: Array; - custom: CustomUserData; - avg_response_time?: number; - - ban_expires?: number; - - deactivated_at?: number; - - deleted_at?: number; - + ban_expires?: TimestampNS; + deactivated_at?: TimestampNS; + deleted_at?: TimestampNS; image?: string; - - last_active?: number; - + last_active?: TimestampNS; name?: string; - - revoke_tokens_issued_before?: number; - + revoke_tokens_issued_before?: TimestampNS; latest_hidden_channels?: Array; - privacy_settings?: PrivacySettingsResponse; - teams_role?: Record; } export interface FutureChannelBanResponse { - created_at: number; - - expires?: number; - + created_at: TimestampNS; + expires?: TimestampNS; reason?: string; - shadow?: boolean; - /** * User response object */ banned_by?: UserResponse; - /** * User response object */ @@ -4449,7 +3417,6 @@ export interface FutureChannelBanResponse { export interface GetActionConfigResponse { duration: string; - /** * Moderation action configs grouped by entity type, sorted by order ascending */ @@ -4458,7 +3425,6 @@ export interface GetActionConfigResponse { export interface GetAppealResponse { duration: string; - item?: AppealItemResponse; } @@ -4467,7 +3433,6 @@ export interface GetApplicationResponse { * Duration of the request in milliseconds */ duration: string; - app: AppResponseFields; } @@ -4476,7 +3441,6 @@ export interface GetBlockedUsersResponse { * Duration of the request in milliseconds */ duration: string; - /** * Array of blocked user object */ @@ -4485,7 +3449,6 @@ export interface GetBlockedUsersResponse { export interface GetConfigResponse { duration: string; - config?: ConfigResponse; } @@ -4494,13 +3457,11 @@ export interface GetDraftResponse { * Duration of the request in milliseconds */ duration: string; - draft: DraftResponse; } export interface GetManyMessagesResponse { duration: string; - /** * List of messages */ @@ -4512,7 +3473,6 @@ export interface GetMessageResponse { * Duration of the request in milliseconds */ duration: string; - /** * Represents any chat message */ @@ -4521,79 +3481,57 @@ export interface GetMessageResponse { export interface GetOGResponse { duration: string; - custom: CustomAttachmentData; - /** * URL of detected video or audio */ asset_url?: string; - author_icon?: string; - /** * og:site */ author_link?: string; - /** * og:site_name */ author_name?: string; - color?: string; - fallback?: string; - footer?: string; - footer_icon?: string; - /** * URL of detected image */ image_url?: string; - /** * extracted url from the text */ og_scrape_url?: string; - original_height?: number; - original_width?: number; - pretext?: string; - /** * og:description */ text?: string; - /** * URL of detected thumb image */ thumb_url?: string; - /** * og:title */ title?: string; - /** * og:url */ title_link?: string; - /** * Attachment type, could be empty, image, audio or video */ type?: string; - actions?: Array; - fields?: Array; - giphy?: Images; } @@ -4602,7 +3540,6 @@ export interface GetPinnedMessagesResponse { * Duration of the request in milliseconds */ duration: string; - /** * Messages */ @@ -4611,7 +3548,6 @@ export interface GetPinnedMessagesResponse { export interface GetReactionsResponse { duration: string; - /** * List of reactions */ @@ -4623,19 +3559,16 @@ export interface GetRepliesResponse { * Duration of the request in milliseconds */ duration: string; - messages: Array; } export interface GetThreadResponse { duration: string; - thread: ThreadStateResponse; } export interface GetUserGroupResponse { duration: string; - user_group?: UserGroupResponse; } @@ -4648,17 +3581,14 @@ export interface GroupedChannelsBucket { * Channels returned for this bucket */ channels: Array; - /** * Cursor for the next page of this group */ next?: string; - /** * Cursor for the previous page of this group */ prev?: string; - /** * Unread channels currently classified into this bucket */ @@ -4667,9 +3597,7 @@ export interface GroupedChannelsBucket { export interface GroupedChannelsGroupRequest { limit?: number; - next?: string; - prev?: string; } @@ -4678,17 +3606,14 @@ export interface GroupedQueryChannelsRequest { * Default max channels per group (default 10) */ limit?: number; - /** * Whether to subscribe to presence events for channel members */ presence?: boolean; - /** * Whether to start watching found channels or not */ watch?: boolean; - /** * Groups to return, keyed by group name. Each group can define limit, next, or prev. 'next' and 'prev' cursors are only allowed when the request contains exactly one group; multi-group pagination is rejected. */ @@ -4700,7 +3625,6 @@ export interface GroupedQueryChannelsResponse { * Duration of the request in milliseconds */ duration: string; - /** * Predefined channel groups keyed by group name */ @@ -4709,29 +3633,19 @@ export interface GroupedQueryChannelsResponse { export interface HarmConfig { cooldown_period: number; - severity: number; - threshold: number; - action_sequences: Array; - harm_types: Array; } export interface HealthCheckEvent { connection_id: string; - - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - type: string; - cid?: string; - - received_at?: number; - + received_at?: TimestampNS; me?: OwnUserResponse; } @@ -4751,47 +3665,34 @@ export interface HideChannelResponse { export interface IPContentCountRuleParameters { threshold?: number; - time_window?: string; } export interface IPFlagCountRuleParameters { severity?: string; - threshold?: number; - time_window?: string; - harm_labels?: Array; } export interface ImageContentParameters { label_operator?: string; - min_confidence?: number; - harm_labels?: Array; } export interface ImageData { frames: string; - height: string; - size: string; - url: string; - width: string; } export interface ImageRuleParameters { min_confidence?: number; - threshold?: number; - time_window?: string; - harm_labels?: Array; } @@ -4800,17 +3701,14 @@ export interface ImageSize { * Crop mode. One of: top, bottom, left, right, center */ crop?: string; - /** * Target image height */ height?: number; - /** * Resize method. One of: clip, crop, scale, fill */ resize?: string; - /** * Target image width */ @@ -4819,12 +3717,10 @@ export interface ImageSize { export interface ImageUploadRequest { file?: string; - /** * field with JSON-encoded array of image size configurations */ upload_sizes?: Array; - user?: OnlyUserID; } @@ -4833,11 +3729,8 @@ export interface ImageUploadResponse { * Duration of the request in milliseconds */ duration: string; - file?: string; - thumb_url?: string; - /** * Array of image size configurations */ @@ -4846,23 +3739,16 @@ export interface ImageUploadResponse { export interface Images { fixed_height: ImageData; - fixed_height_downsampled: ImageData; - fixed_height_still: ImageData; - fixed_width: ImageData; - fixed_width_downsampled: ImageData; - fixed_width_still: ImageData; - original: ImageData; } export interface ImportBlockListRequest { items: Array; - chunk_size?: number; } @@ -4871,37 +3757,27 @@ export interface ImportBlockListResponse { * Duration of the request in milliseconds */ duration: string; - task_id: string; } export interface KeyframeOCRRuleParameters { threshold?: number; - time_window?: string; - harm_labels?: Array; } export interface KeyframeRuleParameters { min_confidence?: number; - threshold?: number; - time_window?: string; - harm_labels?: Array; } export interface LLMConfig { enabled: boolean; - rules: Array; - app_context?: string; - async?: boolean; - severity_descriptions?: Record; } @@ -4914,19 +3790,14 @@ export interface LLMRule { | 'bounce_flag' | 'bounce_remove' | 'keep'; - description: string; - label: string; - severity_rules: Array; } export interface LabelResponse { name: string; - harm_labels?: Array; - phrase_list_ids?: Array; } @@ -4935,7 +3806,6 @@ export interface LabelThresholds { * Threshold for automatic message block */ block?: number; - /** * Threshold for automatic message flag */ @@ -4947,15 +3817,12 @@ export interface ListBlockListResponse { * Duration of the request in milliseconds */ duration: string; - blocklists: Array; - next_cursor?: string; } export interface ListDevicesResponse { duration: string; - /** * List of devices */ @@ -4967,13 +3834,11 @@ export interface ListQueuesResponse { * Duration of the request in milliseconds */ duration: string; - queues: Array; } export interface ListUserGroupsResponse { duration: string; - /** * List of user groups */ @@ -5003,7 +3868,6 @@ export interface MarkReadRequest { * ID of the message that is considered last read by client */ message_id?: string; - /** * Optional Thread ID to specifically mark a given thread as read */ @@ -5015,34 +3879,23 @@ export interface MarkReadResponse { * Duration of the request in milliseconds */ duration: string; - event?: MarkReadResponseEvent; } export interface MarkReadResponseEvent { channel_id: string; - channel_type: string; - cid: string; - - created_at: number; - + created_at: TimestampNS; type: string; - - channel_last_message_at?: number; - + channel_last_message_at?: TimestampNS; last_read_message_id?: string; - team?: string; - /** * Represents channel in chat */ channel?: ChannelResponse; - thread?: ThreadResponse; - user?: UserResponseCommonFields; } @@ -5051,12 +3904,10 @@ export interface MarkReviewedRequestPayload { * Maximum content items to mark as reviewed */ content_to_mark_as_reviewed_limit?: number; - /** * Reason for the appeal decision */ decision_reason?: string; - /** * Skip marking content as reviewed */ @@ -5068,12 +3919,10 @@ export interface MarkUnreadRequest { * ID of the message from where the channel is marked unread */ message_id?: string; - /** * Timestamp of the message from where the channel is marked unread */ message_timestamp?: Date; - /** * Mark a thread unread, specify one of the thread, message timestamp, or message id */ @@ -5081,69 +3930,53 @@ export interface MarkUnreadRequest { } export interface MaxStreakChangedEvent { - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - type: string; - - received_at?: number; + received_at?: TimestampNS; } export interface MemberAddedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - member: ChannelMemberResponse; - /** * The type of event: "member.added" in this case */ type: string; - /** * The ID of the channel to which the member was added */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel to which the member was added */ channel_type?: string; - /** * The CID of the channel to which the member was added */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } @@ -5151,56 +3984,43 @@ export interface MemberRemovedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - member: ChannelMemberResponse; - /** * The type of event: "member.removed" in this case */ type: string; - /** * The ID of the channel from which the member was removed */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel from which the member was removed */ channel_type?: string; - /** * The CID of the channel from which the member was removed */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } @@ -5208,72 +4028,53 @@ export interface MemberUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - member: ChannelMemberResponse; - /** * The type of event: "member.updated" in this case */ type: string; - /** * The ID of the channel in which the member was updated */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel in which the member was updated */ channel_type?: string; - /** * The CID of the channel in which the member was updated */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } export interface MemberUserRequest { id: string; - image?: string; - invisible?: boolean; - language?: string; - name?: string; - custom?: CustomUserData; - privacy_settings?: PrivacySettingsResponse; } @@ -5282,7 +4083,6 @@ export interface MembersResponse { * Duration of the request in milliseconds */ duration: string; - /** * List of found members */ @@ -5301,7 +4101,6 @@ export interface MessageActionResponse { * Duration of the request in milliseconds */ duration: string; - /** * Represents any chat message */ @@ -5310,21 +4109,13 @@ export interface MessageActionResponse { export interface MessageChangeSet { attachments: boolean; - custom: boolean; - html: boolean; - mentioned_user_ids: boolean; - mml: boolean; - pin: boolean; - quoted_message_id: boolean; - silent: boolean; - text: boolean; } @@ -5332,66 +4123,51 @@ export interface MessageDeletedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Whether the message was hard deleted */ hard_delete: boolean; - message_id: string; - custom: CustomEventData; - /** * Represents any chat message */ message: MessageResponse; - /** * The type of event: "message.deleted" in this case */ type: string; - /** * The ID of the channel where the message was sent */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel where the message was sent */ channel_type?: string; - /** * The CID of the channel where the message was sent */ cid?: string; - /** * Whether the message was deleted only for the current user */ deleted_for_me?: boolean; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } @@ -5399,103 +4175,76 @@ export interface MessageDeliveredEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "message.delivered" in this case */ type: string; - /** * The ID of the channel where the message was read */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel where the message was read */ channel_type?: string; - /** * The CID of the channel where the message was read */ cid?: string; - /** * The time when the message was delivered */ last_delivered_at?: string; - /** * The ID of the last delivered message */ last_delivered_message_id?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - /** * Represents channel in chat */ channel?: ChannelResponse; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } export interface MessageFlagResponse { - created_at: number; - + created_at: TimestampNS; created_by_automod: boolean; - - updated_at: number; - - approved_at?: number; - + updated_at: TimestampNS; + approved_at?: TimestampNS; reason?: string; - - rejected_at?: number; - - reviewed_at?: number; - + rejected_at?: TimestampNS; + reviewed_at?: TimestampNS; custom?: Record; - details?: FlagDetailsResponse; - /** * Represents any chat message */ message?: MessageResponse; - moderation_feedback?: FlagFeedbackResponse; - /** * Result of the message moderation */ moderation_result?: MessageModerationResult; - /** * User response object */ reviewed_by?: UserResponse; - /** * User response object */ @@ -5507,49 +4256,39 @@ export interface MessageModerationResult { * Action taken by automod */ action: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * ID of the message */ message_id: string; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; /** * Whether user has bad karma */ user_bad_karma: boolean; - /** * Karma of the user */ user_karma: number; - /** * Word that was blocked */ blocked_word?: string; - /** * Name of the blocklist */ blocklist_name?: string; - /** * User who moderated the message */ moderated_by?: string; - ai_moderation_response?: ModerationResponse; - /** * Sets thresholds for AI moderation */ @@ -5560,93 +4299,71 @@ export interface MessageNewEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; message_id: string; - /** * The number of watchers */ watcher_count: number; - custom: CustomEventData; - /** * Represents any chat message */ message: MessageResponse; - /** * The type of event: "message.new" in this case */ type: string; - /** * The ID of the channel where the message was sent */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel where the message was sent */ channel_type?: string; - /** * The CID of the channel where the message was sent */ cid?: string; - /** * The author of the parent message */ parent_author?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - total_unread_count?: number; - unread_channels?: number; - /** * The number of unread messages */ unread_count?: number; - /** * The participants of the thread */ thread_participants?: Array; - /** * Represents channel in chat */ channel?: ChannelResponse; - channel_custom?: CustomChannelData; - grouped_unread_channels?: Record; - user?: UserResponseCommonFields; } export interface MessageOptions { include_thread_participants?: boolean; - member_custom_include?: Array; } @@ -5655,52 +4372,42 @@ export interface MessagePaginationParams { * The timestamp to get messages with a created_at timestamp greater than */ created_at_after?: Date; - /** * The timestamp to get messages with a created_at timestamp greater than or equal to */ created_at_after_or_equal?: Date; - /** * The result will be a set of messages, that are both older and newer than the created_at timestamp provided, distributed evenly around the timestamp */ created_at_around?: Date; - /** * The timestamp to get messages with a created_at timestamp smaller than */ created_at_before?: Date; - /** * The timestamp to get messages with a created_at timestamp smaller than or equal to */ created_at_before_or_equal?: Date; - /** * The result will be a set of messages, that are both older and newer than the message with the provided ID, and the message with the ID provided will be in the middle of the set */ id_around?: string; - /** * The ID of the message to get messages with a timestamp greater than */ id_gt?: string; - /** * The ID of the message to get messages with a timestamp greater than or equal to */ id_gte?: string; - /** * The ID of the message to get messages with a timestamp smaller than */ id_lt?: string; - /** * The ID of the message to get messages with a timestamp smaller than or equal to */ id_lte?: string; - /** * The maximum number of messages to return (max limit */ @@ -5711,61 +4418,47 @@ export interface MessageReadEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "message.read" in this case */ type: string; - /** * The ID of the channel where the message was read */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel where the message was read */ channel_type?: string; - /** * The CID of the channel where the message was read */ cid?: string; - /** * The ID of the last read message */ last_read_message_id?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - /** * Represents channel in chat */ channel?: ChannelResponse; - channel_custom?: CustomChannelData; - thread?: ThreadResponse; - user?: UserResponseCommonFields; } @@ -5774,88 +4467,67 @@ export interface MessageRequest { * Message ID is unique string identifier of the message */ id?: string; - mentioned_channel?: boolean; - mentioned_here?: boolean; - /** * Should be empty if `text` is provided. Can only be set when using server-side API */ mml?: string; - /** * ID of parent message (thread) */ parent_id?: string; - /** * Date when pinned message expires */ pin_expires?: Date; - /** * Whether message is pinned or not */ pinned?: boolean; - /** * Date when message got pinned */ pinned_at?: Date; - /** * Identifier of the poll to include in the message */ poll_id?: string; - quoted_message_id?: string; - /** * Whether thread reply should be shown in the channel as well */ show_in_channel?: boolean; - /** * Whether message is silent or not */ silent?: boolean; - /** * Text of the message. Should be empty if `mml` is provided */ text?: string; - /** * Contains type of the message. One of: regular, system */ - type?: 'regular' | 'system'; - /** * Array of message attachments */ attachments?: Array; - /** * List of user group IDs to mention. Group members who are also channel members will receive push notifications. Max 10 groups */ mentioned_group_ids?: Array; - mentioned_roles?: Array; - /** * Array of user IDs to mention */ mentioned_users?: Array; - /** * A list of user ids that have restricted visibility to the message */ restricted_visibility?: Array; - custom?: CustomMessageData; - shared_location?: SharedLocation; } @@ -5864,214 +4536,165 @@ export interface MessageResponse { * Channel unique identifier in : format */ cid: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; deleted_reply_count: number; - /** * Contains HTML markup of the message. Can only be set when using server-side API */ html: string; - /** * Message ID is unique string identifier of the message */ id: string; - /** * Whether the message mentioned the channel tag */ mentioned_channel: boolean; - /** * Whether the message mentioned online users with @here tag */ mentioned_here: boolean; - /** * Whether message is pinned or not */ pinned: boolean; - /** * Number of replies to this message */ reply_count: number; - /** * Whether the message was shadowed or not */ shadowed: boolean; - /** * Whether message is silent or not */ silent: boolean; - /** * Text of the message. Should be empty if `mml` is provided */ text: string; - /** * Contains type of the message. One of: regular, ephemeral, error, reply, system, deleted */ type: string; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; /** * Array of message attachments */ attachments: Array; - /** * List of 10 latest reactions to this message */ latest_reactions: Array; - /** * List of mentioned users */ mentioned_users: Array; - /** * List of 10 latest reactions of authenticated user to this message */ own_reactions: Array; - /** * A list of user ids that have restricted visibility to the message, if the list is not empty, the message is only visible to the users in the list */ restricted_visibility: Array; - custom: CustomMessageData; - /** * An object containing number of reactions of each type. Key: reaction type (string), value: number of reactions (int) */ reaction_counts: Record; - /** * An object containing scores of reactions of each type. Key: reaction type (string), value: total score of reactions (int) */ reaction_scores: Record; - /** * User response object */ user: UserResponse; - /** * Contains provided slash command */ command?: string; - /** * Date/time of deletion */ - deleted_at?: number; - + deleted_at?: TimestampNS; deleted_for_me?: boolean; - - message_text_updated_at?: number; - + message_text_updated_at?: TimestampNS; /** * Should be empty if `text` is provided. Can only be set when using server-side API */ mml?: string; - /** * ID of parent message (thread) */ parent_id?: string; - /** * Date when pinned message expires */ - pin_expires?: number; - + pin_expires?: TimestampNS; /** * Date when message got pinned */ - pinned_at?: number; - + pinned_at?: TimestampNS; /** * Identifier of the poll to include in the message */ poll_id?: string; - quoted_message_id?: string; - /** * Whether thread reply should be shown in the channel as well */ show_in_channel?: boolean; - /** * List of user group IDs mentioned in the message. Group members who are also channel members will receive push notifications based on their push preferences. Max 10 groups */ mentioned_group_ids?: Array; - /** * List of mentioned user group objects. */ mentioned_groups?: Array; - /** * List of roles mentioned in the message (e.g. admin, channel_moderator, custom roles). Members with matching roles will receive push notifications based on their push preferences. Max 10 roles */ mentioned_roles?: Array; - /** * List of users who participate in thread */ thread_participants?: Array; - draft?: DraftResponse; - /** * Object with translations. Key `language` contains the original language key. Other keys contain translations */ i18n?: Record; - /** * Contains image moderation information */ image_labels?: Record>; - member?: ChannelMemberPartialResponse; - /** * Channel member data for the users mentioned in the message, keyed by user id. Only present when the app has member custom on mentioned users enabled, and only for the first two mentioned users of each message */ mentioned_channel_members?: Record; - moderation?: ModerationV2Response; - /** * User response object */ pinned_by?: UserResponse; - poll?: PollResponseData; - /** * Represents any chat message */ quoted_message?: MessageResponse; - reaction_groups?: Record; - reminder?: ReminderResponseData; - shared_location?: SharedLocationResponseData; } @@ -6079,60 +4702,47 @@ export interface MessageUndeletedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; message_id: string; - custom: CustomEventData; - /** * Represents any chat message */ message: MessageResponse; - /** * The type of event: "message.undeleted" in this case */ type: string; - /** * The ID of the channel where the message was sent */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel where the message was sent */ channel_type?: string; - /** * The CID of the channel where the message was sent */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; } export interface MessageUpdate { old_text?: string; - change_set?: MessageChangeSet; } @@ -6140,58 +4750,44 @@ export interface MessageUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; message_id: string; - custom: CustomEventData; - /** * Represents any chat message */ message: MessageResponse; - /** * The type of event: "message.updated" in this case */ type: string; - /** * The ID of the channel where the message was sent */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel where the message was sent */ channel_type?: string; - /** * The CID of the channel where the message was sent */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - message_update?: MessageUpdate; - user?: UserResponseCommonFields; } @@ -6200,219 +4796,169 @@ export interface MessageWithChannelResponse { * Channel unique identifier in : format */ cid: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; deleted_reply_count: number; - /** * Contains HTML markup of the message. Can only be set when using server-side API */ html: string; - /** * Message ID is unique string identifier of the message */ id: string; - /** * Whether the message mentioned the channel tag */ mentioned_channel: boolean; - /** * Whether the message mentioned online users with @here tag */ mentioned_here: boolean; - /** * Whether message is pinned or not */ pinned: boolean; - /** * Number of replies to this message */ reply_count: number; - /** * Whether the message was shadowed or not */ shadowed: boolean; - /** * Whether message is silent or not */ silent: boolean; - /** * Text of the message. Should be empty if `mml` is provided */ text: string; - /** * Contains type of the message. One of: regular, ephemeral, error, reply, system, deleted */ type: string; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; /** * Array of message attachments */ attachments: Array; - /** * List of 10 latest reactions to this message */ latest_reactions: Array; - /** * List of mentioned users */ mentioned_users: Array; - /** * List of 10 latest reactions of authenticated user to this message */ own_reactions: Array; - /** * A list of user ids that have restricted visibility to the message, if the list is not empty, the message is only visible to the users in the list */ restricted_visibility: Array; - /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomMessageData; - /** * An object containing number of reactions of each type. Key: reaction type (string), value: number of reactions (int) */ reaction_counts: Record; - /** * An object containing scores of reactions of each type. Key: reaction type (string), value: total score of reactions (int) */ reaction_scores: Record; - /** * User response object */ user: UserResponse; - /** * Contains provided slash command */ command?: string; - /** * Date/time of deletion */ - deleted_at?: number; - + deleted_at?: TimestampNS; deleted_for_me?: boolean; - - message_text_updated_at?: number; - + message_text_updated_at?: TimestampNS; /** * Should be empty if `text` is provided. Can only be set when using server-side API */ mml?: string; - /** * ID of parent message (thread) */ parent_id?: string; - /** * Date when pinned message expires */ - pin_expires?: number; - + pin_expires?: TimestampNS; /** * Date when message got pinned */ - pinned_at?: number; - + pinned_at?: TimestampNS; /** * Identifier of the poll to include in the message */ poll_id?: string; - quoted_message_id?: string; - /** * Whether thread reply should be shown in the channel as well */ show_in_channel?: boolean; - /** * List of user group IDs mentioned in the message. Group members who are also channel members will receive push notifications based on their push preferences. Max 10 groups */ mentioned_group_ids?: Array; - /** * List of mentioned user group objects. */ mentioned_groups?: Array; - /** * List of roles mentioned in the message (e.g. admin, channel_moderator, custom roles). Members with matching roles will receive push notifications based on their push preferences. Max 10 roles */ mentioned_roles?: Array; - /** * List of users who participate in thread */ thread_participants?: Array; - draft?: DraftResponse; - /** * Object with translations. Key `language` contains the original language key. Other keys contain translations */ i18n?: Record; - /** * Contains image moderation information */ image_labels?: Record>; - member?: ChannelMemberPartialResponse; - /** * Channel member data for the users mentioned in the message, keyed by user id. Only present when the app has member custom on mentioned users enabled, and only for the first two mentioned users of each message */ mentioned_channel_members?: Record; - moderation?: ModerationV2Response; - /** * User response object */ pinned_by?: UserResponse; - poll?: PollResponseData; - /** * Represents any chat message */ quoted_message?: MessageResponse; - reaction_groups?: Record; - reminder?: ReminderResponseData; - shared_location?: SharedLocationResponseData; } @@ -6421,34 +4967,27 @@ export interface ModerationActionConfigResponse { * The action to take */ action: string; - /** * Description of what this action does */ description: string; - /** * Type of entity this action applies to */ entity_type: string; - /** * Icon for the dashboard */ icon: string; - /** * Display order (lower numbers shown first) */ order: number; - id?: string; - /** * Queue type this action config belongs to */ queue_type?: string; - /** * Custom data for the action */ @@ -6461,43 +5000,24 @@ export interface ModerationBanResponse { export interface ModerationCallResponse { backstage: boolean; - captioning: boolean; - cid: string; - - created_at: number; - + created_at: TimestampNS; current_session_id: string; - id: string; - recording: boolean; - transcribing: boolean; - translating: boolean; - type: string; - - updated_at: number; - + updated_at: TimestampNS; blocked_user_ids: Array; - custom: Record; - channel_cid?: string; - - ended_at?: number; - + ended_at?: TimestampNS; join_ahead_time_seconds?: number; - routing_number?: string; - - starts_at?: number; - + starts_at?: TimestampNS; team?: string; - /** * User response object */ @@ -6509,22 +5029,15 @@ export interface ModerationCustomActionEvent { * The ID of the custom action that was executed */ action_id: string; - - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - review_queue_item: ReviewQueueItemResponse; - type: string; - - received_at?: number; - + received_at?: TimestampNS; /** * Additional options passed to the custom action */ action_options?: Record; - /** * Represents any chat message */ @@ -6532,37 +5045,24 @@ export interface ModerationCustomActionEvent { } export interface ModerationFlagResponse { - created_at: number; - + created_at: TimestampNS; entity_id: string; - entity_type: string; - type: string; - - updated_at: number; - + updated_at: TimestampNS; user_id: string; - result: Array>; - + content_published_at?: TimestampNS; entity_creator_id?: string; - reason?: string; - review_queue_item_id?: string; - labels?: Array; - custom?: Record; - /** * Content payload for moderation */ moderation_payload?: ModerationPayloadResponse; - review_queue_item?: ReviewQueueItemResponse; - /** * User response object */ @@ -6574,32 +5074,22 @@ export interface ModerationFlaggedEvent { * The type of content that was flagged */ content_type: string; - - created_at: number; - + created_at: TimestampNS; /** * The ID of the flagged content */ object_id: string; - custom: CustomEventData; - type: string; - - received_at?: number; + received_at?: TimestampNS; } export interface ModerationMarkReviewedEvent { - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - item: ReviewQueueItemResponse; - type: string; - - received_at?: number; - + received_at?: TimestampNS; /** * Represents any chat message */ @@ -6607,24 +5097,16 @@ export interface ModerationMarkReviewedEvent { } export interface ModerationPayload { + country_code?: string; audios?: Array; - image_ordered_keys?: Array; - images?: Array; - other_media?: Array; - text_ordered_keys?: Array; - texts?: Array; - videos?: Array; - custom?: Record; - image_ids?: Record; - text_ids?: Record; } @@ -6633,47 +5115,38 @@ export interface ModerationPayloadResponse { * Audio URLs to moderate */ audios?: Array; - /** * Caller-supplied keys for images, index-aligned with images[] */ image_ordered_keys?: Array; - /** * Image URLs to moderate */ images?: Array; - /** * Media URLs from attachments outside the typed image/video/audio lists (custom attachment types such as GIF pickers) */ other_media?: Array; - /** * Caller-supplied keys for texts (e.g. "title", "description"), index-aligned with texts[] */ text_ordered_keys?: Array; - /** * Text content to moderate */ texts?: Array; - /** * Video URLs to moderate */ videos?: Array; - /** * Custom data for moderation */ custom?: Record; - /** * Caller-supplied content IDs per image key (from content_ids on /analyze) */ image_ids?: Record; - /** * Caller-supplied content IDs per text key (from content_ids on /analyze) */ @@ -6681,52 +5154,33 @@ export interface ModerationPayloadResponse { } export interface ModerationQueueResponse { - created_at: number; - + created_at: TimestampNS; created_by: string; - description: string; - id: string; - item_count: number; - name: string; - type: string; - - updated_at: number; - + updated_at: TimestampNS; sort: Array>; - filters: Record; } export interface ModerationResponse { action: string; - explicit: number; - spam: number; - toxic: number; } export interface ModerationV2Response { action: string; - original_text: string; - blocklist_matched?: string; - platform_circumvented?: boolean; - semantic_filter_matched?: string; - blocklists_matched?: Array; - image_harms?: Array; - text_harms?: Array; } @@ -6735,7 +5189,6 @@ export interface MuteChannelRequest { * Duration of mute in milliseconds */ expiration?: number; - /** * Channel CIDs to mute (if multiple channels) */ @@ -6744,14 +5197,11 @@ export interface MuteChannelRequest { export interface MuteChannelResponse { duration: string; - /** * Object with mutes (if multiple channels were muted) */ channel_mutes?: Array; - channel_mute?: ChannelMute; - own_user?: OwnUserResponse; } @@ -6760,7 +5210,6 @@ export interface MuteRequest { * User IDs to mute (if multiple users) */ target_ids: Array; - /** * Duration of mute in minutes */ @@ -6769,17 +5218,14 @@ export interface MuteRequest { export interface MuteResponse { duration: string; - /** * Object with mutes (if multiple users were muted) */ mutes?: Array; - /** * A list of users that can't be found. Common cause for this is deleted users */ non_existing_users?: Array; - own_user?: OwnUserResponse; } @@ -6787,48 +5233,36 @@ export interface NotificationAddedToChannelEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - member: ChannelMemberResponse; - /** * The type of event: "notification.added_to_channel" in this case */ type: string; - /** * The ID of the channel to which the user was added */ channel_id?: string; - channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel to which the user was added */ channel_type?: string; - /** * The CID of the channel to which the user was added */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; } @@ -6836,66 +5270,51 @@ export interface NotificationChannelDeletedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * The type of event: "notification.channel_deleted" in this case */ type: string; - /** * The ID of the channel which was deleted */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was deleted */ channel_type?: string; - /** * The CID of the channel which was deleted */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - /** * The total number of unread messages */ total_unread_count?: number; - /** * The number of channels with unread messages */ unread_channels?: number; - /** * The number of unread messages in the channel */ unread_count?: number; - channel_custom?: CustomChannelData; - grouped_unread_channels?: Record; } @@ -6903,88 +5322,67 @@ export interface NotificationChannelMutesUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - me: OwnUserResponse; - /** * The type of event: "notification.channel_mutes_updated" in this case */ type: string; - - received_at?: number; + received_at?: TimestampNS; } export interface NotificationChannelTruncatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * The type of event: "notification.channel_truncated" in this case */ type: string; - /** * The ID of the channel which was truncated */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was truncated */ channel_type?: string; - /** * The CID of the channel which was truncated */ cid?: string; - message_id?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - /** * The total number of unread messages */ total_unread_count?: number; - /** * The number of channels with unread messages */ unread_channels?: number; - /** * The number of unread messages in the channel */ unread_count?: number; - channel_custom?: CustomChannelData; - grouped_unread_channels?: Record; - /** * Represents any chat message */ @@ -6995,56 +5393,43 @@ export interface NotificationInviteAcceptedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - member: ChannelMemberResponse; - /** * The type of event: "notification.invite_accepted" in this case */ type: string; - /** * The ID of the channel to which the user accepted the invite */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel to which the user accepted the invite */ channel_type?: string; - /** * The CID of the channel to which the user accepted the invite */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } @@ -7052,56 +5437,43 @@ export interface NotificationInviteRejectedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - member: ChannelMemberResponse; - /** * The type of event: "notification.invite_rejected" in this case */ type: string; - /** * The ID of the channel to which the user rejected the invite */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel to which the user rejected the invite */ channel_type?: string; - /** * The CID of the channel to which the user rejected the invite */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } @@ -7109,56 +5481,43 @@ export interface NotificationInvitedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - member: ChannelMemberResponse; - /** * The type of event: "notification.invited" in this case */ type: string; - /** * The ID of the channel to which the user was invited */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel to which the user was invited */ channel_type?: string; - /** * The CID of the channel to which the user was invited */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } @@ -7166,90 +5525,69 @@ export interface NotificationMarkReadEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The total number of unread messages */ total_unread_count: number; - /** * The number of channels with unread messages */ unread_channels: number; - /** * The total number of unread messages */ unread_count: number; - custom: CustomEventData; - /** * The type of event: "notification.mark_read" in this case */ type: string; - /** * The ID of the channel which was marked as read */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was marked as read */ channel_type?: string; - /** * The CID of the channel which was marked as read */ cid?: string; - /** * The ID of the last read message */ last_read_message_id?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - /** * The ID of the thread which was marked as read */ thread_id?: string; - /** * The total number of unread messages in the threads */ unread_thread_messages?: number; - /** * The number of unread threads */ unread_threads?: number; - /** * Represents channel in chat */ channel?: ChannelResponse; - channel_custom?: CustomChannelData; - grouped_unread_channels?: Record; - thread?: ThreadResponse; - user?: UserResponseCommonFields; } @@ -7257,103 +5595,80 @@ export interface NotificationMarkUnreadEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "notification.mark_unread" in this case */ type: string; - /** * The ID of the channel which was marked as unread */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel which was marked as unread */ channel_type?: string; - /** * The CID of the channel which was marked as unread */ cid?: string; - /** * The ID of the first unread message */ first_unread_message_id?: string; - /** * The time when the channel/thread was marked as unread */ - last_read_at?: number; - + last_read_at?: TimestampNS; /** * The ID of the last read message */ last_read_message_id?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - /** * The ID of the thread which was marked as unread */ thread_id?: string; - /** * The total number of unread messages */ total_unread_count?: number; - /** * The number of channels with unread messages */ unread_channels?: number; - /** * The total number of unread messages */ unread_count?: number; - /** * The number of unread messages in the channel/thread after first_unread_message_id */ unread_messages?: number; - /** * The total number of unread messages in the threads */ unread_thread_messages?: number; - /** * The number of unread threads */ unread_threads?: number; - /** * Represents channel in chat */ channel?: ChannelResponse; - channel_custom?: CustomChannelData; - grouped_unread_channels?: Record; - user?: UserResponseCommonFields; } @@ -7361,91 +5676,67 @@ export interface NotificationMutesUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - me: OwnUserResponse; - /** * The type of event: "notification.mutes_updated" in this case */ type: string; - - received_at?: number; + received_at?: TimestampNS; } export interface NotificationNewMessageEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; message_id: string; - /** * The number of watchers */ watcher_count: number; - /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * Represents any chat message */ message: MessageResponse; - /** * The type of event: "notification.message_new" in this case */ type: string; - /** * The ID of the channel where the message was sent */ channel_id?: string; - channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel where the message was sent */ channel_type?: string; - /** * The CID of the channel where the message was sent */ cid?: string; - parent_author?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - total_unread_count?: number; - unread_channels?: number; - unread_count?: number; - /** * The participants of the thread */ thread_participants?: Array; - channel_custom?: CustomChannelData; - grouped_unread_channels?: Record; } @@ -7453,56 +5744,43 @@ export interface NotificationRemovedFromChannelEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - member: ChannelMemberResponse; - /** * The type of event: "notification.removed_from_channel" in this case */ type: string; - /** * The ID of the channel from which the user was removed */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel from which the user was removed */ channel_type?: string; - /** * The CID of the channel from which the user was removed */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - user?: UserResponseCommonFields; } @@ -7510,88 +5788,66 @@ export interface NotificationThreadMessageNewEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; message_id: string; - /** * The ID of the thread */ thread_id: string; - /** * The number of watchers */ watcher_count: number; - /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * Represents any chat message */ message: MessageResponse; - /** * The type of event: "notification.message_new" in this case */ type: string; - /** * The ID of the channel where the message was sent */ channel_id?: string; - channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel where the message was sent */ channel_type?: string; - /** * The CID of the channel where the message was sent */ cid?: string; - parent_author?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - unread_thread_messages?: number; - unread_threads?: number; - /** * The participants of the thread */ thread_participants?: Array; - channel_custom?: CustomChannelData; } export interface OCRContentParameters { label_operator?: string; - severity?: string; - harm_labels?: Array; } export interface OCRRule { action: 'flag' | 'shadow' | 'remove' | 'bounce' | 'bounce_flag' | 'bounce_remove'; - label: string; } @@ -7601,77 +5857,49 @@ export interface OnlyUserID { export interface OwnUserResponse { banned: boolean; - - created_at: number; - + created_at: TimestampNS; id: string; - invisible: boolean; - language: string; - online: boolean; - role: string; - total_unread_count: number; - unread_channels: number; - unread_count: number; - unread_threads: number; - - updated_at: number; - + updated_at: TimestampNS; channel_mutes: Array; - devices: Array; - mutes: Array; - teams: Array; - custom: CustomUserData; - avg_response_time?: number; - - deactivated_at?: number; - - deleted_at?: number; - + deactivated_at?: TimestampNS; + deleted_at?: TimestampNS; image?: string; - - last_active?: number; - + last_active?: TimestampNS; name?: string; - - revoke_tokens_issued_before?: number; - + revoke_tokens_issued_before?: TimestampNS; blocked_user_ids?: Array; - latest_hidden_channels?: Array; - privacy_settings?: PrivacySettingsResponse; - push_preferences?: PushPreferencesResponse; - teams_role?: Record; - total_unread_count_by_team?: Record; } export interface PaginationParams { + id_gt?: number; + id_gte?: number; + id_lt?: number; + id_lte?: number; limit?: number; - offset?: number; } export interface ParsedPredefinedFilterResponse { name: string; - filter: Record; - sort?: Array; } @@ -7679,37 +5907,29 @@ export interface PendingMessageEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The method used for the pending message */ method: string; - custom: CustomEventData; - /** * The type of event: "message.pending" in this case */ type: string; - - received_at?: number; - + received_at?: TimestampNS; /** * Represents channel in chat */ channel?: ChannelResponse; - /** * Represents any chat message */ message?: MessageResponse; - /** * Metadata attached to the pending message */ metadata?: Record; - /** * User response object */ @@ -7721,12 +5941,10 @@ export interface PendingMessageResponse { * Represents channel in chat */ channel?: ChannelResponse; - /** * Represents any chat message */ message?: MessageResponse; - /** * User response object */ @@ -7737,73 +5955,56 @@ export interface PollClosedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - poll: PollResponseData; - /** * The type of event: "poll.closed" in this case */ type: string; - activity_id?: string; - /** * The CID of the channel containing the poll */ cid?: string; - /** * The ID of the message containing the poll */ message_id?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface PollDeletedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - poll: PollResponseData; - /** * The type of event: "poll.deleted" in this case */ type: string; - activity_id?: string; - /** * The CID of the channel containing the poll */ cid?: string; - /** * The ID of the message containing the poll */ message_id?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface PollOptionInput { text?: string; - custom?: CustomPollOptionData; } export interface PollOptionRequest { id: string; - text?: string; - custom?: CustomPollOptionData; } @@ -7812,16 +6013,14 @@ export interface PollOptionResponse { * Duration of the request in milliseconds */ duration: string; - poll_option: PollOptionResponseData; } export interface PollOptionResponseData { id: string; - text: string; - custom: CustomPollOptionData; + text_i18n?: Record; } export interface PollResponse { @@ -7829,185 +6028,134 @@ export interface PollResponse { * Duration of the request in milliseconds */ duration: string; - poll: PollResponseData; } export interface PollResponseData { allow_answers: boolean; - allow_user_suggested_options: boolean; - answers_count: number; - - created_at: number; - + created_at: TimestampNS; created_by_id: string; - description: string; - enforce_unique_vote: boolean; - id: string; - name: string; - - updated_at: number; - + updated_at: TimestampNS; vote_count: number; - /** * Voting visibility of the poll */ - voting_visibility: 'anonymous' | 'public'; - latest_answers: Array; - options: Array; - own_votes: Array; - custom: CustomPollData; - latest_votes_by_option: Record>; - vote_counts_by_option: Record; - is_closed?: boolean; - max_votes_allowed?: number; - /** * User response object */ created_by?: UserResponse; + description_i18n?: Record; + name_i18n?: Record; } export interface PollUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - poll: PollResponseData; - /** * The type of event: "poll.updated" in this case */ type: string; - activity_id?: string; - /** * The CID of the channel containing the poll */ cid?: string; - /** * The ID of the message containing the poll */ message_id?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface PollVoteCastedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - poll: PollResponseData; - poll_vote: PollVoteResponseData; - /** * The type of event: "poll.vote_casted" in this case */ type: string; - activity_id?: string; - /** * The CID of the channel containing the poll */ cid?: string; - /** * The ID of the message containing the poll */ message_id?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface PollVoteChangedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - poll: PollResponseData; - poll_vote: PollVoteResponseData; - /** * The type of event: "poll.vote_changed" in this case */ type: string; - activity_id?: string; - /** * The CID of the channel containing the poll */ cid?: string; - /** * The ID of the message containing the poll */ message_id?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface PollVoteRemovedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - poll: PollResponseData; - poll_vote: PollVoteResponseData; - /** * The type of event: "poll.vote_removed" in this case */ type: string; - activity_id?: string; - /** * The CID of the channel containing the poll */ cid?: string; - /** * The ID of the message containing the poll */ message_id?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface PollVoteResponse { @@ -8015,29 +6163,20 @@ export interface PollVoteResponse { * Duration of the request in milliseconds */ duration: string; - poll?: PollResponseData; - vote?: PollVoteResponseData; } export interface PollVoteResponseData { - created_at: number; - + created_at: TimestampNS; id: string; - option_id: string; - poll_id: string; - - updated_at: number; - + updated_at: TimestampNS; answer_text?: string; - is_answer?: boolean; - user_id?: string; - + answer_text_i18n?: Record; /** * User response object */ @@ -8049,22 +6188,17 @@ export interface PollVotesResponse { * Duration of the request in milliseconds */ duration: string; - /** * Poll votes */ votes: Array; - next?: string; - prev?: string; } export interface PrivacySettingsResponse { delivery_receipts?: DeliveryReceiptsResponse; - read_receipts?: ReadReceiptsResponse; - typing_indicators?: TypingIndicatorsResponse; } @@ -8072,18 +6206,14 @@ export interface PushPreferenceInput { /** * Set the level of call push notifications for the user. One of: all, none, default */ - call_level?: 'all' | 'none' | 'default'; - /** * Set the push preferences for a specific channel. If empty it sets the default for the user */ channel_cid?: string; - /** * Set the level of chat push notifications for the user. Note: "mentions" is deprecated in favor of "direct_mentions". One of: all, mentions, direct_mentions, all_mentions, none, default */ - chat_level?: | 'all' | 'mentions' @@ -8091,59 +6221,43 @@ export interface PushPreferenceInput { | 'all_mentions' | 'none' | 'default'; - /** * Disable push notifications till a certain time */ disabled_until?: Date; - /** * Set the level of feeds push notifications for the user. One of: all, none, default */ - feeds_level?: 'all' | 'none' | 'default'; - /** * Remove the disabled until time. (IE stop snoozing notifications) */ remove_disable?: boolean; - /** * The user id for which to set the push preferences. Required when using server side auths, defaults to current user with client side auth. */ user_id?: string; - chat_preferences?: ChatPreferencesInput; - feeds_preferences?: FeedsPreferences; } export interface PushPreferencesResponse { call_level?: string; - chat_level?: string; - - disabled_until?: number; - + disabled_until?: TimestampNS; feeds_level?: string; - chat_preferences?: ChatPreferencesResponse; - feeds_preferences?: FeedsPreferencesResponse; } export interface QueryAppealsRequest { limit?: number; - next?: string; - prev?: string; - /** * Sorting parameters for appeals */ sort?: Array; - /** * Filter conditions for appeals */ @@ -8207,14 +6321,11 @@ export interface QueryAppealsRequest { export interface QueryAppealsResponse { duration: string; - /** * List of Appeal Items */ items: Array; - next?: string; - prev?: string; } @@ -8256,22 +6367,22 @@ export interface QueryBannedUsersPayload { operators: '$eq' | '$exists' | '$gt' | '$gte' | '$in' | '$lt' | '$lte'; }; }>; - + created_at_after?: TimestampNS; + created_at_after_or_equal?: TimestampNS; + created_at_before?: TimestampNS; + created_at_before_or_equal?: TimestampNS; /** * Whether to exclude expired bans or not */ exclude_expired_bans?: boolean; - /** * Number of records to return */ limit?: number; - /** * Number of records to offset */ offset?: number; - /** * Array of sort parameters */ @@ -8283,7 +6394,6 @@ export interface QueryBannedUsersResponse { * Duration of the request in milliseconds */ duration: string; - /** * List of found bans */ @@ -8295,49 +6405,39 @@ export interface QueryChannelsRequest { * Number of channels to limit */ limit?: number; - /** * Number of members to limit */ member_limit?: number; - /** * Number of messages to limit */ message_limit?: number; - /** * Channel pagination offset */ offset?: number; - /** * ID of a predefined filter to use instead of filter_conditions */ predefined_filter?: string; - presence?: boolean; - /** * Whether to update channel state or not */ state?: boolean; - /** * Whether to start watching found channels or not */ watch?: boolean; - /** * Top-level keys of the message sender's channel-member custom data to include under member.custom (max 8 keys, 64 chars each) */ member_custom_include?: Array; - /** * List of sort parameters */ sort?: Array; - /** * Filter conditions to apply to the query */ @@ -8509,12 +6609,10 @@ export interface QueryChannelsRequest { operators: '$eq' | '$exists' | '$gt' | '$gte' | '$in' | '$lt' | '$lte'; }; }>; - /** * Values to interpolate into the predefined filter template */ filter_values?: Record; - sort_values?: Record; } @@ -8523,27 +6621,21 @@ export interface QueryChannelsResponse { * Duration of the request in milliseconds */ duration: string; - /** * List of channels */ channels: Array; - predefined_filter?: ParsedPredefinedFilterResponse; } export interface QueryDraftsRequest { limit?: number; - next?: string; - prev?: string; - /** * Array of sort parameters */ sort?: Array; - /** * Filter to apply to the query */ @@ -8570,38 +6662,35 @@ export interface QueryDraftsResponse { * Duration of the request in milliseconds */ duration: string; - /** * Drafts */ drafts: Array; - next?: string; - prev?: string; } export interface QueryFutureChannelBansPayload { + created_at_after?: TimestampNS; + created_at_after_or_equal?: TimestampNS; + created_at_before?: TimestampNS; + created_at_before_or_equal?: TimestampNS; /** * Whether to exclude expired bans or not */ exclude_expired_bans?: boolean; - /** * When true, the response includes the total number of bans matching the query filter (independent of limit and offset, capped at 100000) */ include_total?: boolean; - /** * Number of records to return */ limit?: number; - /** * Number of records to offset */ offset?: number; - /** * Filter by the target user ID. Server-side: returns all bans against this user. Client-side: narrows the authenticated user's own bans to this target. */ @@ -8613,12 +6702,10 @@ export interface QueryFutureChannelBansResponse { * Duration of the request in milliseconds */ duration: string; - /** * List of found future channel bans */ bans: Array; - /** * Total number of bans matching the query filter, computed at query time and capped at 100000. Only present when include_total is set on the request; omitted when computing the total timed out */ @@ -8627,20 +6714,22 @@ export interface QueryFutureChannelBansResponse { export interface QueryMembersPayload { type: string; - + created_at_after?: TimestampNS; + created_at_after_or_equal?: TimestampNS; + created_at_before?: TimestampNS; + created_at_before_or_equal?: TimestampNS; id?: string; - limit?: number; - offset?: number; - + user_id_gt?: string; + user_id_gte?: string; + user_id_lt?: string; + user_id_lte?: string; members?: Array; - /** * Array of sort parameters */ sort?: Array; - /** * Filter conditions to apply to the query */ @@ -8739,19 +6828,15 @@ export interface QueryMembersPayload { export interface QueryMessageFlagsPayload { limit?: number; - offset?: number; - /** * Whether to include deleted messages in the results */ show_deleted_messages?: boolean; - /** * Array of sort parameters */ sort?: Array; - /** * Filter conditions to apply to the query */ @@ -8848,7 +6933,6 @@ export interface QueryMessageFlagsResponse { * Duration of the request in milliseconds */ duration: string; - /** * The flags that match the query */ @@ -8857,16 +6941,12 @@ export interface QueryMessageFlagsResponse { export interface QueryModerationConfigsRequest { limit?: number; - next?: string; - prev?: string; - /** * Sorting parameters for the results */ sort?: Array; - /** * Filter conditions for moderation configs */ @@ -8903,29 +6983,22 @@ export interface QueryModerationConfigsRequest { export interface QueryModerationConfigsResponse { duration: string; - /** * List of moderation configurations */ configs: Array; - next?: string; - prev?: string; } export interface QueryPollVotesRequest { limit?: number; - next?: string; - prev?: string; - /** * Array of sort parameters */ sort?: Array; - /** * Filter to apply to the query */ @@ -8969,16 +7042,12 @@ export interface QueryPollVotesRequest { export interface QueryPollsRequest { limit?: number; - next?: string; - prev?: string; - /** * Array of sort parameters */ sort?: Array; - /** * Filter to apply to the query */ @@ -9055,29 +7124,22 @@ export interface QueryPollsResponse { * Duration of the request in milliseconds */ duration: string; - /** * Polls data returned by the query */ polls: Array; - next?: string; - prev?: string; } export interface QueryReactionsRequest { limit?: number; - next?: string; - prev?: string; - /** * Array of sort parameters */ sort?: Array; - /** * Filter to apply to the query */ @@ -9104,26 +7166,19 @@ export interface QueryReactionsResponse { * Duration of the request in milliseconds */ duration: string; - reactions: Array; - next?: string; - prev?: string; } export interface QueryRemindersRequest { limit?: number; - next?: string; - prev?: string; - /** * Array of sort parameters */ sort?: Array; - /** * Filter to apply to the query */ @@ -9155,51 +7210,39 @@ export interface QueryRemindersResponse { * Duration of the request in milliseconds */ duration: string; - /** * MessageReminders data returned by the query */ reminders: Array; - next?: string; - prev?: string; } export interface QueryReviewQueueRequest { exclude_default_action_config?: boolean; - limit?: number; - /** * Number of items to lock (1-25) */ lock_count?: number; - /** * Duration for which items should be locked */ lock_duration?: number; - /** * Whether to lock items for review (true), unlock items (false), or just fetch (nil) */ lock_items?: boolean; - next?: string; - prev?: string; - /** * Whether to return only statistics */ stats_only?: boolean; - /** * Sorting parameters for the results */ sort?: Array; - /** * Filter conditions for review queue items. Accepts built-in fields (e.g. status, channel_cid, severity, recommended_action) and customer-supplied moderation_payload.custom keys: any key that is not a built-in field is matched against the item's custom moderation data (e.g. {"location_id": "loc-42"}). Use filter_config.filterable_custom_keys to discover which custom keys the app exposes as chips. */ @@ -9254,6 +7297,11 @@ export interface QueryReviewQueueRequest { operators: '$eq' | '$exists' | '$gt' | '$gte' | '$in' | '$lt' | '$lte'; }; + confidence_score: { + type: number; + operators: '$eq' | '$exists' | '$gt' | '$gte' | '$in' | '$lt' | '$lte'; + }; + config_key: { type: string; operators: '$eq' | '$exists' | '$gt' | '$gte' | '$in' | '$lt' | '$lte'; @@ -9291,7 +7339,15 @@ export interface QueryReviewQueueRequest { entity_creator_name: { type: string; - operators: '$eq' | '$exists' | '$gt' | '$gte' | '$in' | '$lt' | '$lte'; + operators: + | '$contains' + | '$eq' + | '$exists' + | '$gt' + | '$gte' + | '$in' + | '$lt' + | '$lte'; }; entity_id: { @@ -9443,60 +7499,45 @@ export interface QueryReviewQueueRequest { export interface QueryReviewQueueResponse { duration: string; - /** * List of review queue items */ items: Array; - /** * Configuration for moderation actions */ action_config: Record>; - /** * Statistics about the review queue */ stats: Record; - next?: string; - prev?: string; - default_action_config?: Record>; - filter_config?: FilterConfigResponse; } export interface QueryThreadsRequest { limit?: number; - member_limit?: number; - next?: string; - /** * Limit the number of participants returned per each thread */ participant_limit?: number; - prev?: string; - /** * Limit the number of replies returned per each thread */ reply_limit?: number; - /** * Start watching the channel this thread belongs to */ watch?: boolean; - /** * Array of sort parameters */ sort?: Array; - /** * Filter to apply to the query */ @@ -9583,14 +7624,11 @@ export interface QueryThreadsResponse { * Duration of the request in milliseconds */ duration: string; - /** * List of enriched thread states */ threads: Array; - next?: string; - prev?: string; } @@ -9695,15 +7733,14 @@ export interface QueryUsersPayload { operators: '$autocomplete' | '$eq'; }; }>; - + id_gt?: string; + id_gte?: string; + id_lt?: string; + id_lte?: string; include_deactivated_users?: boolean; - limit?: number; - offset?: number; - presence?: boolean; - /** * Array of sort parameters */ @@ -9715,7 +7752,6 @@ export interface QueryUsersResponse { * Duration of the request in milliseconds */ duration: string; - /** * Array of users as result of filters applied. */ @@ -9727,43 +7763,26 @@ export interface QueueResponse { * Duration of the request in milliseconds */ duration: string; - queue?: ModerationQueueResponse; } export interface Reaction { activity_id: string; - - created_at: number; - + created_at: TimestampNS; kind: string; - - updated_at: number; - + updated_at: TimestampNS; user_id: string; - - deleted_at?: number; - + deleted_at?: TimestampNS; id?: string; - parent?: string; - score?: number; - target_feeds?: Array; - children_counts?: Record; - data?: Record; - latest_children?: Record>; - moderation?: Record; - own_children?: Record>; - target_feeds_extra_data?: Record; - user?: User; } @@ -9771,68 +7790,52 @@ export interface ReactionDeletedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * The type of event: "reaction.deleted" in this case */ type: string; - /** * The ID of the channel containing the message */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel containing the message */ channel_type?: string; - /** * The CID of the channel containing the message */ cid?: string; - message_id?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - /** * The participants of the thread */ thread_participants?: Array; - channel_custom?: CustomChannelData; - /** * Represents any chat message */ message?: MessageResponse; - reaction?: ReactionResponse; - user?: UserResponseCommonFields; } @@ -9841,22 +7844,18 @@ export interface ReactionGroupResponse { * Count is the number of reactions of this type. */ count: number; - /** * FirstReactionAt is the time of the first reaction of this type. This is the same also if all reaction of this type are deleted, because if someone will react again with the same type, will be preserved the sorting. */ - first_reaction_at: number; - + first_reaction_at: TimestampNS; /** * LastReactionAt is the time of the last reaction of this type. */ - last_reaction_at: number; - + last_reaction_at: TimestampNS; /** * SumScores is the sum of all scores of reactions of this type. Medium allows you to clap articles more than once and shows the sum of all claps from all users. For example, you can send `clap` x5 using `score: 5`. */ sum_scores: number; - /** * The most recent users who reacted with this type, ordered by most recent first. */ @@ -9867,13 +7866,11 @@ export interface ReactionGroupUserResponse { /** * The time when the user reacted. */ - created_at: number; - + created_at: TimestampNS; /** * The ID of the user who reacted. */ user_id: string; - /** * User response object */ @@ -9884,68 +7881,52 @@ export interface ReactionNewEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * The type of event: "reaction.new" in this case */ type: string; - /** * The ID of the channel containing the message */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel containing the message */ channel_type?: string; - /** * The CID of the channel containing the message */ cid?: string; - message_id?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - /** * The participants of the thread */ thread_participants?: Array; - channel_custom?: CustomChannelData; - /** * Represents any chat message */ message?: MessageResponse; - reaction?: ReactionResponse; - user?: UserResponseCommonFields; } @@ -9954,22 +7935,18 @@ export interface ReactionRequest { * The type of reaction (e.g. 'like', 'laugh', 'wow') */ type: string; - /** * Date/time of creation */ created_at?: Date; - /** * Reaction score. If not specified reaction has score of 1 */ score?: number; - /** * Date/time of the last update */ updated_at?: Date; - custom?: CustomReactionData; } @@ -9977,38 +7954,31 @@ export interface ReactionResponse { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Message ID */ message_id: string; - /** * Score of the reaction */ score: number; - /** * Type of reaction */ type: string; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; /** * User ID */ user_id: string; - /** * Custom data for this object */ custom: CustomReactionData; - /** * User response object */ @@ -10019,63 +7989,48 @@ export interface ReactionUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; message_id: string; - /** * Represents channel in chat */ channel: ChannelResponse; - custom: CustomEventData; - /** * Represents any chat message */ message: MessageResponse; - /** * The type of event: "reaction.updated" in this case */ type: string; - /** * The ID of the channel containing the message */ channel_id?: string; - /** * The number of members in the channel */ channel_member_count?: number; - /** * The number of messages in the channel */ channel_message_count?: number; - /** * The type of the channel containing the message */ channel_type?: string; - /** * The CID of the channel containing the message */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The team ID */ team?: string; - channel_custom?: CustomChannelData; - reaction?: ReactionResponse; - user?: UserResponseCommonFields; } @@ -10084,19 +8039,14 @@ export interface ReadReceiptsResponse { } export interface ReadStateResponse { - last_read: number; - + last_read: TimestampNS; unread_messages: number; - /** * User response object */ user: UserResponse; - - last_delivered_at?: number; - + last_delivered_at?: TimestampNS; last_delivered_message_id?: string; - last_read_message_id?: string; } @@ -10112,37 +8062,29 @@ export interface ReminderCreatedEvent { * The CID of the Channel for which the reminder was created */ cid: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The ID of the message for which the reminder was created */ message_id: string; - /** * The ID of the user for whom the reminder was created */ user_id: string; - custom: CustomEventData; - reminder: ReminderResponseData; - /** * The type of event: "reminder.created" in this case */ type: string; - /** * The ID of the parent message, if the reminder is for a thread message */ parent_id?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface ReminderDeletedEvent { @@ -10150,37 +8092,29 @@ export interface ReminderDeletedEvent { * The CID of the Channel for which the reminder was created */ cid: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The ID of the message for which the reminder was created */ message_id: string; - /** * The ID of the user for whom the reminder was created */ user_id: string; - custom: CustomEventData; - reminder: ReminderResponseData; - /** * The type of event: "reminder.deleted" in this case */ type: string; - /** * The ID of the parent message, if the reminder is for a thread message */ parent_id?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface ReminderNotificationEvent { @@ -10188,59 +8122,43 @@ export interface ReminderNotificationEvent { * The CID of the Channel for which the reminder was created */ cid: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The ID of the message for which the reminder was created */ message_id: string; - /** * The ID of the user for whom the reminder was created */ user_id: string; - custom: CustomEventData; - reminder: ReminderResponseData; - /** * The type of event: "notification.reminder_due" in this case */ type: string; - parent_id?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface ReminderResponseData { channel_cid: string; - - created_at: number; - + created_at: TimestampNS; message_id: string; - - updated_at: number; - + updated_at: TimestampNS; user_id: string; - - remind_at?: number; - + remind_at?: TimestampNS; /** * Represents channel in chat */ channel?: ChannelResponse; - /** * Represents any chat message */ message?: MessageResponse; - /** * User response object */ @@ -10252,37 +8170,29 @@ export interface ReminderUpdatedEvent { * The CID of the Channel for which the reminder was created */ cid: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The ID of the message for which the reminder was created */ message_id: string; - /** * The ID of the user for whom the reminder was created */ user_id: string; - custom: CustomEventData; - reminder: ReminderResponseData; - /** * The type of event: "reminder.updated" in this case */ type: string; - /** * The ID of the parent message, if the reminder is for a thread message */ parent_id?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface RemoveUserGroupMembersRequest { @@ -10290,13 +8200,11 @@ export interface RemoveUserGroupMembersRequest { * List of user IDs to remove */ member_ids: Array; - team_id?: string; } export interface RemoveUserGroupMembersResponse { duration: string; - user_group?: UserGroupResponse; } @@ -10319,143 +8227,111 @@ export interface ReviewQueueItemResponse { * AI-determined text severity */ ai_text_severity: string; - /** * When the item was created */ - created_at: number; - + created_at: TimestampNS; /** * ID of the entity being reviewed */ entity_id: string; - /** * Type of entity being reviewed */ entity_type: string; - /** * Whether the item has been escalated */ escalated: boolean; - flags_count: number; - /** * Unique identifier of the review queue item */ id: string; - latest_moderator_action: string; - /** * Suggested moderation action */ recommended_action: string; - /** * ID of the moderator who reviewed the item */ reviewed_by: string; - /** * Severity level of the content */ severity: number; - /** * Current status of the review */ status: string; - /** * When the item was last updated */ - updated_at: number; - + updated_at: TimestampNS; /** * Moderation actions taken */ actions: Array; - /** * Associated ban records */ bans: Array; - /** * Associated flag records */ flags: Array; - /** * Detected languages in the content */ languages: Array; - /** * When the review was completed */ - completed_at?: number; - + completed_at?: TimestampNS; + /** + * Highest per-label confidence (0-1) any provider reported across the item's flags; absent when no flag carried one + */ + confidence_score?: number; config_key?: string; - /** * ID of who created the entity */ entity_creator_id?: string; - /** * When the item was escalated */ - escalated_at?: number; - + escalated_at?: TimestampNS; /** * ID of the moderator who escalated the item */ escalated_by?: string; - /** * When the item was reviewed */ - reviewed_at?: number; - + reviewed_at?: TimestampNS; /** * Teams associated with this item */ teams?: Array; - activity?: EnrichedActivity; - appeal?: AppealItemResponse; - /** * User response object */ assigned_to?: UserResponse; - call?: ModerationCallResponse; - entity_creator?: EntityCreatorResponse; - escalation_metadata?: EscalationMetadata; - feeds_v2_activity?: EnrichedActivity; - feeds_v2_reaction?: Reaction; - feeds_v3_activity?: FeedsV3ActivityResponse; - feeds_v3_comment?: FeedsV3CommentResponse; - message?: ChatMessageResponse; - /** * Content payload for moderation */ moderation_payload?: ModerationPayloadResponse; - reaction?: Reaction; } @@ -10463,23 +8339,19 @@ export interface Role { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Whether this is a custom role or built-in */ custom: boolean; - /** * Unique role name */ name: string; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; /** * List of scopes where this role is currently present. `.app` means that role is present in app-level grants */ @@ -10488,9 +8360,7 @@ export interface Role { export interface RuleBuilderAction { reason?: string; - skip_inbox?: boolean; - type?: | 'ban_user' | 'flag_user' @@ -10509,103 +8379,65 @@ export interface RuleBuilderAction { | 'warning' | 'call_warning' | 'webhook_only'; - ban_options?: BanOptions; - call_options?: CallActionOptions; - flag_user_options?: FlagUserOptions; } export interface RuleBuilderCondition { confidence?: number; - type?: string; - call_custom_property_params?: CallCustomPropertyParameters; - call_type_rule_params?: CallTypeRuleParameters; - call_violation_count_params?: CallViolationCountParameters; - channel_message_count_rule_params?: ChannelMessageCountRuleParameters; - closed_caption_rule_params?: ClosedCaptionRuleParameters; - content_count_rule_params?: ContentCountRuleParameters; - content_custom_property_count_params?: ContentCustomPropertyCountParameters; - content_custom_property_params?: ContentCustomPropertyParameters; - content_flag_count_rule_params?: FlagCountRuleParameters; - flood_identical_params?: FloodIdenticalRuleParameters; - flood_similar_params?: FloodSimilarRuleParameters; - image_content_params?: ImageContentParameters; - image_rule_params?: ImageRuleParameters; - ip_content_count_rule_params?: IPContentCountRuleParameters; - ip_flag_count_rule_params?: IPFlagCountRuleParameters; - keyframe_ocr_rule_params?: KeyframeOCRRuleParameters; - keyframe_rule_params?: KeyframeRuleParameters; - ocr_content_params?: OCRContentParameters; - text_content_params?: TextContentParameters; - text_rule_params?: TextRuleParameters; - + user_channel_count_params?: UserChannelCountRuleParameters; user_created_within_params?: UserCreatedWithinParameters; - user_custom_property_params?: UserCustomPropertyParameters; - user_flag_count_rule_params?: FlagCountRuleParameters; - user_identical_content_count_params?: UserIdenticalContentCountParameters; - + user_identical_image_count_params?: UserIdenticalImageCountParameters; + user_reaction_count_params?: UserReactionCountRuleParameters; user_role_params?: UserRoleParameters; - user_rule_params?: UserRuleParameters; - video_content_params?: VideoContentParameters; - video_rule_params?: VideoRuleParameters; } export interface RuleBuilderConditionGroup { logic?: string; - conditions?: Array; } export interface RuleBuilderConfig { async?: boolean; - rules?: Array; } export interface RuleBuilderRule { rule_type: string; - cooldown_period?: string; - id?: string; - logic?: string; - action_sequences?: Array; - conditions?: Array; - groups?: Array; - action?: RuleBuilderAction; } @@ -10781,36 +8613,28 @@ export interface SearchPayload { operators: '$eq' | '$exists' | '$gt' | '$gte' | '$in' | '$lt' | '$lte'; }; }>; - force_default_search?: boolean; - force_sql_v2_backend?: boolean; - /** * Number of messages to return */ limit?: number; - /** * Pagination parameter. Cannot be used with non-zero offset. */ next?: string; - /** * Pagination offset. Cannot be used with sort or next. */ offset?: number; - /** * Search phrase */ query?: string; - /** * Sort parameters. Cannot be used with non-zero offset */ sort?: Array; - /** * Message filter conditions */ @@ -10900,7 +8724,6 @@ export interface SearchPayload { operators: '$eq' | '$in'; }; }>; - message_options?: MessageOptions; } @@ -10909,22 +8732,18 @@ export interface SearchResponse { * Duration of the request in milliseconds */ duration: string; - /** * Search results */ results: Array; - /** * Value to pass to the next search query in order to paginate */ next?: string; - /** * Value that points to the previous page. Pass as the next value in a search query to paginate backwards */ previous?: string; - results_warning?: SearchWarning; } @@ -10934,123 +8753,72 @@ export interface SearchResult { export interface SearchResultMessage { cid: string; - - created_at: number; - + created_at: TimestampNS; deleted_reply_count: number; - html: string; - id: string; - mentioned_channel: boolean; - mentioned_here: boolean; - pinned: boolean; - reply_count: number; - shadowed: boolean; - silent: boolean; - text: string; - type: string; - - updated_at: number; - + updated_at: TimestampNS; attachments: Array; - latest_reactions: Array; - mentioned_users: Array; - own_reactions: Array; - restricted_visibility: Array; - custom: CustomMessageData; - reaction_counts: Record; - reaction_scores: Record; - /** * User response object */ user: UserResponse; - command?: string; - - deleted_at?: number; - + deleted_at?: TimestampNS; deleted_for_me?: boolean; - - message_text_updated_at?: number; - + message_text_updated_at?: TimestampNS; mml?: string; - parent_id?: string; - - pin_expires?: number; - - pinned_at?: number; - + pin_expires?: TimestampNS; + pinned_at?: TimestampNS; poll_id?: string; - quoted_message_id?: string; - show_in_channel?: boolean; - mentioned_group_ids?: Array; - mentioned_groups?: Array; - mentioned_roles?: Array; - thread_participants?: Array; - /** * Represents channel in chat */ channel?: ChannelResponse; - draft?: DraftResponse; - i18n?: Record; - image_labels?: Record>; - member?: ChannelMemberPartialResponse; - mentioned_channel_members?: Record; - moderation?: ModerationV2Response; - /** * User response object */ pinned_by?: UserResponse; - poll?: PollResponseData; - /** * Represents any chat message */ quoted_message?: MessageResponse; - reaction_groups?: Record; - reminder?: ReminderResponseData; - shared_location?: SharedLocationResponseData; } export interface SearchRolesResponse { duration: string; - /** * Matching roles, sorted ascending by name */ @@ -11059,7 +8827,6 @@ export interface SearchRolesResponse { export interface SearchUserGroupsResponse { duration: string; - /** * List of matching user groups */ @@ -11071,17 +8838,14 @@ export interface SearchWarning { * Code corresponding to the warning */ warning_code: number; - /** * Description of the warning */ warning_description: string; - /** * Number of channels searched */ channel_search_count?: number; - /** * Channel CIDs for the searched channels */ @@ -11097,21 +8861,16 @@ export interface SendMessageRequest { * Message data for creating or updating a message */ message: MessageRequest; - /** * When true, the response includes channel_context: a slim channel object with cid, type, id and created_by */ include_channel_context?: boolean; - /** * When true, the response includes mentioned_members: for each mentioned user, whether that user is currently a channel member. Requires the ReadChannelMembers permission */ include_mentioned_members?: boolean; - keep_channel_hidden?: boolean; - skip_enrich_url?: boolean; - skip_push?: boolean; } @@ -11120,17 +8879,14 @@ export interface SendMessageResponse { * Duration of the request in milliseconds */ duration: string; - /** * Represents any chat message */ message: MessageResponse; - /** * Slim channel object: identity plus creator */ channel_context?: ChannelContextResponse; - /** * Map of mentioned user ID to whether that user is currently an active channel member. Only set when include_mentioned_members was requested; omitted when the message has no mentions or the membership lookup failed */ @@ -11142,12 +8898,10 @@ export interface SendReactionRequest { * Represents user reaction to a message */ reaction: ReactionRequest; - /** * Whether to replace all existing user reactions */ enforce_unique?: boolean; - /** * Skips any mobile push notifications */ @@ -11159,12 +8913,10 @@ export interface SendReactionResponse { * Duration of the request in milliseconds */ duration: string; - /** * Represents any chat message */ message: MessageResponse; - reaction: ReactionResponse; } @@ -11177,11 +8929,8 @@ export interface ShadowBlockActionRequestPayload { export interface SharedLocation { latitude: number; - longitude: number; - created_by_device_id?: string; - end_at?: Date; } @@ -11190,54 +8939,43 @@ export interface SharedLocationResponse { * Channel CID */ channel_cid: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Device ID that created the live location */ created_by_device_id: string; - duration: string; - /** * Latitude coordinate */ latitude: number; - /** * Longitude coordinate */ longitude: number; - /** * Message ID */ message_id: string; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; /** * User ID */ user_id: string; - /** * Time when the live location expires */ - end_at?: number; - + end_at?: TimestampNS; /** * Represents channel in chat */ channel?: ChannelResponse; - /** * Represents any chat message */ @@ -11246,28 +8984,18 @@ export interface SharedLocationResponse { export interface SharedLocationResponseData { channel_cid: string; - - created_at: number; - + created_at: TimestampNS; created_by_device_id: string; - latitude: number; - longitude: number; - message_id: string; - - updated_at: number; - + updated_at: TimestampNS; user_id: string; - - end_at?: number; - + end_at?: TimestampNS; /** * Represents channel in chat */ channel?: ChannelResponse; - /** * Represents any chat message */ @@ -11276,7 +9004,6 @@ export interface SharedLocationResponseData { export interface SharedLocationsResponse { duration: string; - active_live_locations: Array; } @@ -11294,12 +9021,10 @@ export interface SortParamRequest { * Direction of sorting, 1 for Ascending, -1 for Descending, default is 1. One of: -1, 1 */ direction?: number; - /** * Name of field to sort by */ field?: string; - /** * Type of field to sort by. Empty string or omitted means string type (default). One of: number, boolean */ @@ -11310,7 +9035,6 @@ export interface SubmitActionRequest { /** * Type of moderation action to perform. One of: mark_reviewed, delete_message, delete_activity, delete_comment, delete_reaction, ban, custom, unban, restore, delete_user, delete_user_messages, unblock, block, shadow_block, unmask, kick_user, end_call, escalate, de_escalate */ - action_type: | 'flag' | 'mark_reviewed' @@ -11334,96 +9058,76 @@ export interface SubmitActionRequest { | 'escalate' | 'de_escalate' | 'bypass'; - /** * UUID of the appeal to act on (required for reject_appeal, optional for other actions) */ appeal_id?: string; - /** * UUID of the review queue item to act on */ item_id?: string; - /** * Configuration for ban moderation action */ ban?: BanActionRequestPayload; - /** * Configuration for block action */ block?: BlockActionRequestPayload; - bypass?: BypassActionRequest; - /** * Configuration for custom moderation action */ custom?: CustomActionRequestPayload; - /** * Configuration for activity deletion action */ delete_activity?: DeleteActivityRequestPayload; - /** * Configuration for comment deletion action */ delete_comment?: DeleteCommentRequestPayload; - /** * Configuration for message deletion action */ delete_message?: DeleteMessageRequestPayload; - /** * Configuration for reaction deletion action */ delete_reaction?: DeleteReactionRequestPayload; - /** * Configuration for user deletion action */ delete_user?: DeleteUserRequestPayload; - /** * Configuration for deleting all of a user's chat messages without banning them or deleting their account */ delete_user_messages?: DeleteUserMessagesRequestPayload; - /** * Configuration for escalation action */ escalate?: EscalatePayload; - flag?: FlagRequest; - /** * Configuration for mark reviewed action */ mark_reviewed?: MarkReviewedRequestPayload; - /** * Configuration for rejecting an appeal */ reject_appeal?: RejectAppealRequestPayload; - /** * Configuration for restore action. State-aware: reverses whichever of a delete, a block, or a shadow block currently applies to the content (including both a delete and a block/shadow block at once). */ restore?: RestoreActionRequestPayload; - /** * Configuration for shadow block action */ shadow_block?: ShadowBlockActionRequestPayload; - /** * Configuration for unban moderation action */ unban?: UnbanActionRequestPayload; - /** * Deprecated: use restore instead — it now also reverses a block or shadow block. Configuration for unblock action. */ @@ -11432,14 +9136,11 @@ export interface SubmitActionRequest { export interface SubmitActionResponse { duration: string; - /** * Present when the appeal was accepted but the entity could not be restored automatically. The moderator should restore it manually. */ auto_restore_warning?: string; - appeal_item?: AppealItemResponse; - item?: ReviewQueueItemResponse; } @@ -11448,7 +9149,6 @@ export interface SyncRequest { * Date from which synchronization should happen */ last_sync_at: Date; - /** * List of channel CIDs to sync */ @@ -11460,12 +9160,10 @@ export interface SyncResponse { * Duration of the request in milliseconds */ duration: string; - /** * List of events */ events: Array; - /** * List of CIDs that user can't access */ @@ -11474,71 +9172,48 @@ export interface SyncResponse { export interface TextContentParameters { contains_url?: boolean; - label_operator?: string; - severity?: string; - text_length?: number; - text_length_operator?: string; - blocklist_match?: Array; - harm_labels?: Array; - llm_harm_labels?: Record; } export interface TextRuleParameters { contains_url?: boolean; - semantic_filter_min_threshold?: number; - severity?: string; - threshold?: number; - time_window?: string; - blocklist_match?: Array; - harm_labels?: Array; - semantic_filter_names?: Array; - llm_harm_labels?: Record; } export interface ThreadParticipant { channel_cid: string; - /** * Date/time of creation */ - created_at: number; - - last_read_at: number; - + created_at: TimestampNS; + last_read_at: TimestampNS; custom: CustomThreadData; - - last_thread_message_at?: number; - + last_thread_message_at?: TimestampNS; /** * Left Thread At is the time when the user left the thread */ - left_thread_at?: number; - + left_thread_at?: TimestampNS; /** * Thead ID is unique string identifier of the thread */ thread_id?: string; - /** * User ID is unique string identifier of the user */ user_id?: string; - /** * User response object */ @@ -11550,77 +9225,62 @@ export interface ThreadResponse { * Active Participant Count */ active_participant_count: number; - /** * Channel CID */ channel_cid: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Created By User ID */ created_by_user_id: string; - /** * Parent Message ID */ parent_message_id: string; - /** * Participant Count */ participant_count: number; - /** * Reply Count */ reply_count: number; - /** * Title */ title: string; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; /** * Custom data for this object */ custom: CustomThreadData; - /** * Deleted At */ - deleted_at?: number; - + deleted_at?: TimestampNS; /** * Last Message At */ - last_message_at?: number; - + last_message_at?: TimestampNS; /** * Thread Participants */ thread_participants?: Array; - /** * Represents channel in chat */ channel?: ChannelResponse; - /** * User response object */ created_by?: UserResponse; - /** * Represents any chat message */ @@ -11632,83 +9292,65 @@ export interface ThreadStateResponse { * Active Participant Count */ active_participant_count: number; - /** * Channel CID */ channel_cid: string; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Created By User ID */ created_by_user_id: string; - /** * Parent Message ID */ parent_message_id: string; - /** * Participant Count */ participant_count: number; - /** * Reply Count */ reply_count: number; - /** * Title */ title: string; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; latest_replies: Array; - /** * Custom data for this object */ custom: CustomThreadData; - /** * Deleted At */ - deleted_at?: number; - + deleted_at?: TimestampNS; /** * Last Message At */ - last_message_at?: number; - + last_message_at?: TimestampNS; read?: Array; - /** * Thread Participants */ thread_participants?: Array; - /** * Represents channel in chat */ channel?: ChannelResponse; - /** * User response object */ created_by?: UserResponse; - draft?: DraftResponse; - /** * Represents any chat message */ @@ -11716,28 +9358,19 @@ export interface ThreadStateResponse { } export interface ThreadUpdatedEvent { - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - type: string; - channel_id?: string; - channel_type?: string; - cid?: string; - - received_at?: number; - + received_at?: TimestampNS; thread?: ThreadResponse; } export interface Thresholds { explicit?: LabelThresholds; - spam?: LabelThresholds; - toxic?: LabelThresholds; } @@ -11747,7 +9380,6 @@ export interface TranslateMessageRequest { /** * Language to translate message to */ - language: | 'af' | 'sq' @@ -11808,27 +9440,34 @@ export interface TranslateMessageRequest { | 'ht'; } +export interface TranslateMessageResponse { + /** + * Duration of the request in milliseconds + */ + duration: string; + /** + * Represents any chat message + */ + message: MessageResponse; +} + export interface TruncateChannelRequest { /** * Permanently delete channel data (messages, reactions, etc.) */ hard_delete?: boolean; - /** * When `message` is set disables all push notifications for it */ skip_push?: boolean; - /** * Truncate channel data up to `truncated_at`. The system message (if provided) creation time is always greater than `truncated_at` */ truncated_at?: Date; - /** * List of member IDs to hide message history for. If empty, truncates the channel for all members */ member_ids?: Array; - /** * Message data for creating or updating a message */ @@ -11840,12 +9479,10 @@ export interface TruncateChannelResponse { * Duration of the request in milliseconds */ duration: string; - /** * Represents channel in chat */ channel?: ChannelResponse; - /** * Represents any chat message */ @@ -11860,37 +9497,30 @@ export interface TypingStartEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "typing.start" in this case */ type: string; - /** * The ID of the channel where the user started typing */ channel_id?: string; - /** * The type of the channel where the user started typing */ channel_type?: string; - /** * The CID of the channel where the user started typing */ cid?: string; - /** * The parent ID if the user started typing in a thread */ parent_id?: string; - - received_at?: number; - + received_at?: TimestampNS; + member?: ChannelMemberPartialResponse; user?: UserResponseCommonFields; } @@ -11898,37 +9528,30 @@ export interface TypingStopEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "typing.stop" in this case */ type: string; - /** * The ID of the channel where the user stopped typing */ channel_id?: string; - /** * The type of the channel where the user stopped typing */ channel_type?: string; - /** * The CID of the channel where the user stopped typing */ cid?: string; - /** * The parent ID if the user stopped typing in a thread */ parent_id?: string; - - received_at?: number; - + received_at?: TimestampNS; + member?: ChannelMemberPartialResponse; user?: UserResponseCommonFields; } @@ -11937,17 +9560,14 @@ export interface UnbanActionRequestPayload { * Channel CID for channel-specific unban */ channel_cid?: string; - /** * Reason for the appeal decision */ decision_reason?: string; - /** * Also remove the future channels ban for this user */ remove_future_channels_ban?: boolean; - /** * Optional: unban user directly without review item */ @@ -11983,7 +9603,6 @@ export interface UnmuteChannelRequest { * Duration of mute in milliseconds */ expiration?: number; - /** * Channel CIDs to mute (if multiple channels) */ @@ -11999,7 +9618,6 @@ export interface UnmuteRequest { export interface UnmuteResponse { duration: string; - /** * A list of users that can't be found. Common cause for this is deleted users */ @@ -12008,41 +9626,29 @@ export interface UnmuteResponse { export interface UnreadCountsChannel { channel_id: string; - - last_read: number; - + last_read: TimestampNS; unread_count: number; } export interface UnreadCountsChannelType { channel_count: number; - channel_type: string; - unread_count: number; } export interface UnreadCountsThread { - last_read: number; - + last_read: TimestampNS; last_read_message_id: string; - parent_message_id: string; - unread_count: number; } export interface UpdateBlockListRequest { is_confusable_folding_enabled?: boolean; - is_leet_check_enabled?: boolean; - is_plural_check_enabled?: boolean; - is_substring_matching_enabled?: boolean; - team?: string; - /** * List of words to block */ @@ -12054,7 +9660,6 @@ export interface UpdateBlockListResponse { * Duration of the request in milliseconds */ duration: string; - /** * Block list contains restricted words */ @@ -12063,7 +9668,6 @@ export interface UpdateBlockListResponse { export interface UpdateChannelPartialRequest { unset?: Array; - set?: Record; } @@ -12072,12 +9676,10 @@ export interface UpdateChannelPartialResponse { * Duration of the request in milliseconds */ duration: string; - /** * List of updated members */ members: Array; - /** * Represents channel in chat */ @@ -12089,59 +9691,47 @@ export interface UpdateChannelRequest { * Set to `true` to accept the invite */ accept_invite?: boolean; - /** * Sets cool down period for the channel in seconds */ cooldown?: number; - /** * Set to `true` to hide channel's history when adding new members */ hide_history?: boolean; - /** * If set, hides channel's history before this time when adding new members. Takes precedence over `hide_history` when both are provided. Must be in RFC3339 format (e.g., "2024-01-01T10:00:00Z") and in the past. */ hide_history_before?: Date; - /** * Set to `true` to reject the invite */ reject_invite?: boolean; - /** * When `message` is set disables all push notifications for it */ skip_push?: boolean; - /** * List of filter tags to add to the channel */ add_filter_tags?: Array; - /** * List of user IDs to add to the channel */ add_members?: Array; - /** * List of user IDs to invite to the channel */ invites?: Array; - /** * List of filter tags to remove from the channel */ remove_filter_tags?: Array; - /** * List of user IDs to remove from the channel */ remove_members?: Array; - data?: ChannelInputRequest; - /** * Message data for creating or updating a message */ @@ -12153,17 +9743,14 @@ export interface UpdateChannelResponse { * Duration of the request in milliseconds */ duration: string; - /** * List of channel members */ members: Array; - /** * Represents channel in chat */ channel?: ChannelResponse; - /** * Represents any chat message */ @@ -12175,17 +9762,14 @@ export interface UpdateLiveLocationRequest { * Live location ID */ message_id: string; - /** * Time when the live location expires */ end_at?: Date; - /** * Latitude coordinate */ latitude?: number; - /** * Longitude coordinate */ @@ -12194,7 +9778,6 @@ export interface UpdateLiveLocationRequest { export interface UpdateMemberPartialRequest { unset?: Array; - set?: Record; } @@ -12203,7 +9786,6 @@ export interface UpdateMemberPartialResponse { * Duration of the request in milliseconds */ duration: string; - channel_member?: ChannelMemberResponse; } @@ -12212,14 +9794,11 @@ export interface UpdateMessagePartialRequest { * Skip enriching the URL in the message */ skip_enrich_url?: boolean; - skip_push?: boolean; - /** * Array of field names to unset */ unset?: Array; - /** * Sets new field values */ @@ -12231,7 +9810,6 @@ export interface UpdateMessagePartialResponse { * Duration of the request in milliseconds */ duration: string; - /** * Represents any chat message */ @@ -12243,12 +9821,10 @@ export interface UpdateMessageRequest { * Message data for creating or updating a message */ message: MessageRequest; - /** * Skip enrich URL */ skip_enrich_url?: boolean; - skip_push?: boolean; } @@ -12257,7 +9833,6 @@ export interface UpdateMessageResponse { * Duration of the request in milliseconds */ duration: string; - /** * Represents any chat message */ @@ -12269,12 +9844,10 @@ export interface UpdatePollOptionRequest { * Option ID */ id: string; - /** * Option text */ text: string; - /** * Custom data for this object */ @@ -12286,7 +9859,6 @@ export interface UpdatePollPartialRequest { * Array of field names to unset */ unset?: Array; - /** * Sets new field values */ @@ -12298,53 +9870,42 @@ export interface UpdatePollRequest { * Poll ID */ id: string; - /** * Poll name */ name: string; - /** * Allow answers */ allow_answers?: boolean; - /** * Allow user suggested options */ allow_user_suggested_options?: boolean; - /** * Poll description */ description?: string; - /** * Enforce unique vote */ enforce_unique_vote?: boolean; - /** * Is closed */ is_closed?: boolean; - /** * Max votes allowed */ max_votes_allowed?: number; - /** * Voting visibility */ - voting_visibility?: 'anonymous' | 'public'; - /** * Poll options */ options?: Array; - /** * Custom data for this object */ @@ -12353,11 +9914,8 @@ export interface UpdatePollRequest { export interface UpdateQueueRequest { description?: string; - name?: string; - sort?: Array>; - filters?: Record; } @@ -12370,7 +9928,6 @@ export interface UpdateReminderResponse { * Duration of the request in milliseconds */ duration: string; - reminder: ReminderResponseData; } @@ -12379,7 +9936,6 @@ export interface UpdateThreadPartialRequest { * Array of field names to unset */ unset?: Array; - /** * Sets new field values */ @@ -12391,7 +9947,6 @@ export interface UpdateThreadPartialResponse { * Duration of the request in milliseconds */ duration: string; - thread: ThreadResponse; } @@ -12400,18 +9955,15 @@ export interface UpdateUserGroupRequest { * The new description for the group */ description?: string; - /** * The new name of the user group */ name?: string; - team_id?: string; } export interface UpdateUserGroupResponse { duration: string; - user_group?: UserGroupResponse; } @@ -12420,9 +9972,7 @@ export interface UpdateUserPartialRequest { * User ID to update */ id: string; - unset?: Array; - set?: Record; } @@ -12442,9 +9992,11 @@ export interface UpdateUsersResponse { * Duration of the request in milliseconds */ duration: string; - + /** + * @deprecated + * Deprecated: always empty. Removing a user from a team no longer deletes their memberships in that team's channels, so there is no task to poll + */ membership_deletion_task_id: string; - /** * Object containing users */ @@ -12456,7 +10008,6 @@ export interface UploadChannelFileRequest { * file field */ file?: string; - user?: OnlyUserID; } @@ -12465,14 +10016,11 @@ export interface UploadChannelFileResponse { * Duration of the request in milliseconds */ duration: string; - /** * URL to the uploaded asset. Should be used to put to `asset_url` attachment field */ file?: string; - moderation_action?: string; - /** * URL of the file thumbnail for supported file formats. Should be put to `thumb_url` attachment field */ @@ -12481,12 +10029,10 @@ export interface UploadChannelFileResponse { export interface UploadChannelRequest { file?: string; - /** * field with JSON-encoded array of image size configurations */ upload_sizes?: Array; - user?: OnlyUserID; } @@ -12495,13 +10041,9 @@ export interface UploadChannelResponse { * Duration of the request in milliseconds */ duration: string; - file?: string; - moderation_action?: string; - thumb_url?: string; - /** * Array of image size configurations */ @@ -12510,19 +10052,12 @@ export interface UploadChannelResponse { export interface UpsertActionConfigItem { action: string; - entity_type: string; - order: number; - description?: string; - icon?: string; - id?: string; - queue_type?: string; - custom?: Record; } @@ -12531,37 +10066,30 @@ export interface UpsertActionConfigRequest { * The action to perform (e.g. ban, delete_message, custom) */ action: string; - /** * Type of entity this action applies to (e.g. stream:chat:v1:message) */ entity_type: string; - /** * Display order in the dashboard (0–100, lower numbers shown first) */ order: number; - /** * Human-readable label for the dashboard button */ description?: string; - /** * Icon identifier for the dashboard button */ icon?: string; - /** * UUID of an existing action config to update; omit to create a new record */ id?: string; - /** * Queue this config belongs to; null means the default queue */ queue_type?: string; - /** * Action-specific parameters passed to the action handler */ @@ -12570,7 +10098,6 @@ export interface UpsertActionConfigRequest { export interface UpsertActionConfigResponse { duration: string; - /** * Configuration for a moderation action */ @@ -12582,53 +10109,34 @@ export interface UpsertConfigRequest { * Unique identifier for the moderation configuration */ key: string; - /** * Whether moderation should be performed asynchronously */ async?: boolean; - /** * Team associated with the configuration */ team?: string; - ai_audio_config?: AIAudioConfigRequest; - ai_image_config?: AIImageConfig; - ai_text_config?: AITextConfig; - - ai_video_config?: AIVideoConfig; - + ai_video_config?: AIVideoConfigRequest; automod_platform_circumvention_config?: AutomodPlatformCircumventionConfig; - automod_semantic_filters_config?: AutomodSemanticFiltersConfig; - automod_toxicity_config?: AutomodToxicityConfig; - aws_rekognition_config?: AIImageConfig; - block_list_config?: BlockListConfig; - bodyguard_config?: AITextConfig; - flood_config?: FloodConfig; - google_vision_config?: GoogleVisionConfig; - llm_config?: LLMConfig; - rule_builder_config?: RuleBuilderConfig; - velocity_filter_config?: VelocityFilterConfig; - video_call_rule_config?: VideoCallRuleConfig; } export interface UpsertConfigResponse { duration: string; - config?: ConfigResponse; } @@ -12644,7 +10152,6 @@ export interface UpsertPushPreferencesResponse { * Duration of the request in milliseconds */ duration: string; - /** * The channel specific push notification preferences, only returned for channels you've edited. */ @@ -12652,7 +10159,6 @@ export interface UpsertPushPreferencesResponse { string, Record >; - /** * The user preferences, always returned regardless if you edited it */ @@ -12661,7 +10167,6 @@ export interface UpsertPushPreferencesResponse { export interface User { id: string; - data?: Record; } @@ -12669,77 +10174,64 @@ export interface UserBannedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - user: UserResponseCommonFields; - /** * The type of event: "user.banned" in this case */ type: string; - /** * The ID of the channel where the target user was banned */ channel_id?: string; - channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel where the target user was banned */ channel_type?: string; - /** * The CID of the channel where the target user was banned */ cid?: string; - /** * The expiration date of the ban */ - expiration?: number; - + expiration?: TimestampNS; /** * The reason for the ban */ reason?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * ID of the review queue item (flagged message) that triggered the ban, if the ban was applied from the moderation review queue */ review_queue_item_id?: string; - /** * Whether the user was shadow banned */ shadow?: boolean; - /** * The team of the channel where the target user was banned */ team?: string; - total_bans?: number; - channel_custom?: CustomChannelData; - created_by?: UserResponseCommonFields; } +export interface UserChannelCountRuleParameters { + threshold?: number; + time_window?: string; +} + export interface UserCreatedWithinParameters { max_age?: string; } export interface UserCustomPropertyParameters { operator?: string; - property_key?: string; } @@ -12747,19 +10239,14 @@ export interface UserDeactivatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - user: UserResponseCommonFields; - /** * The type of event: "user.deactivated" in this case */ type: string; - - received_at?: number; - + received_at?: TimestampNS; created_by?: UserResponseCommonFields; } @@ -12767,67 +10254,49 @@ export interface UserDeletedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The type of deletion that was used for the user's conversations. One of: hard, soft, pruning, (empty string) */ delete_conversation: string; - /** * Whether the user's conversation channels were deleted */ delete_conversation_channels: boolean; - /** * The type of deletion that was used for the user's messages. One of: hard, soft, pruning, (empty string) */ delete_messages: string; - /** * The type of deletion that was used for the user. One of: hard, soft, pruning, (empty string) */ delete_user: string; - /** * Whether the user was hard deleted */ hard_delete: boolean; - /** * Whether the user's messages were marked as deleted */ mark_messages_deleted: boolean; - custom: CustomEventData; - user: UserResponseCommonFields; - /** * The type of event: "user.deleted" in this case */ type: string; - - received_at?: number; + received_at?: TimestampNS; } export interface UserGroup { app_pk: number; - - created_at: number; - + created_at: TimestampNS; id: string; - name: string; - - updated_at: number; - + updated_at: TimestampNS; created_by?: string; - description?: string; - team_id?: string; - members?: Array; } @@ -12835,19 +10304,14 @@ export interface UserGroupCreatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "user_group.created" in this case */ type: string; - - received_at?: number; - + received_at?: TimestampNS; user?: UserResponseCommonFields; - user_group?: UserGroup; } @@ -12855,31 +10319,22 @@ export interface UserGroupDeletedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "user_group.deleted" in this case */ type: string; - - received_at?: number; - + received_at?: TimestampNS; user?: UserResponseCommonFields; - user_group?: UserGroup; } export interface UserGroupMember { app_pk: number; - - created_at: number; - + created_at: TimestampNS; group_id: string; - is_admin: boolean; - user_id: string; } @@ -12887,24 +10342,18 @@ export interface UserGroupMemberAddedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The user IDs that were added */ members: Array; - custom: CustomEventData; - /** * The type of event: "user_group.member_added" in this case */ type: string; - - received_at?: number; - + received_at?: TimestampNS; user?: UserResponseCommonFields; - user_group?: UserGroup; } @@ -12912,42 +10361,29 @@ export interface UserGroupMemberRemovedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The user IDs that were removed */ members: Array; - custom: CustomEventData; - /** * The type of event: "user_group.member_removed" in this case */ type: string; - - received_at?: number; - + received_at?: TimestampNS; user?: UserResponseCommonFields; - user_group?: UserGroup; } export interface UserGroupResponse { - created_at: number; - + created_at: TimestampNS; id: string; - name: string; - - updated_at: number; - + updated_at: TimestampNS; created_by?: string; - description?: string; - team_id?: string; - members?: Array; } @@ -12955,25 +10391,26 @@ export interface UserGroupUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - /** * The type of event: "user_group.updated" in this case */ type: string; - - received_at?: number; - + received_at?: TimestampNS; user?: UserResponseCommonFields; - user_group?: UserGroup; } export interface UserIdenticalContentCountParameters { threshold?: number; + time_window?: string; +} +export interface UserIdenticalImageCountParameters { + match?: string; + similarity_distance?: number; + threshold?: number; time_window?: string; } @@ -12981,63 +10418,47 @@ export interface UserMessagesDeletedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - user: UserResponseCommonFields; - /** * The type of event: "user.messages.deleted" in this case */ type: string; - /** * The ID of the channel where the target user's messages were deleted */ channel_id?: string; - channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel where the target user's messages were deleted */ channel_type?: string; - /** * The CID of the channel where the target user's messages were deleted */ cid?: string; - /** * Whether Messages were hard deleted */ hard_delete?: boolean; - - received_at?: number; - + received_at?: TimestampNS; /** * The team of the channel where the target user's messages were deleted */ team?: string; - channel_custom?: CustomChannelData; } export interface UserMuteResponse { - created_at: number; - - updated_at: number; - - expires?: number; - + created_at: TimestampNS; + updated_at: TimestampNS; + expires?: TimestampNS; /** * User response object */ target?: UserResponse; - /** * User response object */ @@ -13048,24 +10469,18 @@ export interface UserMutedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - user: UserResponseCommonFields; - /** * The type of event: "user.muted" in this case */ type: string; - - received_at?: number; - + received_at?: TimestampNS; /** * The target users that were muted */ target_users?: Array; - target_user?: UserResponseCommonFields; } @@ -13073,37 +10488,34 @@ export interface UserPresenceChangedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - user: UserResponseCommonFields; - /** * The type of event: "user.presence.changed" in this case */ type: string; + received_at?: TimestampNS; +} - received_at?: number; +export interface UserReactionCountRuleParameters { + count?: string; + threshold?: number; + time_window?: string; } export interface UserReactivatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - user: UserResponseCommonFields; - /** * The type of event: "user.reactivated" in this case */ type: string; - - received_at?: number; - + received_at?: TimestampNS; created_by?: UserResponseCommonFields; } @@ -13112,26 +10524,20 @@ export interface UserRequest { * User ID */ id: string; - /** * User's profile image URL */ image?: string; - invisible?: boolean; - language?: string; - /** * Optional name of user */ name?: string; - /** * Custom user data */ custom?: CustomUserData; - privacy_settings?: PrivacySettingsResponse; } @@ -13140,164 +10546,110 @@ export interface UserResponse { * Whether a user is banned or not */ banned: boolean; - /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * Unique user identifier */ id: string; - /** * Preferred language of a user */ language: string; - /** * Whether a user online or not */ online: boolean; - /** * Determines the set of user permissions */ role: string; - /** * Date/time of the last update */ - updated_at: number; - + updated_at: TimestampNS; blocked_user_ids: Array; - /** * List of teams user is a part of */ teams: Array; - /** * Custom data for this object */ custom: CustomUserData; - avg_response_time?: number; - /** * Date of deactivation */ - deactivated_at?: number; - + deactivated_at?: TimestampNS; /** * Date/time of deletion */ - deleted_at?: number; - + deleted_at?: TimestampNS; image?: string; - /** * Date of last activity */ - last_active?: number; - + last_active?: TimestampNS; /** * Optional name of user */ name?: string; - /** * Revocation date for tokens */ - revoke_tokens_issued_before?: number; - + revoke_tokens_issued_before?: TimestampNS; teams_role?: Record; } export interface UserResponseCommonFields { banned: boolean; - - created_at: number; - + created_at: TimestampNS; id: string; - language: string; - online: boolean; - role: string; - - updated_at: number; - + updated_at: TimestampNS; blocked_user_ids: Array; - teams: Array; - custom: CustomUserData; - avg_response_time?: number; - - deactivated_at?: number; - - deleted_at?: number; - + deactivated_at?: TimestampNS; + deleted_at?: TimestampNS; image?: string; - - last_active?: number; - + last_active?: TimestampNS; name?: string; - - revoke_tokens_issued_before?: number; - + revoke_tokens_issued_before?: TimestampNS; teams_role?: Record; } export interface UserResponsePrivacyFields { banned: boolean; - - created_at: number; - + created_at: TimestampNS; id: string; - language: string; - online: boolean; - role: string; - - updated_at: number; - + updated_at: TimestampNS; blocked_user_ids: Array; - teams: Array; - custom: CustomUserData; - avg_response_time?: number; - - deactivated_at?: number; - - deleted_at?: number; - + deactivated_at?: TimestampNS; + deleted_at?: TimestampNS; image?: string; - invisible?: boolean; - - last_active?: number; - + last_active?: TimestampNS; name?: string; - - revoke_tokens_issued_before?: number; - + revoke_tokens_issued_before?: TimestampNS; privacy_settings?: PrivacySettingsResponse; - teams_role?: Record; } export interface UserRoleParameters { operator?: string; - role?: string; } @@ -13309,50 +10661,37 @@ export interface UserUnbannedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - user: UserResponseCommonFields; - /** * The type of event: "user.unbanned" in this case */ type: string; - /** * The ID of the channel where the target user was unbanned */ channel_id?: string; - channel_member_count?: number; - channel_message_count?: number; - /** * The type of the channel where the target user was unbanned */ channel_type?: string; - /** * The CID of the channel where the target user was unbanned */ cid?: string; - - received_at?: number; - + received_at?: TimestampNS; /** * Whether the target user was shadow unbanned */ shadow?: boolean; - /** * The team of the channel where the target user was unbanned */ team?: string; - channel_custom?: CustomChannelData; - created_by?: UserResponseCommonFields; } @@ -13360,153 +10699,111 @@ export interface UserUpdatedEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; custom: CustomEventData; - user: UserResponsePrivacyFields; - /** * The type of event: "user.updated" in this case */ type: string; - - received_at?: number; + received_at?: TimestampNS; } export interface UserWatchingStartEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The number of users watching the channel */ watcher_count: number; - custom: CustomEventData; - user: UserResponseCommonFields; - /** * The type of event: "user.watching.start" in this case */ type: string; - /** * The ID of the channel which the user started watching */ channel_id?: string; - /** * The type of the channel which the user started watching */ channel_type?: string; - /** * The CID of the channel which the user started watching */ cid?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface UserWatchingStopEvent { /** * Date/time of creation */ - created_at: number; - + created_at: TimestampNS; /** * The number of users watching the channel */ watcher_count: number; - custom: CustomEventData; - user: UserResponseCommonFields; - /** * The type of event: "user.watching.stop" in this case */ type: string; - /** * The ID of the channel which the user stopped watching */ channel_id?: string; - /** * The type of the channel which the user stopped watching */ channel_type?: string; - /** * The CID of the channel which the user stopped watching */ cid?: string; - - received_at?: number; + received_at?: TimestampNS; } export interface VelocityFilterConfig { advanced_filters: boolean; - cascading_actions: boolean; - cids_per_user: number; - enabled: boolean; - first_message_only: boolean; - rules: Array; - async?: boolean; } export interface VelocityFilterConfigRule { action: 'flag' | 'shadow' | 'remove' | 'ban'; - ban_duration: number; - cascading_action: 'flag' | 'shadow' | 'remove' | 'ban'; - cascading_threshold: number; - check_message_context: boolean; - fast_spam_threshold: number; - fast_spam_ttl: number; - ip_ban: boolean; - probation_period: number; - shadow_ban: boolean; - slow_spam_threshold: number; - slow_spam_ttl: number; - url_only: boolean; - slow_spam_ban_duration?: number; } export interface VideoCallRuleConfig { flag_all_labels: boolean; - flagged_labels: Array; - rules: Array; } export interface VideoContentParameters { label_operator?: string; - harm_labels?: Array; } @@ -13516,15 +10813,12 @@ export interface VideoKickUserRequestPayload {} export interface VideoRuleParameters { threshold?: number; - time_window?: string; - harm_labels?: Array; } export interface VoteData { answer_text?: string; - option_id?: string; } @@ -13533,14 +10827,11 @@ export interface WSAuthMessage { * JWT token for authentication */ token: string; - user_details: ConnectUserDetailsRequest; - /** * Channel-member custom keys to project onto message.member for messages this connection receives (opt-in; capped, off by default). */ member_custom_include?: Array; - /** * List of products to subscribe to. One of: chat, video, feeds */ @@ -13712,16 +11003,10 @@ export interface WrappedUnreadCountsResponse { * Duration of the request in milliseconds */ duration: string; - total_unread_count: number; - total_unread_threads_count: number; - channel_type: Array; - channels: Array; - threads: Array; - total_unread_count_by_team?: Record; } diff --git a/src/gen/models/timestamp-guard.ts b/src/gen/models/timestamp-guard.ts new file mode 100644 index 0000000000..966c56aa26 --- /dev/null +++ b/src/gen/models/timestamp-guard.ts @@ -0,0 +1,27 @@ +import type { TimestampNS } from './index'; + +/** + * Makes `new Date(ns)` a deprecated constructor, and a compile error for a server-sent nanosecond timestamp. + * + * Any tsconfig whose `include` covers this directory picks this file up; `exclude` it to defer. + * + * The `0 extends 1 & T` guard excludes `any`. Without it this overload would capture + * every `new Date()` in the program -- `any` is assignable to the brand, and a + * merged declaration is tried before lib.es5.d.ts -- breaking each `JSON.parse` path + * and index-signature read on the next regeneration. + */ +declare global { + interface DateConstructor { + /** + * @deprecated Unix nanoseconds, not milliseconds. Use SDK's provided nsToDate(t), or nsToMs(t) for arithmetic. + */ + new ( + t: 0 extends 1 & T ? never : T, + ): { + readonly ERROR_use_SDKs_nsToDate_helper_instead: never; + }; + } +} + +// Keeps this a module, which `declare global` requires, independent of the import above. +export {}; diff --git a/src/gen/moderation/ModerationApi.ts b/src/gen/moderation/ModerationApi.ts index 67279d1756..9661cee550 100644 --- a/src/gen/moderation/ModerationApi.ts +++ b/src/gen/moderation/ModerationApi.ts @@ -187,6 +187,7 @@ export class ModerationApi { appeal_reason: request?.appeal_reason, entity_id: request?.entity_id, entity_type: request?.entity_type, + channel_cid: request?.channel_cid, review_queue_item_id: request?.review_queue_item_id, attachments: request?.attachments, }; diff --git a/src/index.ts b/src/index.ts index e6b9fb068d..084f10273d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -79,6 +79,11 @@ export type { export * from './thread_manager'; export * from './token_manager'; export * from './types'; +// Type-only and erased from the bundle, so there is no runtime import of an empty module. Declaration +// emit keeps it, which is what carries the `DateConstructor` augmentation (`new Date(timestampNs)` +// becomes a compile error) into the published types: a global augmentation only reaches a consumer if +// the entry point's type graph imports the file that declares it. +export type {} from './gen/models/timestamp-guard'; export * from './uploadManager'; export * from './offline-support'; export * from './LiveLocationManager'; @@ -107,6 +112,7 @@ export { } from './utils'; export { FixedSizeQueueCache } from './utils/FixedSizeQueueCache'; export { + asTimestampNS, convertTimestampToDate, dateToNs, msToNs, diff --git a/src/messageComposer/messageComposer.ts b/src/messageComposer/messageComposer.ts index 5f5749dd21..b806ef9634 100644 --- a/src/messageComposer/messageComposer.ts +++ b/src/messageComposer/messageComposer.ts @@ -36,6 +36,7 @@ import type { EventType, LocalMessage, MessageResponse, + TimestampNS, UserResponse, } from '../types'; import { chatLoggerSystem } from '../logger'; @@ -67,7 +68,10 @@ type UnregisterSubscriptions = Unsubscribe; * server-derived `created_at` / `updated_at` are directly comparable — which is the whole point of * this state. */ -export type LastComposerChange = { draftUpdate: number | null; stateUpdate: number }; +export type LastComposerChange = { + draftUpdate: TimestampNS | null; + stateUpdate: TimestampNS; +}; export type EditingAuditState = { lastChange: LastComposerChange; diff --git a/src/messageDelivery/MessageDeliveryReporter.ts b/src/messageDelivery/MessageDeliveryReporter.ts index 208006465a..61838bb52a 100644 --- a/src/messageDelivery/MessageDeliveryReporter.ts +++ b/src/messageDelivery/MessageDeliveryReporter.ts @@ -12,6 +12,7 @@ import type { MarkReadResponse, StreamAPIError, StreamResponse, + TimestampNS, } from '../types'; import { throttle } from '../utils'; import { isAPIError, isErrorRetryable } from '../errors'; @@ -233,8 +234,8 @@ export class MessageDeliveryReporter { let latestMessages: LocalMessage[] = []; // Wire timestamps (unix nanoseconds), directly comparable against a message's `created_at`. - let lastDeliveredAt: number | undefined; - let lastReadAt: number | undefined; + let lastDeliveredAt: TimestampNS | undefined; + let lastReadAt: TimestampNS | undefined; let key: string | undefined = undefined; // todo: unify the API for read state access btw channel and threads diff --git a/src/messageDelivery/MessageReceiptsTracker.ts b/src/messageDelivery/MessageReceiptsTracker.ts index bdd37e62cc..a76a95af9b 100644 --- a/src/messageDelivery/MessageReceiptsTracker.ts +++ b/src/messageDelivery/MessageReceiptsTracker.ts @@ -1,13 +1,14 @@ -import type { ReadStateResponse, UserResponse } from '../types'; +import type { ReadStateResponse, TimestampNS, UserResponse } from '../types'; import { StateStore } from '@stream-io/state-store'; import type { Channel } from '../channel'; +import { asTimestampNS } from '../utils/time'; import { WithSubscriptions } from '../utils/WithSubscriptions'; type UserId = string; type MessageId = string; -export type MsgRef = { timestamp: number; msgId: MessageId }; +export type MsgRef = { timestamp: TimestampNS; msgId: MessageId }; export type OwnMessageReceiptsTrackerMessageLocator = ( - timestamp: number, + timestamp: TimestampNS, ) => MsgRef | null; export type UserProgress = { user: UserResponse; @@ -44,23 +45,26 @@ export type ReadStoreReconcileMeta = { removedUserIds?: string[]; }; type ReadStoreUserState = { - last_read?: number; + last_read?: TimestampNS; unread_messages?: number; user?: UserResponse; first_unread_message_id?: string; last_read_message_id?: string; - last_delivered_at?: number; + last_delivered_at?: TimestampNS; last_delivered_message_id?: string; }; // ---------- ordering utilities ---------- -const MIN_REF: MsgRef = { timestamp: Number.NEGATIVE_INFINITY, msgId: '' } as const; +const MIN_REF: MsgRef = { + timestamp: asTimestampNS(Number.NEGATIVE_INFINITY), + msgId: '', +} as const; const isValidReadState = ( readState: ReadStoreUserState | undefined, ): readState is ReadStoreUserState & { - last_read: number; + last_read: TimestampNS; user: UserResponse; } => !!readState?.user && Number.isFinite(readState.last_read); @@ -204,7 +208,7 @@ export class MessageReceiptsTracker extends WithSubscriptions { this.channel = channel; this.locateMessage = locateMessage ?? - ((timestamp: number) => { + ((timestamp: TimestampNS) => { const message = this.channel.messagePaginator.findItemByTimestamp(timestamp); return message ? { timestamp, msgId: message.id } : null; }); @@ -331,7 +335,7 @@ export class MessageReceiptsTracker extends WithSubscriptions { }: { user: UserResponse; /** Unix nanoseconds, as the API sends it. */ - deliveredAt: number; + deliveredAt: TimestampNS; lastDeliveredMessageId?: string; }) { const timestamp = deliveredAt; @@ -367,7 +371,7 @@ export class MessageReceiptsTracker extends WithSubscriptions { }: { user: UserResponse; /** Unix nanoseconds, as the API sends it. */ - readAt: number; + readAt: TimestampNS; lastReadMessageId?: string; }) { const timestamp = readAt; @@ -417,7 +421,7 @@ export class MessageReceiptsTracker extends WithSubscriptions { }: { user: UserResponse; /** Unix nanoseconds, as the API sends it. */ - lastReadAt?: number; + lastReadAt?: TimestampNS; lastReadMessageId?: string; }) { const userProgress = this.ensureUser(user); @@ -661,12 +665,12 @@ export class MessageReceiptsTracker extends WithSubscriptions { } private readStateToUserProgress(readState: { - last_read: number; + last_read: TimestampNS; unread_messages?: number; user: UserResponse; first_unread_message_id?: string; last_read_message_id?: string; - last_delivered_at?: number; + last_delivered_at?: TimestampNS; last_delivered_message_id?: string; }): UserProgress { const lastReadTimestamp = readState.last_read; diff --git a/src/offline-support/offline_support_api.ts b/src/offline-support/offline_support_api.ts index b93c8612f8..e970b48615 100644 --- a/src/offline-support/offline_support_api.ts +++ b/src/offline-support/offline_support_api.ts @@ -985,13 +985,22 @@ export abstract class AbstractOfflineDB implements OfflineDBApi { execute?: boolean; }) => { const { - received_at: last_read = nowNs(), last_read_message_id, // @ts-expect-error property missing unread_messages = 0, user, cid, } = event; + // Mirror what `Channel` writes into `state.read` for the same event, so a cold start hydrated from + // the DB agrees with the live state: a mark-unread moves the cursor back to the server's + // `last_read_at`, every other read event reads up to the server's `created_at`. `received_at` is + // the local clock at receipt — skewed against server time, and for a mark-unread plain wrong, + // since "now" is after the messages the user just marked unread — so it is only a fallback. + const last_read = + (event.type === 'notification.mark_unread' ? event.last_read_at : undefined) ?? + event.created_at ?? + event.received_at ?? + nowNs(); const overriddenUnreadMessages = unreadMessages ?? unread_messages; diff --git a/src/offline-support/types.ts b/src/offline-support/types.ts index f314f14da4..04fd474984 100644 --- a/src/offline-support/types.ts +++ b/src/offline-support/types.ts @@ -15,6 +15,7 @@ import type { ReactionResponse, ReadStateResponse, SortParamRequest, + TimestampNS, } from '../types'; import type { Channel } from '../channel'; import type { StreamChat } from '../client'; @@ -321,7 +322,7 @@ export type DBDeleteMessagesForChannelType = { cid: string; /** Timestamp before which messages are deleted. */ /** Unix nanoseconds, as the API sends it. */ - truncated_at?: number; + truncated_at?: TimestampNS; /** Whether to immediately execute the operation. */ execute?: boolean; }; diff --git a/src/pagination/cursorDerivation/createdAtAroundPaginationFlags.ts b/src/pagination/cursorDerivation/createdAtAroundPaginationFlags.ts index 26991a8881..ca6e7568b4 100644 --- a/src/pagination/cursorDerivation/createdAtAroundPaginationFlags.ts +++ b/src/pagination/cursorDerivation/createdAtAroundPaginationFlags.ts @@ -1,10 +1,11 @@ import { binarySearch } from '../sortCompiler'; import { dateToNs } from '../../utils/time'; +import type { TimestampNS } from '../../types'; import type { BasePaginator, CursorDeriveContext, PaginationFlags } from '../paginators'; import { ComparisonResult } from '../types.normalization'; export const deriveCreatedAtAroundPaginationFlags = < - T extends { id: string; created_at: number }, + T extends { id: string; created_at: TimestampNS }, Q extends { created_at_around?: Date | string }, P extends BasePaginator, >({ diff --git a/src/pagination/cursorDerivation/linearPaginationFlags.ts b/src/pagination/cursorDerivation/linearPaginationFlags.ts index 09302e56d8..4981dd67f0 100644 --- a/src/pagination/cursorDerivation/linearPaginationFlags.ts +++ b/src/pagination/cursorDerivation/linearPaginationFlags.ts @@ -1,5 +1,5 @@ import type { CursorDeriveContext, PaginationFlags } from '../paginators'; -import type { MessagePaginationParams } from '../../types'; +import type { MessagePaginationParams, TimestampNS } from '../../types'; /** * The query keys this helper understands when deriving a cursor direction. Not a request @@ -34,7 +34,7 @@ const HEADWARD_QUERY_PROPERTIES: Array = [ 'id_gte', ]; export const deriveLinearPaginationFlags = < - T extends { id: string; created_at: number }, + T extends { id: string; created_at: TimestampNS }, Q extends LinearPaginationQueryShape, >({ direction, diff --git a/src/pagination/paginators/MessageIntervalPaginator.ts b/src/pagination/paginators/MessageIntervalPaginator.ts index 389aa17d2c..9bfb0d567a 100644 --- a/src/pagination/paginators/MessageIntervalPaginator.ts +++ b/src/pagination/paginators/MessageIntervalPaginator.ts @@ -22,6 +22,7 @@ import type { MessagePaginationParams, PinnedMessagePaginationOptions, SortParamRequest, + TimestampNS, UserResponse, } from '../../types'; import type { Channel } from '../../channel'; @@ -118,7 +119,7 @@ const dataFieldFilterResolver: FieldToDataResolver = { */ export const getMessageCreatedAtTimestamp = ( message: LocalMessage | null | undefined, -): number | null => { +): TimestampNS | null => { const timestamp = message?.created_at; return typeof timestamp === 'number' && Number.isFinite(timestamp) ? timestamp : null; }; @@ -1133,7 +1134,7 @@ export class MessageIntervalPaginator extends BasePaginator< lastReadAt, messages, }: { - lastReadAt: number; + lastReadAt: TimestampNS; messages: LocalMessage[]; }): { firstUnreadMessageId: string | null; lastReadMessageId: string | null } => { // Messages are expected in chronological order. We find: @@ -1271,7 +1272,7 @@ export class MessageIntervalPaginator extends BasePaginator< * `isTail`/`hasMoreTail` are set; intervals entirely newer keep their flags (unloaded older * messages may still sit between them and the cutoff). The active window is re-emitted once. */ - truncate = ({ truncatedAt }: { truncatedAt: number }) => { + truncate = ({ truncatedAt }: { truncatedAt: TimestampNS }) => { const cutoff = truncatedAt; if (!Number.isFinite(cutoff)) return; @@ -1348,7 +1349,7 @@ export class MessageIntervalPaginator extends BasePaginator< }: { userId: string; hardDelete?: boolean; - deletedAt: number; + deletedAt: TimestampNS; }) => { const loadedMessages = this.items ?? []; @@ -1453,7 +1454,7 @@ export class MessageIntervalPaginator extends BasePaginator< * read cursors live — which is already sorted, so this is O(log n) with no re-sort. */ findItemByTimestamp = ( - timestamp: number, + timestamp: TimestampNS, exactTsMatch = false, ): LocalMessage | null => { const items = this.headItems; // ascending by created_at diff --git a/src/pagination/paginators/MessagePaginator.ts b/src/pagination/paginators/MessagePaginator.ts index 24f0f098f2..6ffdd4cf64 100644 --- a/src/pagination/paginators/MessagePaginator.ts +++ b/src/pagination/paginators/MessagePaginator.ts @@ -11,8 +11,8 @@ import { MessageIntervalPaginator, type MessageQueryShape, } from './MessageIntervalPaginator'; -import type { LocalMessage } from '../../types'; -import { nsToDate } from '../../utils/time'; +import type { LocalMessage, TimestampNS } from '../../types'; +import { asTimestampNS, nsToDate } from '../../utils/time'; import { StateStore } from '@stream-io/state-store'; export type { @@ -58,7 +58,7 @@ export type MessagePaginatorAggregateState = { * sort key {@link MessagePaginator.lastMessageAt} is derived as the max of the two, so the two can * never drift out of sync. */ - seededLastMessageAt: number | null; + seededLastMessageAt: TimestampNS | null; }; export type MessagePaginatorOptions = BaseMessagePaginatorOptions & { @@ -73,7 +73,7 @@ export type MessagePaginatorOptions = BaseMessagePaginatorOptions & { }; export type UnreadSnapshotState = { - lastReadAt: number | null; + lastReadAt: TimestampNS | null; unreadCount: number; /** * Snapshot of the first unread message id for the user. @@ -167,11 +167,11 @@ export class MessagePaginator extends MessageIntervalPaginator { * **Derived** (never stored) so it cannot drift from {@link lastMessage}. `null` until seeded or a * message is ingested. */ - get lastMessageAt(): number | null { + get lastMessageAt(): TimestampNS | null { const { lastMessage, seededLastMessageAt } = this.aggregateState.getLatestValue(); const fromMessage = getMessageCreatedAtTimestamp(lastMessage); if (fromMessage !== null && seededLastMessageAt !== null) { - return Math.max(fromMessage, seededLastMessageAt); + return asTimestampNS(Math.max(fromMessage, seededLastMessageAt)); } return fromMessage ?? seededLastMessageAt; } @@ -244,7 +244,7 @@ export class MessagePaginator extends MessageIntervalPaginator { * authoritative whole-channel aggregate. Monotonic: a no-op when the paginator already advanced * past it (e.g. from ingested messages), so seed order does not matter. */ - seedLastMessageAt(value: number | null | undefined) { + seedLastMessageAt(value: TimestampNS | null | undefined) { if (value == null || !Number.isFinite(value)) return; const current = this.aggregateState.getLatestValue().seededLastMessageAt; if (current !== null && value <= current) return; diff --git a/src/poll.ts b/src/poll.ts index b50d18d81b..036dd1297c 100644 --- a/src/poll.ts +++ b/src/poll.ts @@ -16,7 +16,11 @@ import type { UpdatePollRequest, VotingVisibility, } from './types'; -import type { PollResponseData as Gen_PollResponseData, WSEvent } from './gen/models'; +import type { + PollResponseData as Gen_PollResponseData, + TimestampNS, + WSEvent, +} from './gen/models'; const isPollUpdatedEvent = (e: WSEvent): e is EventPayload<'poll.updated'> => e.type === 'poll.updated'; @@ -50,7 +54,7 @@ type OptionId = string; export type PollState = Omit & { /** Unix nanoseconds, matching every API timestamp. */ - lastActivityAt: number; // todo: would be ideal to get this from the BE + lastActivityAt: TimestampNS; // todo: would be ideal to get this from the BE maxVotedOptionIds: OptionId[]; ownVotesByOptionId: Record; ownAnswer?: PollVoteResponseData; // each user can have only one answer diff --git a/src/reminders/Reminder.ts b/src/reminders/Reminder.ts index d8ed1f7225..ad8635897b 100644 --- a/src/reminders/Reminder.ts +++ b/src/reminders/Reminder.ts @@ -2,7 +2,12 @@ import { ReminderTimer } from './ReminderTimer'; import { StateStore } from '@stream-io/state-store'; import { nowNs, nsToMs } from '../utils/time'; import type { ReminderTimerConfig } from './ReminderTimer'; -import type { MessageResponse, ReminderResponseData, UserResponse } from '../types'; +import type { + MessageResponse, + ReminderResponseData, + TimestampNS, + UserResponse, +} from '../types'; /** * Milliseconds until `remindAt`, negative once it has passed. @@ -10,22 +15,22 @@ import type { MessageResponse, ReminderResponseData, UserResponse } from '../typ * @param remindAt - Unix nanoseconds, as the API sends it. The subtraction happens in the wire unit * and is converted once, so the returned duration stays in the milliseconds `setTimeout` speaks. */ -export const timeLeftMs = (remindAt: number) => nsToMs(remindAt - nowNs()); +export const timeLeftMs = (remindAt: TimestampNS) => nsToMs(remindAt - nowNs()); export type ReminderResponseBaseOrResponse = ReminderResponseData; export type ReminderState = { channel_cid: string; /** Unix nanoseconds, as the API sends it. */ - created_at: number; + created_at: TimestampNS; message: MessageResponse | null; message_id: string; /** Unix nanoseconds, as the API sends it. */ - remind_at: number | null; + remind_at: TimestampNS | null; /** A duration, so milliseconds — see {@link timeLeftMs}. */ timeLeftMs: number | null; /** Unix nanoseconds, as the API sends it. */ - updated_at: number; + updated_at: TimestampNS; user: UserResponse | null; user_id: string; }; diff --git a/src/thread.ts b/src/thread.ts index 171f977cdf..9534f74785 100644 --- a/src/thread.ts +++ b/src/thread.ts @@ -11,6 +11,7 @@ import type { SortParamRequest, StreamResponse, ThreadStateResponse, + TimestampNS, UserResponse, } from './types'; import { isDoesNotExistError } from './errors'; @@ -43,10 +44,10 @@ export type ThreadState = { active: boolean; channel: Channel; /** Unix nanoseconds, as the API sends it. */ - createdAt: number; + createdAt: TimestampNS; custom: CustomThreadData; /** Unix nanoseconds, as the API sends it. */ - deletedAt: number | null; + deletedAt: TimestampNS | null; isLoading: boolean; isStateStale: boolean; /** @@ -59,12 +60,12 @@ export type ThreadState = { replyCount: number; title: string; /** Unix nanoseconds, as the API sends it. */ - updatedAt: number | null; + updatedAt: TimestampNS | null; }; export type ThreadUserReadState = { /** Unix nanoseconds, as the API sends it. */ - lastReadAt: number; + lastReadAt: TimestampNS; unreadMessageCount: number; user: UserResponse; lastReadMessageId?: string; @@ -73,7 +74,10 @@ export type ThreadUserReadState = { export type ThreadReadState = Record; -const timestampOr = (value: number | undefined, fallback: number): number => +const timestampOr = ( + value: TimestampNS | undefined, + fallback: TimestampNS, +): TimestampNS => typeof value === 'number' && Number.isFinite(value) ? value : fallback; const DEFAULT_PAGE_LIMIT = 50; diff --git a/src/types.ts b/src/types.ts index 776e3f0b46..30eba342af 100644 --- a/src/types.ts +++ b/src/types.ts @@ -29,6 +29,7 @@ import type { SendMessageRequest, SendMessageResponse, SharedLocationResponseData, + TimestampNS, TranslateMessageRequest, UpdateChannelRequest, UpdateMessageRequest, @@ -224,13 +225,13 @@ type LocalEvent = ( | ({ type: 'message.read_locally' } & { channel_type: string; cid: string; - created_at: number; + created_at: TimestampNS; channel_id?: string; last_read_message_id?: string; team?: string; user?: UserResponse; }) -) & { received_at?: number }; +) & { received_at?: TimestampNS }; /** * The hello event of the `/api/v2/connect` WebSocket endpoint, sent once the auth frame @@ -244,10 +245,10 @@ export type ConnectedEvent = { type: 'connection.ok'; connection_id: string; /** Unix nanoseconds, as every other wire event carries it. */ - created_at: number; + created_at: TimestampNS; me: OwnUserResponse; /** Unix nanoseconds, as every other wire event carries it. */ - received_at?: number; + received_at?: TimestampNS; }; export type Event = WSEvent | ConnectedEvent | LocalEvent | keyof CustomEventTypes; diff --git a/src/utils/time.ts b/src/utils/time.ts index fe153f5c7c..f422f69518 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -25,14 +25,32 @@ * has round-tripped through JSON is not guaranteed. */ +import type { TimestampNS } from '../gen/models'; + /** Nanoseconds per millisecond — the only magic number in this module. */ export const NS_PER_MS = 1e6; -/** A wire timestamp as epoch milliseconds, for arithmetic against `Date.now()` or a `Date`. */ +/** + * Brands a raw number as a wire timestamp, without converting it. + * + * The one sanctioned way to mint a {@link TimestampNS} from a value that is already in + * nanoseconds but has lost its brand on the way: a row read back from a local database, a value + * parsed out of persisted JSON, a test fixture, or the epoch seed `asTimestampNS(0)`. It does no + * checking — a millisecond value passed here stays a millisecond value, now mislabelled. When the + * source is milliseconds or a `Date`, use {@link msToNs} or {@link dateToNs} instead. + */ +export const asTimestampNS = (ns: number): TimestampNS => ns as TimestampNS; + +/** + * A wire timestamp as epoch milliseconds, for arithmetic against `Date.now()` or a `Date`. + * + * Takes a plain `number` rather than a {@link TimestampNS} because it converts durations too: the + * difference of two timestamps is a nanosecond span, not an instant, and has no brand. + */ export const nsToMs = (ns: number): number => Math.floor(ns / NS_PER_MS); /** Epoch milliseconds as a wire timestamp. */ -export const msToNs = (ms: number): number => ms * NS_PER_MS; +export const msToNs = (ms: number): TimestampNS => asTimestampNS(ms * NS_PER_MS); /** * The local clock as a wire-comparable timestamp, for optimistic writes into API-shaped objects. @@ -41,23 +59,23 @@ export const msToNs = (ms: number): number => ms * NS_PER_MS; * Callers that order by timestamp must tie-break on something else — `findIndexInSortedArray` * does so via `selectKey`, and `MessageReceiptsTracker` compares message ids. */ -export const nowNs = (): number => msToNs(Date.now()); +export const nowNs = (): TimestampNS => msToNs(Date.now()); /** A wire timestamp as a `Date`, for request payloads and date libraries. */ -export const nsToDate = (ns: number): Date => new Date(nsToMs(ns)); +export const nsToDate = (ns: TimestampNS): Date => new Date(nsToMs(ns)); /** * A wire timestamp as a nanosecond-precision RFC3339 string, for an outgoing request date field. * {@link nsToDate} is lossy here — `Date` holds only milliseconds. */ -export const nsToRfc3339 = (ns: number): string => { +export const nsToRfc3339 = (ns: TimestampNS): string => { const ms = nsToMs(ns); const subMs = String(ns - ms * NS_PER_MS).padStart(6, '0'); return new Date(ms).toISOString().replace(/\.(\d{3})Z$/, `.$1${subMs}Z`); }; /** A `Date` as a wire timestamp. */ -export const dateToNs = (date: Date): number => msToNs(date.getTime()); +export const dateToNs = (date: Date): TimestampNS => msToNs(date.getTime()); /** * A server-sent timestamp as a `Date`, or `undefined` when there is none. @@ -78,7 +96,9 @@ export const dateToNs = (date: Date): number => msToNs(date.getTime()); * Returning `undefined` rather than throwing lets a caller fall back to whatever it already does * for a missing timestamp, which is usually to render nothing. */ -export const convertTimestampToDate = (timestamp?: number | null): Date | undefined => { +export const convertTimestampToDate = ( + timestamp?: TimestampNS | null, +): Date | undefined => { if (timestamp == null || !Number.isFinite(timestamp)) return undefined; return nsToDate(timestamp); }; diff --git a/test/types/timestamps.ts b/test/types/timestamps.ts new file mode 100644 index 0000000000..6ad7a6dc4a --- /dev/null +++ b/test/types/timestamps.ts @@ -0,0 +1,76 @@ +/** + * Compile-time contract for server-sent timestamps (`TimestampNS`). Type-checked, never executed. + * + * Every `@ts-expect-error` is an assertion: when the line below it stops being an error, tsc fails on + * the unused directive. The plain lines assert the opposite — that the sanctioned paths still compile. + * + * `stream-chat` is imported by its own package name, so the same file is checked twice: + * - `tsconfig.types-test.json` maps it to `src/` (`yarn types`). The `DateConstructor` guard only + * enters that program through `src/index.ts`'s re-export, so this also pins that line. + * - `tsconfig.types-test.dist.json` resolves it through `package.json` `exports`, i.e. the built + * `dist/types` a consumer installs (`yarn build`). That is what proves the guard actually ships. + */ +import type { MessageResponse, StreamChat, TimestampNS } from 'stream-chat'; +import { + asTimestampNS, + convertTimestampToDate, + dateToNs, + msToNs, + nowNs, + nsToDate, + nsToMs, +} from 'stream-chat'; + +declare const message: MessageResponse; +declare const client: StreamChat; +declare const untyped: any; + +// --- The `new Date` guard ------------------------------------------------------------------------ + +// @ts-expect-error -- a nanosecond value is out of `Date`'s range +export const assignedToDate: Date = new Date(message.created_at); +// @ts-expect-error -- the error surfaces at the first use of the result +new Date(message.created_at).toISOString(); + +export const fromMilliseconds: Date = new Date(Date.now()); +export const fromConverted: Date = new Date(nsToMs(message.created_at)); +// `any` must fall through to the lib overload, or every `JSON.parse` path would break. +export const fromAny: Date = new Date(untyped); + +// --- Helpers take and return the brand ----------------------------------------------------------- + +// @ts-expect-error -- epoch milliseconds are not a wire timestamp +nsToDate(Date.now()); +// @ts-expect-error -- epoch milliseconds are not a wire timestamp +convertTimestampToDate(Date.now()); + +export const converted: Date = nsToDate(message.created_at); +export const guarded: Date | undefined = convertTimestampToDate(message.pinned_at); +// `nsToMs` also converts durations, which carry no brand. +export const durationMs: number = nsToMs(message.updated_at - message.created_at); + +// --- Minting --------------------------------------------------------------------------------------- + +// @ts-expect-error -- a plain number is not assignable to a timestamp field +export const unbranded: MessageResponse['created_at'] = Date.now(); + +export const minted: TimestampNS[] = [ + nowNs(), + msToNs(Date.now()), + dateToNs(new Date()), + asTimestampNS(0), +]; + +// --- `pinMessage`: a `number` is a relative offset in seconds ---------------------------------------- + +// @ts-expect-error -- a server timestamp would be read as ~1.79e18 seconds from now +client.pinMessage(message.id, message.pin_expires); +// @ts-expect-error -- same for `pinnedAt` +client.pinMessage(message.id, null, message.pinned_at); + +client.pinMessage(message.id); +client.pinMessage(message, null); +client.pinMessage(message.id, 60); +client.pinMessage(message.id, null, -60); +client.pinMessage(message.id, nsToDate(message.created_at), '2026-01-01T00:00:00Z'); +client.pinMessage(message.id, untyped, untyped); diff --git a/test/unit/MessageComposer/middleware/messageComposer/attachments.test.ts b/test/unit/MessageComposer/middleware/messageComposer/attachments.test.ts index c0ba2f9aaa..9be2e7fbea 100644 --- a/test/unit/MessageComposer/middleware/messageComposer/attachments.test.ts +++ b/test/unit/MessageComposer/middleware/messageComposer/attachments.test.ts @@ -572,7 +572,7 @@ describe('createAttachmentsCompositionMiddleware with pendingUploadsEnabled', () message: { id: 'test-id', parent_id: undefined, type: 'regular' }, localMessage: { attachments: [], - created_at: new Date(), + created_at: convertDateToTimestamp(new Date()), deleted_at: null, error: undefined, id: 'test-id', @@ -583,7 +583,7 @@ describe('createAttachmentsCompositionMiddleware with pendingUploadsEnabled', () status: 'sending', text: '', type: 'regular', - updated_at: new Date(), + updated_at: convertDateToTimestamp(new Date()), }, sendOptions: {}, }); diff --git a/test/unit/api-client.test.ts b/test/unit/api-client.test.ts index efbcebe828..902bc5ce36 100644 --- a/test/unit/api-client.test.ts +++ b/test/unit/api-client.test.ts @@ -127,6 +127,38 @@ describe('ApiClient request options', () => { }); }); +describe('ApiClient rate limit metadata', () => { + const sendWithHeaders = async (headers: Record) => { + const client = getClientWithUser(); + vi.spyOn(client.axiosInstance, 'request').mockResolvedValue({ + data: {}, + status: 200, + headers, + }); + const { metadata } = await client.api.sendRequest('GET', '/api/v2/chat/channels'); + return metadata.rate_limit; + }; + + it('reads x-ratelimit-reset as unix seconds', async () => { + const rateLimit = await sendWithHeaders({ + 'x-ratelimit-limit': '60', + 'x-ratelimit-remaining': '59', + 'x-ratelimit-reset': '1700000000', + }); + + expect(rateLimit?.rate_limit).toBe(60); + expect(rateLimit?.rate_limit_remaining).toBe(59); + expect(rateLimit?.rate_limit_reset?.toISOString()).toBe('2023-11-14T22:13:20.000Z'); + }); + + it('leaves the reset unset rather than producing an Invalid Date', async () => { + expect( + (await sendWithHeaders({ 'x-ratelimit-reset': 'garbage' }))?.rate_limit_reset, + ).toBe(undefined); + expect((await sendWithHeaders({}))?.rate_limit_reset).toBe(undefined); + }); +}); + describe('ApiClient header precedence', () => { let client: StreamChat; let requestSpy: ReturnType; diff --git a/test/unit/channel.test.js b/test/unit/channel.test.js index 106f5ccf09..efadea07fe 100644 --- a/test/unit/channel.test.js +++ b/test/unit/channel.test.js @@ -2504,7 +2504,7 @@ describe('Channel _handleChannelEvent', function () { role: 'admin', created_at: convertDateToTimestamp('2022-03-08T09:46:56.840739Z'), updated_at: convertDateToTimestamp('2022-03-15T08:30:09.796926Z'), - last_active: '2023-05-24T09:20:31.041292724Z', + last_active: convertDateToTimestamp('2023-05-24T09:20:31.041292724Z'), banned: false, online: true, }, @@ -2532,7 +2532,7 @@ describe('Channel _handleChannelEvent', function () { role: 'admin', created_at: convertDateToTimestamp('2022-03-08T09:46:56.840739Z'), updated_at: convertDateToTimestamp('2022-03-15T08:30:09.796926Z'), - last_active: '2023-05-24T09:20:31.041292724Z', + last_active: convertDateToTimestamp('2023-05-24T09:20:31.041292724Z'), banned: false, online: true, }, diff --git a/test/unit/offline-support/offline_support_api.test.ts b/test/unit/offline-support/offline_support_api.test.ts index a1bcfce10d..da3f2fb9f8 100644 --- a/test/unit/offline-support/offline_support_api.test.ts +++ b/test/unit/offline-support/offline_support_api.test.ts @@ -1209,12 +1209,15 @@ describe('OfflineSupportApi', () => { beforeEach(() => { readEvent = { ...baseEvent, + type: 'message.read', cid: 'channel-123', + created_at: convertDateToTimestamp('2023-01-01T00:00:00Z'), user: { id: 'user-1', name: 'Alice' }, last_read_message_id: 'msg-987', - received_at: '2023-01-01T00:00:00Z', + // The local receipt time, deliberately later than `created_at`: it must not be persisted. + received_at: convertDateToTimestamp('2023-01-01T00:00:05Z'), unread_messages: 3, - }; + } as Event; offlineDb.upsertReads.mockResolvedValue([['UPSERT read']]); }); @@ -1233,7 +1236,7 @@ describe('OfflineSupportApi', () => { execute: true, reads: [ { - last_read: readEvent.received_at, + last_read: readEvent.created_at, last_read_message_id: readEvent.last_read_message_id, unread_messages: 5, user: readEvent.user, @@ -1258,7 +1261,7 @@ describe('OfflineSupportApi', () => { execute: true, reads: [ { - last_read: readEvent.received_at, + last_read: readEvent.created_at, last_read_message_id: readEvent.last_read_message_id, unread_messages: readEvent.unread_messages, user: readEvent.user, @@ -1284,7 +1287,7 @@ describe('OfflineSupportApi', () => { execute: false, reads: [ { - last_read: readEvent.received_at, + last_read: readEvent.created_at, last_read_message_id: readEvent.last_read_message_id, unread_messages: readEvent.unread_messages, user: readEvent.user, @@ -1299,6 +1302,38 @@ describe('OfflineSupportApi', () => { expect(result).toEqual([['UPSERT read']]); }); + it('persists the server `last_read_at` for a mark-unread, not the receipt time', async () => { + const lastReadAt = convertDateToTimestamp('2022-12-31T23:00:00Z'); + const markUnreadEvent = { + ...readEvent, + type: 'notification.mark_unread', + last_read_at: lastReadAt, + } as Event; + + await offlineDb.handleRead({ event: markUnreadEvent }); + + expect(offlineDb.upsertReads).toHaveBeenCalledWith( + expect.objectContaining({ + reads: [expect.objectContaining({ last_read: lastReadAt })], + }), + ); + }); + + it('falls back to `received_at` when the event carries no server timestamp', async () => { + const { created_at: _createdAt, ...eventWithoutCreatedAt } = + readEvent as Event & { + created_at?: number; + }; + + await offlineDb.handleRead({ event: eventWithoutCreatedAt as Event }); + + expect(offlineDb.upsertReads).toHaveBeenCalledWith( + expect.objectContaining({ + reads: [expect.objectContaining({ last_read: readEvent.received_at })], + }), + ); + }); + it('returns empty array if user is missing', async () => { const result = await offlineDb.handleRead({ event: { ...readEvent, user: undefined }, @@ -2617,12 +2652,12 @@ describe('OfflineSupportApi', () => { const editedMessage = { id: 'msg-123', cid: 'messaging:channel123', - created_at: new Date(), + created_at: convertDateToTimestamp(new Date()), pinned_at: null, status: 'failed', text: 'A edited', type: 'regular', - updated_at: new Date(), + updated_at: convertDateToTimestamp(new Date()), } as unknown as LocalMessage; vi.spyOn(offlineDb, 'getPendingTasks').mockResolvedValue([ { diff --git a/test/unit/poll.test.js b/test/unit/poll.test.js index 3978e337a7..b072351c55 100644 --- a/test/unit/poll.test.js +++ b/test/unit/poll.test.js @@ -11,7 +11,7 @@ const user1 = { role: 'admin', created_at: convertDateToTimestamp('2022-03-08T09:46:56.840739Z'), updated_at: convertDateToTimestamp('2024-09-13T13:53:32.883409Z'), - last_active: '2024-10-23T08:14:23.299448386Z', + last_active: convertDateToTimestamp('2024-10-23T08:14:23.299448386Z'), banned: false, online: true, mutes: null, @@ -44,7 +44,7 @@ const user2 = { role: 'user', created_at: convertDateToTimestamp('2022-01-27T08:28:28.412254Z'), updated_at: convertDateToTimestamp('2024-09-26T10:12:23.427141Z'), - last_active: '2024-10-23T08:01:43.157632831Z', + last_active: convertDateToTimestamp('2024-10-23T08:01:43.157632831Z'), banned: false, online: true, nickname: 'Ann', diff --git a/test/unit/poll_manager.test.ts b/test/unit/poll_manager.test.ts index acb9878a7c..fcee21caf9 100644 --- a/test/unit/poll_manager.test.ts +++ b/test/unit/poll_manager.test.ts @@ -33,7 +33,7 @@ const generatePollMessage = ( role: 'admin', created_at: convertDateToTimestamp('2022-03-08T09:46:56.840739Z'), updated_at: convertDateToTimestamp('2024-09-13T13:53:32.883409Z'), - last_active: '2024-10-23T08:14:23.299448386Z', + last_active: convertDateToTimestamp('2024-10-23T08:14:23.299448386Z'), banned: false, online: true, mutes: null, @@ -66,7 +66,7 @@ const generatePollMessage = ( role: 'user', created_at: convertDateToTimestamp('2022-01-27T08:28:28.412254Z'), updated_at: convertDateToTimestamp('2024-09-26T10:12:23.427141Z'), - last_active: '2024-10-23T08:01:43.157632831Z', + last_active: convertDateToTimestamp('2024-10-23T08:01:43.157632831Z'), banned: false, online: true, nickname: 'Ann', diff --git a/test/unit/threads.test.ts b/test/unit/threads.test.ts index b621089fa6..5db0807cfa 100644 --- a/test/unit/threads.test.ts +++ b/test/unit/threads.test.ts @@ -2300,13 +2300,13 @@ describe('Threads 2.0', () => { const parentMessageId = uuidv4(); client.dispatchEvent({ - received_at: new Date().toISOString(), + received_at: nowNs(), type: 'notification.thread_message_new', message: generateMsg({ parent_id: parentMessageId }) as MessageResponse, }); client.dispatchEvent({ - received_at: new Date().toISOString(), + received_at: nowNs(), type: 'notification.thread_message_new', message: generateMsg({ parent_id: parentMessageId }) as MessageResponse, }); @@ -2328,7 +2328,7 @@ describe('Threads 2.0', () => { expect(stateBefore.unseenThreadIds).to.be.empty; client.dispatchEvent({ - received_at: new Date().toISOString(), + received_at: nowNs(), type: 'notification.thread_message_new', message: generateMsg({ parent_id: thread.id }) as MessageResponse, }); diff --git a/test/unit/utils/time.test.ts b/test/unit/utils/time.test.ts index 017541e7b4..7f8e3adf38 100644 --- a/test/unit/utils/time.test.ts +++ b/test/unit/utils/time.test.ts @@ -1,10 +1,26 @@ import { describe, expect, it } from 'vitest'; -import { msToNs, nsToDate, nsToMs, nsToRfc3339 } from '../../../src/utils/time'; +import { + asTimestampNS, + msToNs, + nsToDate, + nsToMs, + nsToRfc3339, +} from '../../../src/utils/time'; + +describe('asTimestampNS', () => { + it('brands the value without converting it', () => { + const raw = 1786219962651957000; + + expect(asTimestampNS(raw)).toBe(raw); + expect(asTimestampNS(0)).toBe(0); + expect(nsToDate(asTimestampNS(raw)).getTime()).toBe(nsToMs(raw)); + }); +}); describe('nsToRfc3339', () => { /** A real on-device value, whose sub-millisecond remainder is non-zero. */ - const NANOS = 1786219962651957000; + const NANOS = asTimestampNS(1786219962651957000); it('emits nine fractional digits', () => { expect(nsToRfc3339(NANOS)).toMatch(/^\d{4}-\d{2}-\d{2}T[\d:]{8}\.\d{9}Z$/); diff --git a/tsconfig.types-test.dist.json b/tsconfig.types-test.dist.json new file mode 100644 index 0000000000..85cafd6262 --- /dev/null +++ b/tsconfig.types-test.dist.json @@ -0,0 +1,10 @@ +{ + // The `test/types/` assertions against the built package, exactly as a consumer resolves it: with no + // `paths` override, the self-referencing `stream-chat` import goes through `package.json` `exports` + // to `dist/types`. `yarn build` runs it last, so a build whose published types lost the + // `DateConstructor` guard fails instead of shipping. + "extends": "./tsconfig.types-test.json", + "compilerOptions": { + "paths": {} + } +} diff --git a/tsconfig.types-test.json b/tsconfig.types-test.json new file mode 100644 index 0000000000..2b6a79e0c3 --- /dev/null +++ b/tsconfig.types-test.json @@ -0,0 +1,17 @@ +{ + // Compile-time assertions in `test/types/` against the source tree. `yarn types` runs it. + // + // `stream-chat` is mapped to `src/` so the assertions need no build; `tsconfig.types-test.dist.json` + // re-runs the same files against the built `dist/types`. + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": ".", + "emitDeclarationOnly": false, + "declaration": false, + "paths": { + "stream-chat": ["./src/index.ts"] + } + }, + "include": ["./test/types/**/*.ts"] +} diff --git a/v9-to-v10-migration-guide-dates.md b/v9-to-v10-migration-guide-dates.md index 6c48838068..5f0fea651b 100644 --- a/v9-to-v10-migration-guide-dates.md +++ b/v9-to-v10-migration-guide-dates.md @@ -23,12 +23,18 @@ `expires` — on every response type, every `WSEvent` member, and the SDK state stores that mirror them. Not a `Date`, not an ISO string. In v9 these were typed `string`; in the early v10 RCs they were decoded to `Date`. Both are gone: there is no decoder layer any more. +- **They are typed `TimestampNS`, a branded `number`.** Reading, comparing, sorting and subtracting + work exactly as with a plain `number`. What changes is creating one: a plain `number` is not + assignable, so optimistic objects and fixtures use `nowNs()`, `msToNs()`, `dateToNs()` or + `asTimestampNS()`. See [`TimestampNS` and the `new Date` guard](#timestampns-and-the-new-date-guard). +- **`new Date(timestamp)` is a compile error.** The package's types augment the global + `DateConstructor`, so the most common mistake — `new Date(message.created_at)` — no longer compiles. - **Outgoing request date fields are still `Date`.** So a response value can no longer be assigned to a request field — the one thing that _was_ safe in v9, when both sides were `string`. The compiler catches this. -- **Three things break with no compile error**: every `Date`-based path is out of range, so - `new Date(ns)` and `dayjs(ns)` are both an `Invalid Date`; a millisecond/nanosecond mix-up between - two `number`s produces a plausible wrong answer and no complaint; and `0` is now a legitimate +- **Three things still break with no compile error**: a date library is out of range too, so + `dayjs(ns)` is an `Invalid Date` (only `new Date` is guarded); a millisecond/nanosecond mix-up + between two numbers produces a plausible wrong answer and no complaint; and `0` is now a legitimate timestamp, so `if (!created_at)` is wrong. - **`t('timestamp.X', { timestamp })` is not type-checked** — i18next's interpolation bag is untyped, so a raw nanosecond number reaches the formatter and renders the literal text `Invalid Date` into @@ -37,7 +43,7 @@ - **Filter operands changed meaning**: a bare `number` in a filter is now read as nanoseconds, not milliseconds. - Convert with the helpers the package now exports: `convertTimestampToDate` (guarded), `nsToDate`, - `nsToMs`, `msToNs`, `nowNs`, `dateToNs`, `nsToRfc3339`, `NS_PER_MS`. + `nsToMs`, `msToNs`, `nowNs`, `dateToNs`, `nsToRfc3339`, `asTimestampNS`, `NS_PER_MS`. --- @@ -59,13 +65,15 @@ reads a bare number as milliseconds, which lands in exactly the same place rathe plausible. ```ts -new Date(message.created_at); // Invalid Date +new Date(message.created_at); // Invalid Date (now a compile error — see below) new Date(message.created_at).toISOString(); // RangeError: Invalid time value dayjs(message.created_at).isValid(); // false dayjs(message.created_at).format(); // 'Invalid Date' — the literal string ``` -Neither is a type error, and the two surface differently: `.toISOString()` **throws**, usually +The `new Date` forms are caught at compile time by the +[guard](#timestampns-and-the-new-date-guard); the date-library forms are not, and the two surface +differently: `.toISOString()` **throws**, usually mid-render in a component that had no reason to expect it, while dayjs's `.format()` quietly returns the string `'Invalid Date'` and renders it on screen. So this mistake is loud in a `RangeError` stack trace and near-silent in a formatted timestamp — do not rely on noticing it either way. @@ -102,20 +110,93 @@ What this means in practice: --- +## `TimestampNS` and the `new Date` guard + +Every server-sent date field is typed `TimestampNS` rather than `number`: + +```ts +export type TimestampNS = number & { readonly [timestampNsBrand]: true }; +``` + +It is still a `number` at runtime and for every read. Comparison, sorting, subtraction and passing it +where a `number` is expected all compile as before. Two things change. + +**Minting one needs a helper.** A plain `number` is not assignable to a `TimestampNS` field, so code +that _constructs_ a response-shaped object — an optimistic message, a local reaction, a test fixture, +a row read back from your own database — has to say where the value came from: + +| Source | Use | +| ----------------------------------------------------------------------------------------- | ------------------ | +| The local clock | `nowNs()` | +| Epoch milliseconds | `msToNs(ms)` | +| A `Date` | `dateToNs(date)` | +| A number that is already nanoseconds (a DB row, persisted JSON, a fixture, the epoch `0`) | `asTimestampNS(n)` | + +`asTimestampNS` brands without converting — handing it milliseconds produces a mislabelled value, so +reserve it for values that are known to be nanoseconds. Arithmetic drops the brand +(`nowNs() + msToNs(5000)` is a plain `number`); wrap the result in `asTimestampNS` when it goes back +into a timestamp field. + +**`new Date(timestamp)` does not compile.** The package's published types include a global +augmentation (`gen/models/timestamp-guard`) that adds a `DateConstructor` overload for `TimestampNS`: + +```ts +const d: Date = new Date(message.created_at); +// error TS2740: Type '{ readonly ERROR_use_SDKs_nsToDate_helper_instead: never; }' is missing … +new Date(message.created_at).toISOString(); +// error TS2339: Property 'toISOString' does not exist on type '{ readonly ERROR_use_SDKs_… }' +new Date(Date.now()); // unaffected +nsToDate(message.created_at); // the fix +``` + +The helpers that consume an instant — `nsToDate`, `nsToRfc3339`, `convertTimestampToDate` — take a +`TimestampNS` too, so `nsToDate(Date.now())` is an error rather than a date in 1970. `nsToMs` takes a +plain `number`, because it also converts durations (the difference of two timestamps). + +What the guard does **not** catch: + +- **An unused result.** `console.log(new Date(ts))` compiles, since the result is never used as a + `Date`. The overload is `@deprecated`, so editors strike it through and + `@typescript-eslint/no-deprecated` reports it. +- **Date libraries.** `dayjs(ts)` / `moment(ts)` accept any `number`. +- **A fallback or a derived value.** The overload only matches an argument that is _purely_ + `TimestampNS`, and each of these compiles clean and is an `Invalid Date` at runtime: + + ```ts + new Date(message.pinned_at ?? Date.now()); // `TimestampNS | number` collapses to `number` + new Date(message.pinned_at ?? 0); // `TimestampNS | 0` is no longer purely branded + new Date(Math.max(a.created_at, b.created_at)); // `Math.max` returns a plain `number` + ``` + + Convert first, then fall back: `convertTimestampToDate(message.pinned_at) ?? fallbackDate`, and + `nsToDate(asTimestampNS(Math.max(a.created_at, b.created_at)))`. + +- **Untyped paths.** i18next interpolation (see + [Rendering timestamps](#rendering-timestamps-the-one-path-the-compiler-does-not-guard)), `any`, and + values you have widened to `number` yourself. +- **Unit mix-ups.** `ts > Date.now()` is two numbers compared; see + [Comparison and arithmetic](#comparison-and-arithmetic). + +The augmentation is global: it applies to the whole program once `stream-chat`'s types are loaded, +and there is no per-file opt-out. + +--- + ## The helpers All exported from the package root. -| Helper | Signature | Use for | -| ----------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------ | -| `convertTimestampToDate(ts?)` | `number \| null \| undefined → Date \| undefined` | The default. Guarded: `undefined` for an absent or non-finite value. | -| `nsToDate(ns)` | `number → Date` | A value known to be present. | -| `nsToMs(ns)` | `number → number` | Epoch milliseconds, for arithmetic against `Date.now()`. | -| `msToNs(ms)` | `number → number` | Milliseconds back into the wire unit. | -| `nowNs()` | `() → number` | The local clock as a wire timestamp, for optimistic writes. | -| `dateToNs(date)` | `Date → number` | A `Date` into the wire unit. | -| `nsToRfc3339(ns)` | `number → string` | A nanosecond-precision RFC3339 string, when milliseconds are not enough. | -| `NS_PER_MS` | `1e6` | The conversion constant, if you need it directly. | +| Helper | Signature | Use for | +| ----------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------- | +| `convertTimestampToDate(ts?)` | `TimestampNS \| null \| undefined → Date \| undefined` | The default. Guarded: `undefined` for an absent or non-finite value. | +| `nsToDate(ns)` | `TimestampNS → Date` | A value known to be present. | +| `nsToMs(ns)` | `number → number` | Epoch milliseconds (or a ns duration in ms), for arithmetic. | +| `msToNs(ms)` | `number → TimestampNS` | Milliseconds back into the wire unit. | +| `nowNs()` | `() → TimestampNS` | The local clock as a wire timestamp, for optimistic writes. | +| `dateToNs(date)` | `Date → TimestampNS` | A `Date` into the wire unit. | +| `nsToRfc3339(ns)` | `TimestampNS → string` | A nanosecond-precision RFC3339 string, when milliseconds are not enough. | +| `asTimestampNS(n)` | `number → TimestampNS` | Brands a value already in nanoseconds (DB rows, fixtures). No conversion. | +| `NS_PER_MS` | `1e6` | The conversion constant, if you need it directly. | **Prefer `convertTimestampToDate` at the boundary where a wire number becomes something a date library or a UI prop consumes.** Many timestamps are optional in practice even where the generated @@ -246,22 +327,26 @@ number. The SDK normalises it internally (`src/channel.ts`); if you read the fie yourself, parse it rather than treating it as a wire number. This is an upstream spec bug and the field is expected to become a number. -### `pinMessage`'s `number` overload now collides with the response type +### `pinMessage`'s `number` overload rejects a server timestamp ```ts client.pinMessage(messageOrId, timeoutOrExpirationDate?, pinnedAt?, requestOptions?); ``` -For both date arguments a `number` means **relative seconds**, not a timestamp — unchanged from v9, -but `message.pinned_at` is now also a `number`, so the wrong thing type-checks: +For both date arguments a `number` means **relative seconds**, not a timestamp — unchanged from v9. +`message.pinned_at` is now also a number, so both parameters exclude `TimestampNS`: handing a +server timestamp back is a compile error rather than an expiry ~1.79e18 seconds away. ```ts -// WRONG — reads the timestamp as "1.79e18 seconds from now". +// Compile error — would read the timestamp as "1.79e18 seconds from now". client.pinMessage(id, null, message.pinned_at); // RIGHT client.pinMessage(id, null, nsToDate(message.pinned_at)); ``` +A value you have widened to plain `number` yourself still type-checks as seconds, so keep server +timestamps typed as `TimestampNS` up to this call. + ### Filter operands: a server query needs a `Date`, client-side matching reads a number as nanoseconds These two paths behave differently, and only one of them fails loudly. @@ -360,41 +445,43 @@ Two related notes: ## Changed public types and members in this package -Type changes where the member name is unchanged and only the type moved from `Date` to `number`: - -| Type / member | Was | Now | -| ----------------------------------------------------------- | ------------------- | --------------------- | -| `ChannelMuteStatus.createdAt` / `.expiresAt` | `Date \| null` | `number \| null` | -| `ChannelState['read'][userId].last_read` | `Date` | `number` | -| `ChannelState['read'][userId].last_delivered_at` | `Date \| undefined` | `number \| undefined` | -| `ThreadState.createdAt` | `Date` | `number` | -| `ThreadState.deletedAt` / `.updatedAt` | `Date \| null` | `number \| null` | -| `ThreadUserReadState.lastReadAt` | `Date` | `number` | -| `ReminderState.created_at` / `.updated_at` | `Date` | `number` | -| `ReminderState.remind_at` | `Date \| null` | `number \| null` | -| `UnreadSnapshotState.lastReadAt` | `Date \| null` | `number \| null` | -| `MessagePaginatorAggregateState.seededLastMessageAt` | `Date \| null` | `number \| null` | -| `MessagePaginator.lastMessageAt` (getter) | `Date \| null` | `number \| null` | -| `LocalEvent` `created_at`, and `received_at` on every event | `Date` | `number` | -| `ConnectedEvent.created_at` / `.received_at` | `Date` | `number` | -| `DBDeleteMessagesForChannelType.truncated_at` | `Date \| undefined` | `number \| undefined` | +Type changes where the member name is unchanged and only the type moved from `Date` to `TimestampNS` +(read as `number`; see [`TimestampNS`](#timestampns-and-the-new-date-guard)): + +| Type / member | Was | Now | +| ----------------------------------------------------------- | ------------------- | -------------------------- | +| `ChannelMuteStatus.createdAt` / `.expiresAt` | `Date \| null` | `TimestampNS \| null` | +| `ChannelState['read'][userId].last_read` | `Date` | `TimestampNS` | +| `ChannelState['read'][userId].last_delivered_at` | `Date \| undefined` | `TimestampNS \| undefined` | +| `ThreadState.createdAt` | `Date` | `TimestampNS` | +| `ThreadState.deletedAt` / `.updatedAt` | `Date \| null` | `TimestampNS \| null` | +| `ThreadUserReadState.lastReadAt` | `Date` | `TimestampNS` | +| `ReminderState.created_at` / `.updated_at` | `Date` | `TimestampNS` | +| `ReminderState.remind_at` | `Date \| null` | `TimestampNS \| null` | +| `UnreadSnapshotState.lastReadAt` | `Date \| null` | `TimestampNS \| null` | +| `MessagePaginatorAggregateState.seededLastMessageAt` | `Date \| null` | `TimestampNS \| null` | +| `MessagePaginator.lastMessageAt` (getter) | `Date \| null` | `TimestampNS \| null` | +| `LocalEvent` `created_at`, and `received_at` on every event | `Date` | `TimestampNS` | +| `ConnectedEvent.created_at` / `.received_at` | `Date` | `TimestampNS` | +| `DBDeleteMessagesForChannelType.truncated_at` | `Date \| undefined` | `TimestampNS \| undefined` | Signature changes: -| Member | Was | Now | -| ---------------------------------------------------------------- | ------------------------------------- | ---------------------------------- | -| `channel.countUnread(lastRead?)` | `Date \| null` | `number \| null` | -| `channel.lastRead()` | `Date \| null \| undefined` | `number \| null \| undefined` | -| `channel.muteStatus()` | `{ createdAt: Date \| null; … }` | `{ createdAt: number \| null; … }` | -| `MessagePaginator.seedLastMessageAt(value)` | `string \| Date \| null \| undefined` | `number \| null \| undefined` | -| `MessagePaginator.truncate({ truncatedAt })` | `Date` | `number` | -| `MessagePaginator.applyMessageDeletionForUser({ deletedAt })` | `Date` | `number` | -| `MessagePaginator.findItemByTimestamp(timestamp, exactTsMatch?)` | epoch **ms** | wire **ns** | -| `MessageReceiptsTracker.onMessageDelivered({ deliveredAt })` | `Date` | `number` | -| `MessageReceiptsTracker.onMessageRead({ readAt })` | `Date` | `number` | -| `MessageReceiptsTracker.reconcileUserRead({ lastReadAt })` | `Date \| undefined` | `number \| undefined` | -| `timeLeftMs(remindAt)` | epoch **ms** | wire **ns** | -| `LocationComposer.validLocation` (getter) | `SharedLocation \| null` | `StaticLocationPreview \| null` | +| Member | Was | Now | +| ---------------------------------------------------------------- | -------------------------------------- | ------------------------------------------ | +| `channel.countUnread(lastRead?)` | `Date \| null` | `TimestampNS \| null` | +| `channel.lastRead()` | `Date \| null \| undefined` | `TimestampNS \| null \| undefined` | +| `channel.muteStatus()` | `{ createdAt: Date \| null; … }` | `{ createdAt: TimestampNS \| null; … }` | +| `MessagePaginator.seedLastMessageAt(value)` | `string \| Date \| null \| undefined` | `TimestampNS \| null \| undefined` | +| `MessagePaginator.truncate({ truncatedAt })` | `Date` | `TimestampNS` | +| `MessagePaginator.applyMessageDeletionForUser({ deletedAt })` | `Date` | `TimestampNS` | +| `MessagePaginator.findItemByTimestamp(timestamp, exactTsMatch?)` | epoch **ms** | `TimestampNS` | +| `MessageReceiptsTracker.onMessageDelivered({ deliveredAt })` | `Date` | `TimestampNS` | +| `MessageReceiptsTracker.onMessageRead({ readAt })` | `Date` | `TimestampNS` | +| `MessageReceiptsTracker.reconcileUserRead({ lastReadAt })` | `Date \| undefined` | `TimestampNS \| undefined` | +| `timeLeftMs(remindAt)` | epoch **ms** | `TimestampNS` | +| `client.pinMessage(id, timeoutOrExpirationDate?, pinnedAt?)` | `number` (seconds) accepted any number | `number` (seconds), `TimestampNS` rejected | +| `LocationComposer.validLocation` (getter) | `SharedLocation \| null` | `StaticLocationPreview \| null` | Renames — these do **not** fail as a type error if you were reading them off a value typed `any`: @@ -406,12 +493,13 @@ Renames — these do **not** fail as a type error if you were reading them off a | `MsgRef.timestampMs` | `MsgRef.timestamp` | | `OwnMessageReceiptsTrackerMessageLocator(msgMs)` | `OwnMessageReceiptsTrackerMessageLocator(ns)` | -**Unit changes with no type change** — the compiler cannot help with these at all: +**Unit changes** — now typed `TimestampNS`, so assigning a millisecond value no longer compiles: -| Member | Was | Now | -| ------------------------------------------------- | ------------ | ----------- | -| `PollState.lastActivityAt` | `Date` | wire **ns** | -| `LastComposerChange.stateUpdate` / `.draftUpdate` | epoch **ms** | wire **ns** | +| Member | Was | Now | +| ------------------------------------------------- | --------------------- | -------------------------- | +| `PollState.lastActivityAt` | `Date` | `TimestampNS` | +| `LastComposerChange.stateUpdate` / `.draftUpdate` | epoch **ms** `number` | `TimestampNS` | +| `CooldownTimerState.ownLatestMessageTimestamp` | — | `TimestampNS \| undefined` | Removed: @@ -443,5 +531,7 @@ Storage-level notes if you maintain your own: round-trips through SQLite `INTEGER` exactly, and lexicographic ISO sorting is no longer needed. - `ORDER BY` and range comparisons become plain numeric ones — drop any `datetime(…)` / `strftime(…)` wrapping, which silently returns `NULL` for an integer column. +- Rows read back hold plain numbers. Brand them with `asTimestampNS(row.created_at)` when rebuilding + a response-shaped object — do not route them through `msToNs`, which would multiply them again. - Distinguish absent from epoch. Write `NULL` for an absent timestamp rather than `0` or `''`, or you reintroduce exactly the ambiguity the `!= null` discipline above exists to remove.