archastro.platform.channels.api_chat_channel

  1# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License.
  2# This file is auto-generated by @archastro/sdk-generator. Do not edit.
  3# Content hash: 068983a8456f
  4
  5from collections.abc import Callable
  6from datetime import datetime
  7from typing import TYPE_CHECKING, Any, Literal, Required, TypedDict
  8
  9if TYPE_CHECKING:
 10    from archastro.phx_channel.socket import Socket
 11
 12
 13class ApiChatForkThreadInput(TypedDict, total=False):
 14    "Fork a sub-thread from an existing message"
 15
 16    message_id: Required[str]
 17    title: str
 18
 19
 20class ApiChatMarkThreadReadInput(TypedDict):
 21    "Mark a thread as read up to a given message"
 22
 23    message_id: str
 24
 25
 26class ApiChatLoadMoreMessagesInput(TypedDict, total=False):
 27    "Load additional messages with cursor-based pagination"
 28
 29    after_cursor: str
 30    before_cursor: str
 31    include_metadata: bool
 32    limit: int
 33
 34
 35class ApiChatPostMessageInput(TypedDict, total=False):
 36    "Post a new message with optional uploads and reply-to"
 37
 38    content: Required[str]
 39    idempotency_key: str
 40    reply_to: str
 41    uploads: list[dict[str, Any]]
 42
 43
 44class ApiChatPostSimpleMessageInput(TypedDict, total=False):
 45    "Post a simple text message"
 46
 47    content: str
 48    idempotency_key: str
 49    reply_to: str
 50
 51
 52class ApiChatEditMessageInput(TypedDict):
 53    "Edit an existing message's content"
 54
 55    content: str
 56    message_id: str
 57
 58
 59class ApiChatDeleteMessageInput(TypedDict):
 60    "Delete a message"
 61
 62    message_id: str
 63
 64
 65class ApiChatAddReactionInput(TypedDict):
 66    "Add an emoji reaction to a message"
 67
 68    emoji: str
 69    message_id: str
 70
 71
 72class ApiChatRemoveReactionInput(TypedDict):
 73    "Remove an emoji reaction from a message"
 74
 75    emoji: str
 76    message_id: str
 77
 78
 79class ApiChatTypingInput(TypedDict):
 80    "Signal that the current user has started or stopped typing in the thread"
 81
 82    is_typing: bool
 83
 84
 85class MessageAddedPayloadMessageAclAddItem(TypedDict, total=False):
 86    actions: Required[list[str]]
 87    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
 88    principal: str | None
 89    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
 90    principal_type: Required[str]
 91    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
 92
 93
 94class MessageAddedPayloadMessageAclGrantsItem(TypedDict, total=False):
 95    actions: Required[list[str]]
 96    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
 97    principal: str | None
 98    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
 99    principal_type: Required[str]
100    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
101
102
103class MessageAddedPayloadMessageAclRemoveItem(TypedDict, total=False):
104    principal: str | None
105    'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.'
106    principal_type: Required[str]
107    'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
108
109
110class MessageAddedPayloadMessageAcl(TypedDict, total=False):
111    add: list[MessageAddedPayloadMessageAclAddItem] | None
112    "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`."
113    grants: list[MessageAddedPayloadMessageAclGrantsItem] | None
114    "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`."
115    remove: list[MessageAddedPayloadMessageAclRemoveItem] | None
116    "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."
117
118
119class MessageAddedPayloadMessageActorsItemProfilePicture(TypedDict, total=False):
120    file: str | None
121    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
122    height: int | None
123    "Height of the image in pixels. `null` if not known."
124    media: str | None
125    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
126    mime_type: str | None
127    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
128    refresh_url: str | None
129    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
130    url: str | None
131    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
132    width: int | None
133    "Width of the image in pixels. `null` if not known."
134
135
136class MessageAddedPayloadMessageActorsItem(TypedDict, total=False):
137    alias: str | None
138    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
139    id: str | None
140    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
141    name: str | None
142    "Display name of the actor shown in the UI. `null` if no name is set."
143    profile_picture: MessageAddedPayloadMessageActorsItemProfilePicture | None
144    "Profile picture for the actor. `null` if the actor has no profile picture."
145
146
147class MessageAddedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False):
148    file: str | None
149    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
150    height: int | None
151    "Height of the image in pixels. `null` if not known."
152    media: str | None
153    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
154    mime_type: str | None
155    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
156    refresh_url: str | None
157    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
158    url: str | None
159    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
160    width: int | None
161    "Width of the image in pixels. `null` if not known."
162
163
164class MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False):
165    file: str | None
166    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
167    height: int | None
168    "Height of the image in pixels. `null` if not known."
169    media: str | None
170    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
171    mime_type: str | None
172    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
173    refresh_url: str | None
174    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
175    url: str | None
176    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
177    width: int | None
178    "Width of the image in pixels. `null` if not known."
179
180
181class MessageAddedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False):
182    content_type: str | None
183    'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.'
184    created_at: datetime | None
185    "When this variant was created (ISO 8601)."
186    file: str | None
187    "ID of the underlying storage file that backs this variant (`fil_...`)."
188    filename: str | None
189    "Original filename of the uploaded file for this variant. `null` if the file is not loaded."
190    height: int | None
191    "Height of this variant in pixels. `null` if not recorded."
192    id: Required[str]
193    "Media variant ID (`mvr_...`)."
194    image_source: MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource | None
195    "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types."
196    updated_at: datetime | None
197    "When this variant was last updated (ISO 8601)."
198    url: str | None
199    "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable."
200    variant_key: str | None
201    'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).'
202    width: int | None
203    "Width of this variant in pixels. `null` if not recorded."
204
205
206class MessageAddedPayloadMessageAttachmentsItem(TypedDict, total=False):
207    content_type: str | None
208    'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
209    description: str | None
210    "Short description. The page meta-description for `scraped_link`, the artifact description for `artifact`, and the task description for `task` types. `null` on other types."
211    filename: str | None
212    'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
213    height: int | None
214    "Height in pixels of the media item. Present on `media` type only. `null` otherwise."
215    id: Required[str]
216    "Unique identifier for this attachment within the message."
217    image_height: int | None
218    "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
219    image_source: MessageAddedPayloadMessageAttachmentsItemImageSource | None
220    "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise."
221    image_url: str | None
222    "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise."
223    image_width: int | None
224    "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
225    media_type: str | None
226    'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only; omitted otherwise.'
227    name: str | None
228    "Display name of the media item. Present on `media` type only. `null` otherwise."
229    object: dict[str, Any] | None
230    "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. For `chart` type, contains the chart with its inline `spec`. Omitted on other types."
231    title: str | None
232    "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types."
233    type: Required[str]
234    'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, `"action"`, or `"chart"`. Determines which additional fields are present.'
235    url: str | None
236    "URL to access the resource. A signed download URL for `file` and `artifact` types; the original URL for `scraped_link`; a media playback URL for `media`. `null` on `task` and `action` types."
237    variants: list[MessageAddedPayloadMessageAttachmentsItemVariantsItem] | None
238    "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise."
239    version: int | None
240    "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise."
241    width: int | None
242    "Width in pixels of the media item. Present on `media` type only. `null` otherwise."
243
244
245class MessageAddedPayloadMessageReactionsItem(TypedDict, total=False):
246    payload: dict[str, Any] | None
247    'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).'
248    type: Required[str]
249    'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.'
250    user: str | None
251    "Public ID of the user who added the reaction (`usr_...`)."
252
253
254class MessageAddedPayloadMessage(TypedDict, total=False):
255    acl: MessageAddedPayloadMessageAcl | None
256    "Access control list for private messages (grants with `read` action). Only returned to resource owners (and privileged/org-admin viewers) via server-side `field_redactions: [acl: :owner]`; `null` for everyone else."
257    actors: list[MessageAddedPayloadMessageActorsItem] | None
258    "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry."
259    agent: str | None
260    "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users."
261    agent_mode: Literal["cli", "embedded"] | None
262    "Local agent execution mode for this message. One of `cli`, `embedded`, or `null` when the message was not created by a local agent execution path."
263    attachments: list[MessageAddedPayloadMessageAttachmentsItem] | None
264    "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments."
265    branched_thread: str | None
266    "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread."
267    content: str | None
268    "Text content of the message. `null` for messages that contain only attachments."
269    created_at: str | None
270    "When the message was posted (ISO 8601)."
271    has_replies: bool | None
272    "Whether this message has at least one reply. Only present when explicitly requested or computed by the server."
273    id: Required[str]
274    "Message ID (`msg_...`)."
275    idempotency_key: str | None
276    "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one."
277    is_deleted: bool | None
278    "Whether this message is a deletion tombstone. `true` only on the `message_updated` broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always `false` for live messages."
279    legacy_agent: str | None
280    "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users."
281    metadata: dict[str, Any] | None
282    "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set."
283    org: str | None
284    "ID of the organization that owns this message (`org_...`)."
285    reactions: list[MessageAddedPayloadMessageReactionsItem] | None
286    "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded."
287    rendering_mode: str | None
288    'Display hint for how the message should be rendered. One of `"reply"`, `"direct"`, or `"inline"`. `null` for user-authored messages, which are always rendered as standard replies.'
289    replies: list[dict[str, Any]] | None
290    "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message."
291    replies_after_cursor: str | None
292    "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response."
293    replies_before_cursor: str | None
294    "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response."
295    reply_count: int | None
296    "Total number of direct replies to this message. Only present when explicitly requested or computed by the server."
297    reply_to: dict[str, Any] | None
298    "The parent message this message is a reply to, expanded as a full message object when loaded. `null` if this is a top-level message or the association is not preloaded."
299    root_message_id: str | None
300    "ID of the root message in this reply chain (`msg_...`). `null` for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages."
301    sandbox: str | None
302    "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages."
303    team: str | None
304    "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped."
305    thread: str | None
306    "ID of the thread this message belongs to (`thr_...`)."
307    type: str | None
308    "Optional client-defined classification for the message (for example `note` or `status`). Free-form string up to 64 characters. The value `system` is reserved for platform-authored messages and cannot be set by clients. `null` when unset."
309    user: str | dict[str, Any] | None
310    "The human user who sent this message. Returns a public ID string (`usr_...`) when the association is not preloaded, or an expanded user object when it is. `null` for messages sent by agents."
311    visibility: Literal["default", "private"] | None
312    "Message-level visibility. `default` is visible to anyone who can see the parent thread. `private` is restricted to the sender and explicit ACL `read` grantees."
313
314
315class MessageAddedPayload(TypedDict, total=False):
316    "Broadcast when a new message is added to a thread"
317
318    after_cursor: str | None
319    before_cursor: str | None
320    message: MessageAddedPayloadMessage | None
321    "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata."
322    thread_id: str | None
323
324
325class MessageUpdatedPayloadMessageAclAddItem(TypedDict, total=False):
326    actions: Required[list[str]]
327    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
328    principal: str | None
329    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
330    principal_type: Required[str]
331    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
332
333
334class MessageUpdatedPayloadMessageAclGrantsItem(TypedDict, total=False):
335    actions: Required[list[str]]
336    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
337    principal: str | None
338    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
339    principal_type: Required[str]
340    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
341
342
343class MessageUpdatedPayloadMessageAclRemoveItem(TypedDict, total=False):
344    principal: str | None
345    'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.'
346    principal_type: Required[str]
347    'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
348
349
350class MessageUpdatedPayloadMessageAcl(TypedDict, total=False):
351    add: list[MessageUpdatedPayloadMessageAclAddItem] | None
352    "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`."
353    grants: list[MessageUpdatedPayloadMessageAclGrantsItem] | None
354    "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`."
355    remove: list[MessageUpdatedPayloadMessageAclRemoveItem] | None
356    "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."
357
358
359class MessageUpdatedPayloadMessageActorsItemProfilePicture(TypedDict, total=False):
360    file: str | None
361    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
362    height: int | None
363    "Height of the image in pixels. `null` if not known."
364    media: str | None
365    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
366    mime_type: str | None
367    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
368    refresh_url: str | None
369    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
370    url: str | None
371    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
372    width: int | None
373    "Width of the image in pixels. `null` if not known."
374
375
376class MessageUpdatedPayloadMessageActorsItem(TypedDict, total=False):
377    alias: str | None
378    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
379    id: str | None
380    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
381    name: str | None
382    "Display name of the actor shown in the UI. `null` if no name is set."
383    profile_picture: MessageUpdatedPayloadMessageActorsItemProfilePicture | None
384    "Profile picture for the actor. `null` if the actor has no profile picture."
385
386
387class MessageUpdatedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False):
388    file: str | None
389    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
390    height: int | None
391    "Height of the image in pixels. `null` if not known."
392    media: str | None
393    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
394    mime_type: str | None
395    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
396    refresh_url: str | None
397    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
398    url: str | None
399    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
400    width: int | None
401    "Width of the image in pixels. `null` if not known."
402
403
404class MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False):
405    file: str | None
406    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
407    height: int | None
408    "Height of the image in pixels. `null` if not known."
409    media: str | None
410    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
411    mime_type: str | None
412    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
413    refresh_url: str | None
414    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
415    url: str | None
416    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
417    width: int | None
418    "Width of the image in pixels. `null` if not known."
419
420
421class MessageUpdatedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False):
422    content_type: str | None
423    'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.'
424    created_at: datetime | None
425    "When this variant was created (ISO 8601)."
426    file: str | None
427    "ID of the underlying storage file that backs this variant (`fil_...`)."
428    filename: str | None
429    "Original filename of the uploaded file for this variant. `null` if the file is not loaded."
430    height: int | None
431    "Height of this variant in pixels. `null` if not recorded."
432    id: Required[str]
433    "Media variant ID (`mvr_...`)."
434    image_source: MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource | None
435    "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types."
436    updated_at: datetime | None
437    "When this variant was last updated (ISO 8601)."
438    url: str | None
439    "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable."
440    variant_key: str | None
441    'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).'
442    width: int | None
443    "Width of this variant in pixels. `null` if not recorded."
444
445
446class MessageUpdatedPayloadMessageAttachmentsItem(TypedDict, total=False):
447    content_type: str | None
448    'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
449    description: str | None
450    "Short description. The page meta-description for `scraped_link`, the artifact description for `artifact`, and the task description for `task` types. `null` on other types."
451    filename: str | None
452    'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
453    height: int | None
454    "Height in pixels of the media item. Present on `media` type only. `null` otherwise."
455    id: Required[str]
456    "Unique identifier for this attachment within the message."
457    image_height: int | None
458    "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
459    image_source: MessageUpdatedPayloadMessageAttachmentsItemImageSource | None
460    "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise."
461    image_url: str | None
462    "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise."
463    image_width: int | None
464    "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
465    media_type: str | None
466    'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only; omitted otherwise.'
467    name: str | None
468    "Display name of the media item. Present on `media` type only. `null` otherwise."
469    object: dict[str, Any] | None
470    "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. For `chart` type, contains the chart with its inline `spec`. Omitted on other types."
471    title: str | None
472    "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types."
473    type: Required[str]
474    'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, `"action"`, or `"chart"`. Determines which additional fields are present.'
475    url: str | None
476    "URL to access the resource. A signed download URL for `file` and `artifact` types; the original URL for `scraped_link`; a media playback URL for `media`. `null` on `task` and `action` types."
477    variants: list[MessageUpdatedPayloadMessageAttachmentsItemVariantsItem] | None
478    "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise."
479    version: int | None
480    "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise."
481    width: int | None
482    "Width in pixels of the media item. Present on `media` type only. `null` otherwise."
483
484
485class MessageUpdatedPayloadMessageReactionsItem(TypedDict, total=False):
486    payload: dict[str, Any] | None
487    'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).'
488    type: Required[str]
489    'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.'
490    user: str | None
491    "Public ID of the user who added the reaction (`usr_...`)."
492
493
494class MessageUpdatedPayloadMessage(TypedDict, total=False):
495    acl: MessageUpdatedPayloadMessageAcl | None
496    "Access control list for private messages (grants with `read` action). Only returned to resource owners (and privileged/org-admin viewers) via server-side `field_redactions: [acl: :owner]`; `null` for everyone else."
497    actors: list[MessageUpdatedPayloadMessageActorsItem] | None
498    "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry."
499    agent: str | None
500    "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users."
501    agent_mode: Literal["cli", "embedded"] | None
502    "Local agent execution mode for this message. One of `cli`, `embedded`, or `null` when the message was not created by a local agent execution path."
503    attachments: list[MessageUpdatedPayloadMessageAttachmentsItem] | None
504    "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments."
505    branched_thread: str | None
506    "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread."
507    content: str | None
508    "Text content of the message. `null` for messages that contain only attachments."
509    created_at: str | None
510    "When the message was posted (ISO 8601)."
511    has_replies: bool | None
512    "Whether this message has at least one reply. Only present when explicitly requested or computed by the server."
513    id: Required[str]
514    "Message ID (`msg_...`)."
515    idempotency_key: str | None
516    "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one."
517    is_deleted: bool | None
518    "Whether this message is a deletion tombstone. `true` only on the `message_updated` broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always `false` for live messages."
519    legacy_agent: str | None
520    "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users."
521    metadata: dict[str, Any] | None
522    "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set."
523    org: str | None
524    "ID of the organization that owns this message (`org_...`)."
525    reactions: list[MessageUpdatedPayloadMessageReactionsItem] | None
526    "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded."
527    rendering_mode: str | None
528    'Display hint for how the message should be rendered. One of `"reply"`, `"direct"`, or `"inline"`. `null` for user-authored messages, which are always rendered as standard replies.'
529    replies: list[dict[str, Any]] | None
530    "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message."
531    replies_after_cursor: str | None
532    "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response."
533    replies_before_cursor: str | None
534    "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response."
535    reply_count: int | None
536    "Total number of direct replies to this message. Only present when explicitly requested or computed by the server."
537    reply_to: dict[str, Any] | None
538    "The parent message this message is a reply to, expanded as a full message object when loaded. `null` if this is a top-level message or the association is not preloaded."
539    root_message_id: str | None
540    "ID of the root message in this reply chain (`msg_...`). `null` for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages."
541    sandbox: str | None
542    "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages."
543    team: str | None
544    "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped."
545    thread: str | None
546    "ID of the thread this message belongs to (`thr_...`)."
547    type: str | None
548    "Optional client-defined classification for the message (for example `note` or `status`). Free-form string up to 64 characters. The value `system` is reserved for platform-authored messages and cannot be set by clients. `null` when unset."
549    user: str | dict[str, Any] | None
550    "The human user who sent this message. Returns a public ID string (`usr_...`) when the association is not preloaded, or an expanded user object when it is. `null` for messages sent by agents."
551    visibility: Literal["default", "private"] | None
552    "Message-level visibility. `default` is visible to anyone who can see the parent thread. `private` is restricted to the sender and explicit ACL `read` grantees."
553
554
555class MessageUpdatedPayload(TypedDict, total=False):
556    "Broadcast when a message is updated or removed"
557
558    message: MessageUpdatedPayloadMessage | None
559    "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata."
560    thread_id: str | None
561
562
563class ThreadEventPayload(TypedDict, total=False):
564    "Broadcast thread-level events (agent updates, read state, unread counts)"
565
566    payload: dict[str, Any] | None
567    thread_id: str | None
568    type: str | None
569
570
571class SystemEventPayload(TypedDict, total=False):
572    "Broadcast system-wide events"
573
574    event: dict[str, Any] | None
575
576
577class TypingPayloadActorProfilePicture(TypedDict, total=False):
578    file: str | None
579    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
580    height: int | None
581    "Height of the image in pixels. `null` if not known."
582    media: str | None
583    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
584    mime_type: str | None
585    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
586    refresh_url: str | None
587    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
588    url: str | None
589    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
590    width: int | None
591    "Width of the image in pixels. `null` if not known."
592
593
594class TypingPayloadActor(TypedDict, total=False):
595    alias: str | None
596    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
597    id: str | None
598    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
599    name: str | None
600    "Display name of the actor shown in the UI. `null` if no name is set."
601    profile_picture: TypingPayloadActorProfilePicture | None
602    "Profile picture for the actor. `null` if the actor has no profile picture."
603
604
605class TypingPayload(TypedDict, total=False):
606    "Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted."
607
608    actor: TypingPayloadActor | None
609    "The entity that authored a message, either a human user or an agent."
610    is_typing: bool | None
611    thread_id: str | None
612
613
614# Channel for real-time chat messaging.
615# Supports team-scoped and user-scoped threads with keyed, transient, and direct
616# thread access patterns.
617class ApiChatChannel:
618    def __init__(self, channel, join_response=None):
619        self._channel = channel
620        self.join_response = join_response
621
622    # Join a team-scoped thread by ID
623    @staticmethod
624    def topic_team_thread(team_id: str, thread_id: str) -> str:
625        return f"api:chat:team:{team_id}:thread:{thread_id}"
626
627    # Join a team-scoped thread by ID
628    @classmethod
629    async def join_team_thread(
630        cls,
631        socket: "Socket",
632        team_id: str,
633        thread_id: str,
634        *,
635        after_cursor: str | None = None,
636        before_cursor: str | None = None,
637        include_metadata: bool | None = None,
638        limit: int | None = None,
639    ) -> "ApiChatChannel":
640        topic = cls.topic_team_thread(team_id, thread_id)
641        channel = socket.channel(topic)
642        payload: dict[str, object] = {}
643        if after_cursor is not None:
644            payload["after_cursor"] = after_cursor
645        if before_cursor is not None:
646            payload["before_cursor"] = before_cursor
647        if include_metadata is not None:
648            payload["include_metadata"] = include_metadata
649        if limit is not None:
650            payload["limit"] = limit
651        join_response = await channel.join(payload)
652        return cls(channel, join_response)
653
654    # Join or create a team-scoped keyed thread
655    @staticmethod
656    def topic_team_keyed(team_id: str, key: str) -> str:
657        return f"api:chat:team:{team_id}:key:{key}"
658
659    # Join or create a team-scoped keyed thread
660    @classmethod
661    async def join_team_keyed(
662        cls,
663        socket: "Socket",
664        team_id: str,
665        key: str,
666        *,
667        after_cursor: str | None = None,
668        before_cursor: str | None = None,
669        include_metadata: bool | None = None,
670        limit: int | None = None,
671    ) -> "ApiChatChannel":
672        topic = cls.topic_team_keyed(team_id, key)
673        channel = socket.channel(topic)
674        payload: dict[str, object] = {}
675        if after_cursor is not None:
676            payload["after_cursor"] = after_cursor
677        if before_cursor is not None:
678            payload["before_cursor"] = before_cursor
679        if include_metadata is not None:
680            payload["include_metadata"] = include_metadata
681        if limit is not None:
682            payload["limit"] = limit
683        join_response = await channel.join(payload)
684        return cls(channel, join_response)
685
686    # Join a team-scoped transient (ephemeral) thread
687    @staticmethod
688    def topic_team_transient(team_id: str, key: str) -> str:
689        return f"api:chat:team:{team_id}:transient:{key}"
690
691    # Join a team-scoped transient (ephemeral) thread
692    @classmethod
693    async def join_team_transient(
694        cls,
695        socket: "Socket",
696        team_id: str,
697        key: str,
698        *,
699        after_cursor: str | None = None,
700        before_cursor: str | None = None,
701        include_metadata: bool | None = None,
702        limit: int | None = None,
703    ) -> "ApiChatChannel":
704        topic = cls.topic_team_transient(team_id, key)
705        channel = socket.channel(topic)
706        payload: dict[str, object] = {}
707        if after_cursor is not None:
708            payload["after_cursor"] = after_cursor
709        if before_cursor is not None:
710            payload["before_cursor"] = before_cursor
711        if include_metadata is not None:
712            payload["include_metadata"] = include_metadata
713        if limit is not None:
714            payload["limit"] = limit
715        join_response = await channel.join(payload)
716        return cls(channel, join_response)
717
718    # Join a user-scoped thread by ID, optionally supplying local tools for a personal thread
719    @staticmethod
720    def topic_user_thread(thread_id: str) -> str:
721        return f"api:chat:user:thread:{thread_id}"
722
723    # Join a user-scoped thread by ID, optionally supplying local tools for a personal thread
724    @classmethod
725    async def join_user_thread(
726        cls,
727        socket: "Socket",
728        thread_id: str,
729        *,
730        after_cursor: str | None = None,
731        before_cursor: str | None = None,
732        include_metadata: bool | None = None,
733        limit: int | None = None,
734        local_tool_provider_id: str | None = None,
735        local_tools: list[dict[str, Any]] | None = None,
736    ) -> "ApiChatChannel":
737        topic = cls.topic_user_thread(thread_id)
738        channel = socket.channel(topic)
739        payload: dict[str, object] = {}
740        if after_cursor is not None:
741            payload["after_cursor"] = after_cursor
742        if before_cursor is not None:
743            payload["before_cursor"] = before_cursor
744        if include_metadata is not None:
745            payload["include_metadata"] = include_metadata
746        if limit is not None:
747            payload["limit"] = limit
748        if local_tool_provider_id is not None:
749            payload["local_tool_provider_id"] = local_tool_provider_id
750        if local_tools is not None:
751            payload["local_tools"] = local_tools
752        join_response = await channel.join(payload)
753        return cls(channel, join_response)
754
755    # Join or create a user-scoped keyed thread
756    @staticmethod
757    def topic_user_keyed(key: str) -> str:
758        return f"api:chat:user:key:{key}"
759
760    # Join or create a user-scoped keyed thread
761    @classmethod
762    async def join_user_keyed(
763        cls,
764        socket: "Socket",
765        key: str,
766        *,
767        after_cursor: str | None = None,
768        before_cursor: str | None = None,
769        include_metadata: bool | None = None,
770        limit: int | None = None,
771    ) -> "ApiChatChannel":
772        topic = cls.topic_user_keyed(key)
773        channel = socket.channel(topic)
774        payload: dict[str, object] = {}
775        if after_cursor is not None:
776            payload["after_cursor"] = after_cursor
777        if before_cursor is not None:
778            payload["before_cursor"] = before_cursor
779        if include_metadata is not None:
780            payload["include_metadata"] = include_metadata
781        if limit is not None:
782            payload["limit"] = limit
783        join_response = await channel.join(payload)
784        return cls(channel, join_response)
785
786    # Join a user-scoped transient (ephemeral) thread
787    @staticmethod
788    def topic_user_transient(key: str) -> str:
789        return f"api:chat:user:transient:{key}"
790
791    # Join a user-scoped transient (ephemeral) thread
792    @classmethod
793    async def join_user_transient(
794        cls,
795        socket: "Socket",
796        key: str,
797        *,
798        after_cursor: str | None = None,
799        before_cursor: str | None = None,
800        include_metadata: bool | None = None,
801        limit: int | None = None,
802    ) -> "ApiChatChannel":
803        topic = cls.topic_user_transient(key)
804        channel = socket.channel(topic)
805        payload: dict[str, object] = {}
806        if after_cursor is not None:
807            payload["after_cursor"] = after_cursor
808        if before_cursor is not None:
809            payload["before_cursor"] = before_cursor
810        if include_metadata is not None:
811            payload["include_metadata"] = include_metadata
812        if limit is not None:
813            payload["limit"] = limit
814        join_response = await channel.join(payload)
815        return cls(channel, join_response)
816
817    # Leave the underlying channel.
818    async def leave(self):
819        await self._channel.leave()
820
821    # Fork a sub-thread from an existing message
822    async def api_chat_fork_thread(self, payload: ApiChatForkThreadInput) -> dict[str, Any]:
823        return await self._channel.push("api:chat:fork_thread", payload)
824
825    # Mark a thread as read up to a given message
826    async def api_chat_mark_thread_read(
827        self, payload: ApiChatMarkThreadReadInput
828    ) -> dict[str, Any]:
829        return await self._channel.push("api:chat:mark_thread_read", payload)
830
831    # List all messages in the current thread
832    async def api_chat_list_messages(self, payload: dict) -> dict[str, Any]:
833        return await self._channel.push("api:chat:list_messages", payload)
834
835    # Load additional messages with cursor-based pagination
836    async def api_chat_load_more_messages(
837        self, payload: ApiChatLoadMoreMessagesInput
838    ) -> dict[str, Any]:
839        return await self._channel.push("api:chat:load_more_messages", payload)
840
841    # Post a new message with optional uploads and reply-to
842    async def api_chat_post_message(self, payload: ApiChatPostMessageInput) -> dict[str, Any]:
843        return await self._channel.push("api:chat:post_message", payload)
844
845    # Post a simple text message
846    async def api_chat_post_simple_message(
847        self, payload: ApiChatPostSimpleMessageInput
848    ) -> dict[str, Any]:
849        return await self._channel.push("api:chat:post_simple_message", payload)
850
851    # Edit an existing message's content
852    async def api_chat_edit_message(self, payload: ApiChatEditMessageInput) -> dict[str, Any]:
853        return await self._channel.push("api:chat:edit_message", payload)
854
855    # Delete a message
856    async def api_chat_delete_message(self, payload: ApiChatDeleteMessageInput) -> dict[str, Any]:
857        return await self._channel.push("api:chat:delete_message", payload)
858
859    # Add an emoji reaction to a message
860    async def api_chat_add_reaction(self, payload: ApiChatAddReactionInput) -> dict[str, Any]:
861        return await self._channel.push("api:chat:add_reaction", payload)
862
863    # Remove an emoji reaction from a message
864    async def api_chat_remove_reaction(self, payload: ApiChatRemoveReactionInput) -> dict[str, Any]:
865        return await self._channel.push("api:chat:remove_reaction", payload)
866
867    # Signal that the current user has started or stopped typing in the thread
868    async def api_chat_typing(self, payload: ApiChatTypingInput) -> dict[str, Any]:
869        return await self._channel.push("api:chat:typing", payload)
870
871    # Broadcast when a new message is added to a thread
872    def on_message_added(
873        self, callback: Callable[[MessageAddedPayload], None]
874    ) -> Callable[[], None]:
875        return self._channel.on("message_added", callback)
876
877    # Broadcast when a message is updated or removed
878    def on_message_updated(
879        self, callback: Callable[[MessageUpdatedPayload], None]
880    ) -> Callable[[], None]:
881        return self._channel.on("message_updated", callback)
882
883    # Broadcast thread-level events (agent updates, read state, unread counts)
884    def on_thread_event(self, callback: Callable[[ThreadEventPayload], None]) -> Callable[[], None]:
885        return self._channel.on("thread_event", callback)
886
887    # Broadcast system-wide events
888    def on_system_event(self, callback: Callable[[SystemEventPayload], None]) -> Callable[[], None]:
889        return self._channel.on("system_event", callback)
890
891    # Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted.
892    def on_typing(self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]:
893        return self._channel.on("typing", callback)
class ApiChatForkThreadInput(typing.TypedDict):
14class ApiChatForkThreadInput(TypedDict, total=False):
15    "Fork a sub-thread from an existing message"
16
17    message_id: Required[str]
18    title: str

Fork a sub-thread from an existing message

message_id: Required[str]
title: str
class ApiChatMarkThreadReadInput(typing.TypedDict):
21class ApiChatMarkThreadReadInput(TypedDict):
22    "Mark a thread as read up to a given message"
23
24    message_id: str

Mark a thread as read up to a given message

message_id: str
class ApiChatLoadMoreMessagesInput(typing.TypedDict):
27class ApiChatLoadMoreMessagesInput(TypedDict, total=False):
28    "Load additional messages with cursor-based pagination"
29
30    after_cursor: str
31    before_cursor: str
32    include_metadata: bool
33    limit: int

Load additional messages with cursor-based pagination

after_cursor: str
before_cursor: str
include_metadata: bool
limit: int
class ApiChatPostMessageInput(typing.TypedDict):
36class ApiChatPostMessageInput(TypedDict, total=False):
37    "Post a new message with optional uploads and reply-to"
38
39    content: Required[str]
40    idempotency_key: str
41    reply_to: str
42    uploads: list[dict[str, Any]]

Post a new message with optional uploads and reply-to

content: Required[str]
idempotency_key: str
reply_to: str
uploads: list[dict[str, typing.Any]]
class ApiChatPostSimpleMessageInput(typing.TypedDict):
45class ApiChatPostSimpleMessageInput(TypedDict, total=False):
46    "Post a simple text message"
47
48    content: str
49    idempotency_key: str
50    reply_to: str

Post a simple text message

content: str
idempotency_key: str
reply_to: str
class ApiChatEditMessageInput(typing.TypedDict):
53class ApiChatEditMessageInput(TypedDict):
54    "Edit an existing message's content"
55
56    content: str
57    message_id: str

Edit an existing message's content

content: str
message_id: str
class ApiChatDeleteMessageInput(typing.TypedDict):
60class ApiChatDeleteMessageInput(TypedDict):
61    "Delete a message"
62
63    message_id: str

Delete a message

message_id: str
class ApiChatAddReactionInput(typing.TypedDict):
66class ApiChatAddReactionInput(TypedDict):
67    "Add an emoji reaction to a message"
68
69    emoji: str
70    message_id: str

Add an emoji reaction to a message

emoji: str
message_id: str
class ApiChatRemoveReactionInput(typing.TypedDict):
73class ApiChatRemoveReactionInput(TypedDict):
74    "Remove an emoji reaction from a message"
75
76    emoji: str
77    message_id: str

Remove an emoji reaction from a message

emoji: str
message_id: str
class ApiChatTypingInput(typing.TypedDict):
80class ApiChatTypingInput(TypedDict):
81    "Signal that the current user has started or stopped typing in the thread"
82
83    is_typing: bool

Signal that the current user has started or stopped typing in the thread

is_typing: bool
class MessageAddedPayloadMessageAclAddItem(typing.TypedDict):
86class MessageAddedPayloadMessageAclAddItem(TypedDict, total=False):
87    actions: Required[list[str]]
88    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
89    principal: str | None
90    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
91    principal_type: Required[str]
92    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
actions: Required[list[str]]

Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.

principal: str | None

The identifier of the principal. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role"; omit entirely when principal_type is "everyone".

principal_type: Required[str]

The kind of principal receiving the grant. One of "user", "team", "org", "org_role", "agent", or "everyone".

class MessageAddedPayloadMessageAclGrantsItem(typing.TypedDict):
 95class MessageAddedPayloadMessageAclGrantsItem(TypedDict, total=False):
 96    actions: Required[list[str]]
 97    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
 98    principal: str | None
 99    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
100    principal_type: Required[str]
101    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
actions: Required[list[str]]

Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.

principal: str | None

The identifier of the principal. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role"; omit entirely when principal_type is "everyone".

principal_type: Required[str]

The kind of principal receiving the grant. One of "user", "team", "org", "org_role", "agent", or "everyone".

class MessageAddedPayloadMessageAclRemoveItem(typing.TypedDict):
104class MessageAddedPayloadMessageAclRemoveItem(TypedDict, total=False):
105    principal: str | None
106    'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.'
107    principal_type: Required[str]
108    'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
principal: str | None

The identifier of the principal to remove. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role". Omit when principal_type is "everyone".

principal_type: Required[str]

The kind of principal to remove. One of "user", "team", "org", "org_role", "agent", or "everyone".

class MessageAddedPayloadMessageAcl(typing.TypedDict):
111class MessageAddedPayloadMessageAcl(TypedDict, total=False):
112    add: list[MessageAddedPayloadMessageAclAddItem] | None
113    "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`."
114    grants: list[MessageAddedPayloadMessageAclGrantsItem] | None
115    "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`."
116    remove: list[MessageAddedPayloadMessageAclRemoveItem] | None
117    "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."

Patch mode: grants to add or merge into the existing list. Cannot be combined with grants.

Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array ([]) to clear all grants. Cannot be combined with add or remove.

Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with grants.

class MessageAddedPayloadMessageActorsItemProfilePicture(typing.TypedDict):
120class MessageAddedPayloadMessageActorsItemProfilePicture(TypedDict, total=False):
121    file: str | None
122    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
123    height: int | None
124    "Height of the image in pixels. `null` if not known."
125    media: str | None
126    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
127    mime_type: str | None
128    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
129    refresh_url: str | None
130    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
131    url: str | None
132    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
133    width: int | None
134    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageAddedPayloadMessageActorsItem(typing.TypedDict):
137class MessageAddedPayloadMessageActorsItem(TypedDict, total=False):
138    alias: str | None
139    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
140    id: str | None
141    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
142    name: str | None
143    "Display name of the actor shown in the UI. `null` if no name is set."
144    profile_picture: MessageAddedPayloadMessageActorsItemProfilePicture | None
145    "Profile picture for the actor. `null` if the actor has no profile picture."
alias: str | None

Short handle or alias for the actor, used as an alternate display identifier. null if not configured.

id: str | None

Composite actor identifier. Format is "user-<usr_...>" for human users or "agent-<agi_...>" for agents.

name: str | None

Display name of the actor shown in the UI. null if no name is set.

Profile picture for the actor. null if the actor has no profile picture.

class MessageAddedPayloadMessageAttachmentsItemImageSource(typing.TypedDict):
148class MessageAddedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False):
149    file: str | None
150    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
151    height: int | None
152    "Height of the image in pixels. `null` if not known."
153    media: str | None
154    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
155    mime_type: str | None
156    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
157    refresh_url: str | None
158    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
159    url: str | None
160    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
161    width: int | None
162    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource(typing.TypedDict):
165class MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False):
166    file: str | None
167    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
168    height: int | None
169    "Height of the image in pixels. `null` if not known."
170    media: str | None
171    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
172    mime_type: str | None
173    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
174    refresh_url: str | None
175    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
176    url: str | None
177    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
178    width: int | None
179    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageAddedPayloadMessageAttachmentsItemVariantsItem(typing.TypedDict):
182class MessageAddedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False):
183    content_type: str | None
184    'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.'
185    created_at: datetime | None
186    "When this variant was created (ISO 8601)."
187    file: str | None
188    "ID of the underlying storage file that backs this variant (`fil_...`)."
189    filename: str | None
190    "Original filename of the uploaded file for this variant. `null` if the file is not loaded."
191    height: int | None
192    "Height of this variant in pixels. `null` if not recorded."
193    id: Required[str]
194    "Media variant ID (`mvr_...`)."
195    image_source: MessageAddedPayloadMessageAttachmentsItemVariantsItemImageSource | None
196    "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types."
197    updated_at: datetime | None
198    "When this variant was last updated (ISO 8601)."
199    url: str | None
200    "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable."
201    variant_key: str | None
202    'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).'
203    width: int | None
204    "Width of this variant in pixels. `null` if not recorded."
content_type: str | None

MIME type of this variant's file (e.g., "image/jpeg", "video/mp4"). null if the file is not loaded.

created_at: datetime.datetime | None

When this variant was created (ISO 8601).

file: str | None

ID of the underlying storage file that backs this variant (fil_...).

filename: str | None

Original filename of the uploaded file for this variant. null if the file is not loaded.

height: int | None

Height of this variant in pixels. null if not recorded.

id: Required[str]

Media variant ID (mvr_...).

Resolved image delivery metadata for this variant, including dimensions and CDN URL. null for non-image content types.

updated_at: datetime.datetime | None

When this variant was last updated (ISO 8601).

url: str | None

Signed download URL for this variant, resolved at request time. null if the file is unavailable.

variant_key: str | None

Identifier for this variant's processing tier. Common values include "original" (the unmodified upload) and "thumbnail" (a resized preview).

width: int | None

Width of this variant in pixels. null if not recorded.

class MessageAddedPayloadMessageAttachmentsItem(typing.TypedDict):
207class MessageAddedPayloadMessageAttachmentsItem(TypedDict, total=False):
208    content_type: str | None
209    'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
210    description: str | None
211    "Short description. The page meta-description for `scraped_link`, the artifact description for `artifact`, and the task description for `task` types. `null` on other types."
212    filename: str | None
213    'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
214    height: int | None
215    "Height in pixels of the media item. Present on `media` type only. `null` otherwise."
216    id: Required[str]
217    "Unique identifier for this attachment within the message."
218    image_height: int | None
219    "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
220    image_source: MessageAddedPayloadMessageAttachmentsItemImageSource | None
221    "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise."
222    image_url: str | None
223    "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise."
224    image_width: int | None
225    "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
226    media_type: str | None
227    'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only; omitted otherwise.'
228    name: str | None
229    "Display name of the media item. Present on `media` type only. `null` otherwise."
230    object: dict[str, Any] | None
231    "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. For `chart` type, contains the chart with its inline `spec`. Omitted on other types."
232    title: str | None
233    "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types."
234    type: Required[str]
235    'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, `"action"`, or `"chart"`. Determines which additional fields are present.'
236    url: str | None
237    "URL to access the resource. A signed download URL for `file` and `artifact` types; the original URL for `scraped_link`; a media playback URL for `media`. `null` on `task` and `action` types."
238    variants: list[MessageAddedPayloadMessageAttachmentsItemVariantsItem] | None
239    "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise."
240    version: int | None
241    "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise."
242    width: int | None
243    "Width in pixels of the media item. Present on `media` type only. `null` otherwise."
content_type: str | None

MIME type of the attached file, e.g. "image/png" or "application/pdf". Present on file, artifact, and media types. null otherwise.

description: str | None

Short description. The page meta-description for scraped_link, the artifact description for artifact, and the task description for task types. null on other types.

filename: str | None

Original filename of the attached file, e.g. "report.pdf". Present on file, artifact, and media types. null otherwise.

height: int | None

Height in pixels of the media item. Present on media type only. null otherwise.

id: Required[str]

Unique identifier for this attachment within the message.

image_height: int | None

Height in pixels of the scraped preview image. Present on scraped_link type only. null otherwise.

Image source metadata for inline rendering. Present on file, scraped_link, artifact, and media types when the content is an image. null otherwise.

image_url: str | None

URL of the preview image extracted from the scraped page. Present on scraped_link type only. null otherwise.

image_width: int | None

Width in pixels of the scraped preview image. Present on scraped_link type only. null otherwise.

media_type: str | None

The media category, e.g. "video" or "audio". Present on media type only; omitted otherwise.

name: str | None

Display name of the media item. Present on media type only. null otherwise.

object: dict[str, typing.Any] | None

The full embedded object payload. For task type, contains the task record. For action type, contains the action definition. For chart type, contains the chart with its inline spec. Omitted on other types.

title: str | None

Display title. The page title for scraped_link, the artifact name for artifact, and the task title for task types. null on other types.

type: Required[str]

The attachment type. One of "file", "scraped_link", "artifact", "task", "media", "action", or "chart". Determines which additional fields are present.

url: str | None

URL to access the resource. A signed download URL for file and artifact types; the original URL for scraped_link; a media playback URL for media. null on task and action types.

Array of available encoding variants for the media item (e.g. different resolutions). Present on media type only; omitted otherwise.

version: int | None

Version number of the attached artifact at the time of attachment. Present on artifact type only. null otherwise.

width: int | None

Width in pixels of the media item. Present on media type only. null otherwise.

class MessageAddedPayloadMessageReactionsItem(typing.TypedDict):
246class MessageAddedPayloadMessageReactionsItem(TypedDict, total=False):
247    payload: dict[str, Any] | None
248    'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).'
249    type: Required[str]
250    'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.'
251    user: str | None
252    "Public ID of the user who added the reaction (`usr_...`)."
payload: dict[str, typing.Any] | None

Type-specific reaction data. For "emoji_reaction" reactions, contains an emoji key with the Unicode emoji string (e.g., " ").

type: Required[str]

Reaction type identifier. Currently always "emoji_reaction" for emoji-based reactions.

user: str | None

Public ID of the user who added the reaction (usr_...).

class MessageAddedPayloadMessage(typing.TypedDict):
255class MessageAddedPayloadMessage(TypedDict, total=False):
256    acl: MessageAddedPayloadMessageAcl | None
257    "Access control list for private messages (grants with `read` action). Only returned to resource owners (and privileged/org-admin viewers) via server-side `field_redactions: [acl: :owner]`; `null` for everyone else."
258    actors: list[MessageAddedPayloadMessageActorsItem] | None
259    "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry."
260    agent: str | None
261    "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users."
262    agent_mode: Literal["cli", "embedded"] | None
263    "Local agent execution mode for this message. One of `cli`, `embedded`, or `null` when the message was not created by a local agent execution path."
264    attachments: list[MessageAddedPayloadMessageAttachmentsItem] | None
265    "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments."
266    branched_thread: str | None
267    "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread."
268    content: str | None
269    "Text content of the message. `null` for messages that contain only attachments."
270    created_at: str | None
271    "When the message was posted (ISO 8601)."
272    has_replies: bool | None
273    "Whether this message has at least one reply. Only present when explicitly requested or computed by the server."
274    id: Required[str]
275    "Message ID (`msg_...`)."
276    idempotency_key: str | None
277    "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one."
278    is_deleted: bool | None
279    "Whether this message is a deletion tombstone. `true` only on the `message_updated` broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always `false` for live messages."
280    legacy_agent: str | None
281    "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users."
282    metadata: dict[str, Any] | None
283    "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set."
284    org: str | None
285    "ID of the organization that owns this message (`org_...`)."
286    reactions: list[MessageAddedPayloadMessageReactionsItem] | None
287    "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded."
288    rendering_mode: str | None
289    'Display hint for how the message should be rendered. One of `"reply"`, `"direct"`, or `"inline"`. `null` for user-authored messages, which are always rendered as standard replies.'
290    replies: list[dict[str, Any]] | None
291    "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message."
292    replies_after_cursor: str | None
293    "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response."
294    replies_before_cursor: str | None
295    "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response."
296    reply_count: int | None
297    "Total number of direct replies to this message. Only present when explicitly requested or computed by the server."
298    reply_to: dict[str, Any] | None
299    "The parent message this message is a reply to, expanded as a full message object when loaded. `null` if this is a top-level message or the association is not preloaded."
300    root_message_id: str | None
301    "ID of the root message in this reply chain (`msg_...`). `null` for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages."
302    sandbox: str | None
303    "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages."
304    team: str | None
305    "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped."
306    thread: str | None
307    "ID of the thread this message belongs to (`thr_...`)."
308    type: str | None
309    "Optional client-defined classification for the message (for example `note` or `status`). Free-form string up to 64 characters. The value `system` is reserved for platform-authored messages and cannot be set by clients. `null` when unset."
310    user: str | dict[str, Any] | None
311    "The human user who sent this message. Returns a public ID string (`usr_...`) when the association is not preloaded, or an expanded user object when it is. `null` for messages sent by agents."
312    visibility: Literal["default", "private"] | None
313    "Message-level visibility. `default` is visible to anyone who can see the parent thread. `private` is restricted to the sender and explicit ACL `read` grantees."

Access control list for private messages (grants with read action). Only returned to resource owners (and privileged/org-admin viewers) via server-side field_redactions: [acl: :owner]; null for everyone else.

Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry.

agent: str | None

ID of the agent user that sent this message (agi_...). null for messages sent by human users.

agent_mode: Optional[Literal['cli', 'embedded']]

Local agent execution mode for this message. One of cli, embedded, or null when the message was not created by a local agent execution path.

attachments: list[MessageAddedPayloadMessageAttachmentsItem] | None

Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments.

branched_thread: str | None

ID of the thread that was branched from this message (thr_...). null if this message has not spawned a branch thread.

content: str | None

Text content of the message. null for messages that contain only attachments.

created_at: str | None

When the message was posted (ISO 8601).

has_replies: bool | None

Whether this message has at least one reply. Only present when explicitly requested or computed by the server.

id: Required[str]

Message ID (msg_...).

idempotency_key: str | None

Client-supplied idempotency key used to deduplicate message sends. null if the sender did not provide one.

is_deleted: bool | None

Whether this message is a deletion tombstone. true only on the message_updated broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always false for live messages.

legacy_agent: str | None

Identifier of the legacy chat agent that sent this message, if applicable. null for messages sent by users or modern agent users.

metadata: dict[str, typing.Any] | None

Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set.

org: str | None

ID of the organization that owns this message (org_...).

reactions: list[MessageAddedPayloadMessageReactionsItem] | None

Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded.

rendering_mode: str | None

Display hint for how the message should be rendered. One of "reply", "direct", or "inline". null for user-authored messages, which are always rendered as standard replies.

replies: list[dict[str, typing.Any]] | None

Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message.

replies_after_cursor: str | None

Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response.

replies_before_cursor: str | None

Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response.

reply_count: int | None

Total number of direct replies to this message. Only present when explicitly requested or computed by the server.

reply_to: dict[str, typing.Any] | None

The parent message this message is a reply to, expanded as a full message object when loaded. null if this is a top-level message or the association is not preloaded.

root_message_id: str | None

ID of the root message in this reply chain (msg_...). null for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages.

sandbox: str | None

ID of the developer sandbox this message belongs to (dsb_...). null for non-sandbox messages.

team: str | None

ID of the team this message is scoped to (tem_...). null if the message is not team-scoped.

thread: str | None

ID of the thread this message belongs to (thr_...).

type: str | None

Optional client-defined classification for the message (for example note or status). Free-form string up to 64 characters. The value system is reserved for platform-authored messages and cannot be set by clients. null when unset.

user: str | dict[str, typing.Any] | None

The human user who sent this message. Returns a public ID string (usr_...) when the association is not preloaded, or an expanded user object when it is. null for messages sent by agents.

visibility: Optional[Literal['default', 'private']]

Message-level visibility. default is visible to anyone who can see the parent thread. private is restricted to the sender and explicit ACL read grantees.

class MessageAddedPayload(typing.TypedDict):
316class MessageAddedPayload(TypedDict, total=False):
317    "Broadcast when a new message is added to a thread"
318
319    after_cursor: str | None
320    before_cursor: str | None
321    message: MessageAddedPayloadMessage | None
322    "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata."
323    thread_id: str | None

Broadcast when a new message is added to a thread

after_cursor: str | None
before_cursor: str | None
message: MessageAddedPayloadMessage | None

A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata.

thread_id: str | None
class MessageUpdatedPayloadMessageAclAddItem(typing.TypedDict):
326class MessageUpdatedPayloadMessageAclAddItem(TypedDict, total=False):
327    actions: Required[list[str]]
328    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
329    principal: str | None
330    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
331    principal_type: Required[str]
332    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
actions: Required[list[str]]

Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.

principal: str | None

The identifier of the principal. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role"; omit entirely when principal_type is "everyone".

principal_type: Required[str]

The kind of principal receiving the grant. One of "user", "team", "org", "org_role", "agent", or "everyone".

class MessageUpdatedPayloadMessageAclGrantsItem(typing.TypedDict):
335class MessageUpdatedPayloadMessageAclGrantsItem(TypedDict, total=False):
336    actions: Required[list[str]]
337    'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.'
338    principal: str | None
339    'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.'
340    principal_type: Required[str]
341    'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
actions: Required[list[str]]

Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.

principal: str | None

The identifier of the principal. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role"; omit entirely when principal_type is "everyone".

principal_type: Required[str]

The kind of principal receiving the grant. One of "user", "team", "org", "org_role", "agent", or "everyone".

class MessageUpdatedPayloadMessageAclRemoveItem(typing.TypedDict):
344class MessageUpdatedPayloadMessageAclRemoveItem(TypedDict, total=False):
345    principal: str | None
346    'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.'
347    principal_type: Required[str]
348    'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
principal: str | None

The identifier of the principal to remove. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role". Omit when principal_type is "everyone".

principal_type: Required[str]

The kind of principal to remove. One of "user", "team", "org", "org_role", "agent", or "everyone".

class MessageUpdatedPayloadMessageAcl(typing.TypedDict):
351class MessageUpdatedPayloadMessageAcl(TypedDict, total=False):
352    add: list[MessageUpdatedPayloadMessageAclAddItem] | None
353    "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`."
354    grants: list[MessageUpdatedPayloadMessageAclGrantsItem] | None
355    "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`."
356    remove: list[MessageUpdatedPayloadMessageAclRemoveItem] | None
357    "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."

Patch mode: grants to add or merge into the existing list. Cannot be combined with grants.

Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array ([]) to clear all grants. Cannot be combined with add or remove.

Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with grants.

class MessageUpdatedPayloadMessageActorsItemProfilePicture(typing.TypedDict):
360class MessageUpdatedPayloadMessageActorsItemProfilePicture(TypedDict, total=False):
361    file: str | None
362    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
363    height: int | None
364    "Height of the image in pixels. `null` if not known."
365    media: str | None
366    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
367    mime_type: str | None
368    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
369    refresh_url: str | None
370    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
371    url: str | None
372    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
373    width: int | None
374    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageUpdatedPayloadMessageActorsItem(typing.TypedDict):
377class MessageUpdatedPayloadMessageActorsItem(TypedDict, total=False):
378    alias: str | None
379    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
380    id: str | None
381    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
382    name: str | None
383    "Display name of the actor shown in the UI. `null` if no name is set."
384    profile_picture: MessageUpdatedPayloadMessageActorsItemProfilePicture | None
385    "Profile picture for the actor. `null` if the actor has no profile picture."
alias: str | None

Short handle or alias for the actor, used as an alternate display identifier. null if not configured.

id: str | None

Composite actor identifier. Format is "user-<usr_...>" for human users or "agent-<agi_...>" for agents.

name: str | None

Display name of the actor shown in the UI. null if no name is set.

Profile picture for the actor. null if the actor has no profile picture.

class MessageUpdatedPayloadMessageAttachmentsItemImageSource(typing.TypedDict):
388class MessageUpdatedPayloadMessageAttachmentsItemImageSource(TypedDict, total=False):
389    file: str | None
390    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
391    height: int | None
392    "Height of the image in pixels. `null` if not known."
393    media: str | None
394    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
395    mime_type: str | None
396    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
397    refresh_url: str | None
398    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
399    url: str | None
400    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
401    width: int | None
402    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource(typing.TypedDict):
405class MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource(TypedDict, total=False):
406    file: str | None
407    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
408    height: int | None
409    "Height of the image in pixels. `null` if not known."
410    media: str | None
411    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
412    mime_type: str | None
413    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
414    refresh_url: str | None
415    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
416    url: str | None
417    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
418    width: int | None
419    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class MessageUpdatedPayloadMessageAttachmentsItemVariantsItem(typing.TypedDict):
422class MessageUpdatedPayloadMessageAttachmentsItemVariantsItem(TypedDict, total=False):
423    content_type: str | None
424    'MIME type of this variant\'s file (e.g., `"image/jpeg"`, `"video/mp4"`). `null` if the file is not loaded.'
425    created_at: datetime | None
426    "When this variant was created (ISO 8601)."
427    file: str | None
428    "ID of the underlying storage file that backs this variant (`fil_...`)."
429    filename: str | None
430    "Original filename of the uploaded file for this variant. `null` if the file is not loaded."
431    height: int | None
432    "Height of this variant in pixels. `null` if not recorded."
433    id: Required[str]
434    "Media variant ID (`mvr_...`)."
435    image_source: MessageUpdatedPayloadMessageAttachmentsItemVariantsItemImageSource | None
436    "Resolved image delivery metadata for this variant, including dimensions and CDN URL. `null` for non-image content types."
437    updated_at: datetime | None
438    "When this variant was last updated (ISO 8601)."
439    url: str | None
440    "Signed download URL for this variant, resolved at request time. `null` if the file is unavailable."
441    variant_key: str | None
442    'Identifier for this variant\'s processing tier. Common values include `"original"` (the unmodified upload) and `"thumbnail"` (a resized preview).'
443    width: int | None
444    "Width of this variant in pixels. `null` if not recorded."
content_type: str | None

MIME type of this variant's file (e.g., "image/jpeg", "video/mp4"). null if the file is not loaded.

created_at: datetime.datetime | None

When this variant was created (ISO 8601).

file: str | None

ID of the underlying storage file that backs this variant (fil_...).

filename: str | None

Original filename of the uploaded file for this variant. null if the file is not loaded.

height: int | None

Height of this variant in pixels. null if not recorded.

id: Required[str]

Media variant ID (mvr_...).

Resolved image delivery metadata for this variant, including dimensions and CDN URL. null for non-image content types.

updated_at: datetime.datetime | None

When this variant was last updated (ISO 8601).

url: str | None

Signed download URL for this variant, resolved at request time. null if the file is unavailable.

variant_key: str | None

Identifier for this variant's processing tier. Common values include "original" (the unmodified upload) and "thumbnail" (a resized preview).

width: int | None

Width of this variant in pixels. null if not recorded.

class MessageUpdatedPayloadMessageAttachmentsItem(typing.TypedDict):
447class MessageUpdatedPayloadMessageAttachmentsItem(TypedDict, total=False):
448    content_type: str | None
449    'MIME type of the attached file, e.g. `"image/png"` or `"application/pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
450    description: str | None
451    "Short description. The page meta-description for `scraped_link`, the artifact description for `artifact`, and the task description for `task` types. `null` on other types."
452    filename: str | None
453    'Original filename of the attached file, e.g. `"report.pdf"`. Present on `file`, `artifact`, and `media` types. `null` otherwise.'
454    height: int | None
455    "Height in pixels of the media item. Present on `media` type only. `null` otherwise."
456    id: Required[str]
457    "Unique identifier for this attachment within the message."
458    image_height: int | None
459    "Height in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
460    image_source: MessageUpdatedPayloadMessageAttachmentsItemImageSource | None
461    "Image source metadata for inline rendering. Present on `file`, `scraped_link`, `artifact`, and `media` types when the content is an image. `null` otherwise."
462    image_url: str | None
463    "URL of the preview image extracted from the scraped page. Present on `scraped_link` type only. `null` otherwise."
464    image_width: int | None
465    "Width in pixels of the scraped preview image. Present on `scraped_link` type only. `null` otherwise."
466    media_type: str | None
467    'The media category, e.g. `"video"` or `"audio"`. Present on `media` type only; omitted otherwise.'
468    name: str | None
469    "Display name of the media item. Present on `media` type only. `null` otherwise."
470    object: dict[str, Any] | None
471    "The full embedded object payload. For `task` type, contains the task record. For `action` type, contains the action definition. For `chart` type, contains the chart with its inline `spec`. Omitted on other types."
472    title: str | None
473    "Display title. The page title for `scraped_link`, the artifact name for `artifact`, and the task title for `task` types. `null` on other types."
474    type: Required[str]
475    'The attachment type. One of `"file"`, `"scraped_link"`, `"artifact"`, `"task"`, `"media"`, `"action"`, or `"chart"`. Determines which additional fields are present.'
476    url: str | None
477    "URL to access the resource. A signed download URL for `file` and `artifact` types; the original URL for `scraped_link`; a media playback URL for `media`. `null` on `task` and `action` types."
478    variants: list[MessageUpdatedPayloadMessageAttachmentsItemVariantsItem] | None
479    "Array of available encoding variants for the media item (e.g. different resolutions). Present on `media` type only; omitted otherwise."
480    version: int | None
481    "Version number of the attached artifact at the time of attachment. Present on `artifact` type only. `null` otherwise."
482    width: int | None
483    "Width in pixels of the media item. Present on `media` type only. `null` otherwise."
content_type: str | None

MIME type of the attached file, e.g. "image/png" or "application/pdf". Present on file, artifact, and media types. null otherwise.

description: str | None

Short description. The page meta-description for scraped_link, the artifact description for artifact, and the task description for task types. null on other types.

filename: str | None

Original filename of the attached file, e.g. "report.pdf". Present on file, artifact, and media types. null otherwise.

height: int | None

Height in pixels of the media item. Present on media type only. null otherwise.

id: Required[str]

Unique identifier for this attachment within the message.

image_height: int | None

Height in pixels of the scraped preview image. Present on scraped_link type only. null otherwise.

Image source metadata for inline rendering. Present on file, scraped_link, artifact, and media types when the content is an image. null otherwise.

image_url: str | None

URL of the preview image extracted from the scraped page. Present on scraped_link type only. null otherwise.

image_width: int | None

Width in pixels of the scraped preview image. Present on scraped_link type only. null otherwise.

media_type: str | None

The media category, e.g. "video" or "audio". Present on media type only; omitted otherwise.

name: str | None

Display name of the media item. Present on media type only. null otherwise.

object: dict[str, typing.Any] | None

The full embedded object payload. For task type, contains the task record. For action type, contains the action definition. For chart type, contains the chart with its inline spec. Omitted on other types.

title: str | None

Display title. The page title for scraped_link, the artifact name for artifact, and the task title for task types. null on other types.

type: Required[str]

The attachment type. One of "file", "scraped_link", "artifact", "task", "media", "action", or "chart". Determines which additional fields are present.

url: str | None

URL to access the resource. A signed download URL for file and artifact types; the original URL for scraped_link; a media playback URL for media. null on task and action types.

Array of available encoding variants for the media item (e.g. different resolutions). Present on media type only; omitted otherwise.

version: int | None

Version number of the attached artifact at the time of attachment. Present on artifact type only. null otherwise.

width: int | None

Width in pixels of the media item. Present on media type only. null otherwise.

class MessageUpdatedPayloadMessageReactionsItem(typing.TypedDict):
486class MessageUpdatedPayloadMessageReactionsItem(TypedDict, total=False):
487    payload: dict[str, Any] | None
488    'Type-specific reaction data. For `"emoji_reaction"` reactions, contains an `emoji` key with the Unicode emoji string (e.g., `" "`).'
489    type: Required[str]
490    'Reaction type identifier. Currently always `"emoji_reaction"` for emoji-based reactions.'
491    user: str | None
492    "Public ID of the user who added the reaction (`usr_...`)."
payload: dict[str, typing.Any] | None

Type-specific reaction data. For "emoji_reaction" reactions, contains an emoji key with the Unicode emoji string (e.g., " ").

type: Required[str]

Reaction type identifier. Currently always "emoji_reaction" for emoji-based reactions.

user: str | None

Public ID of the user who added the reaction (usr_...).

class MessageUpdatedPayloadMessage(typing.TypedDict):
495class MessageUpdatedPayloadMessage(TypedDict, total=False):
496    acl: MessageUpdatedPayloadMessageAcl | None
497    "Access control list for private messages (grants with `read` action). Only returned to resource owners (and privileged/org-admin viewers) via server-side `field_redactions: [acl: :owner]`; `null` for everyone else."
498    actors: list[MessageUpdatedPayloadMessageActorsItem] | None
499    "Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry."
500    agent: str | None
501    "ID of the agent user that sent this message (`agi_...`). `null` for messages sent by human users."
502    agent_mode: Literal["cli", "embedded"] | None
503    "Local agent execution mode for this message. One of `cli`, `embedded`, or `null` when the message was not created by a local agent execution path."
504    attachments: list[MessageUpdatedPayloadMessageAttachmentsItem] | None
505    "Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments."
506    branched_thread: str | None
507    "ID of the thread that was branched from this message (`thr_...`). `null` if this message has not spawned a branch thread."
508    content: str | None
509    "Text content of the message. `null` for messages that contain only attachments."
510    created_at: str | None
511    "When the message was posted (ISO 8601)."
512    has_replies: bool | None
513    "Whether this message has at least one reply. Only present when explicitly requested or computed by the server."
514    id: Required[str]
515    "Message ID (`msg_...`)."
516    idempotency_key: str | None
517    "Client-supplied idempotency key used to deduplicate message sends. `null` if the sender did not provide one."
518    is_deleted: bool | None
519    "Whether this message is a deletion tombstone. `true` only on the `message_updated` broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always `false` for live messages."
520    legacy_agent: str | None
521    "Identifier of the legacy chat agent that sent this message, if applicable. `null` for messages sent by users or modern agent users."
522    metadata: dict[str, Any] | None
523    "Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set."
524    org: str | None
525    "ID of the organization that owns this message (`org_...`)."
526    reactions: list[MessageUpdatedPayloadMessageReactionsItem] | None
527    "Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded."
528    rendering_mode: str | None
529    'Display hint for how the message should be rendered. One of `"reply"`, `"direct"`, or `"inline"`. `null` for user-authored messages, which are always rendered as standard replies.'
530    replies: list[dict[str, Any]] | None
531    "Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message."
532    replies_after_cursor: str | None
533    "Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response."
534    replies_before_cursor: str | None
535    "Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response."
536    reply_count: int | None
537    "Total number of direct replies to this message. Only present when explicitly requested or computed by the server."
538    reply_to: dict[str, Any] | None
539    "The parent message this message is a reply to, expanded as a full message object when loaded. `null` if this is a top-level message or the association is not preloaded."
540    root_message_id: str | None
541    "ID of the root message in this reply chain (`msg_...`). `null` for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages."
542    sandbox: str | None
543    "ID of the developer sandbox this message belongs to (`dsb_...`). `null` for non-sandbox messages."
544    team: str | None
545    "ID of the team this message is scoped to (`tem_...`). `null` if the message is not team-scoped."
546    thread: str | None
547    "ID of the thread this message belongs to (`thr_...`)."
548    type: str | None
549    "Optional client-defined classification for the message (for example `note` or `status`). Free-form string up to 64 characters. The value `system` is reserved for platform-authored messages and cannot be set by clients. `null` when unset."
550    user: str | dict[str, Any] | None
551    "The human user who sent this message. Returns a public ID string (`usr_...`) when the association is not preloaded, or an expanded user object when it is. `null` for messages sent by agents."
552    visibility: Literal["default", "private"] | None
553    "Message-level visibility. `default` is visible to anyone who can see the parent thread. `private` is restricted to the sender and explicit ACL `read` grantees."

Access control list for private messages (grants with read action). Only returned to resource owners (and privileged/org-admin viewers) via server-side field_redactions: [acl: :owner]; null for everyone else.

Resolved actor descriptors for the message sender, combining identity and display metadata. Always contains exactly one entry.

agent: str | None

ID of the agent user that sent this message (agi_...). null for messages sent by human users.

agent_mode: Optional[Literal['cli', 'embedded']]

Local agent execution mode for this message. One of cli, embedded, or null when the message was not created by a local agent execution path.

Files, links, tasks, media, artifacts, and actions attached to this message. Empty array if there are no attachments.

branched_thread: str | None

ID of the thread that was branched from this message (thr_...). null if this message has not spawned a branch thread.

content: str | None

Text content of the message. null for messages that contain only attachments.

created_at: str | None

When the message was posted (ISO 8601).

has_replies: bool | None

Whether this message has at least one reply. Only present when explicitly requested or computed by the server.

id: Required[str]

Message ID (msg_...).

idempotency_key: str | None

Client-supplied idempotency key used to deduplicate message sends. null if the sender did not provide one.

is_deleted: bool | None

Whether this message is a deletion tombstone. true only on the message_updated broadcast emitted when a message is deleted: the original content is replaced with a placeholder and the message no longer exists on the server. Always false for live messages.

legacy_agent: str | None

Identifier of the legacy chat agent that sent this message, if applicable. null for messages sent by users or modern agent users.

metadata: dict[str, typing.Any] | None

Arbitrary key-value metadata attached to the message. Always present; defaults to an empty object when no metadata has been set.

org: str | None

ID of the organization that owns this message (org_...).

Emoji and other reactions added to this message by users. Empty array if no reactions have been added or the association is not preloaded.

rendering_mode: str | None

Display hint for how the message should be rendered. One of "reply", "direct", or "inline". null for user-authored messages, which are always rendered as standard replies.

replies: list[dict[str, typing.Any]] | None

Inline array of reply messages, each serialized as a full message object. Only present when the server has preloaded replies for this message.

replies_after_cursor: str | None

Opaque pagination cursor to fetch replies posted after the current page. Only present when inline replies are included in the response.

replies_before_cursor: str | None

Opaque pagination cursor to fetch replies posted before the current page. Only present when inline replies are included in the response.

reply_count: int | None

Total number of direct replies to this message. Only present when explicitly requested or computed by the server.

reply_to: dict[str, typing.Any] | None

The parent message this message is a reply to, expanded as a full message object when loaded. null if this is a top-level message or the association is not preloaded.

root_message_id: str | None

ID of the root message in this reply chain (msg_...). null for a top-level message. The value is persisted when the reply is created, so callers can correlate a multi-turn session without walking parent messages.

sandbox: str | None

ID of the developer sandbox this message belongs to (dsb_...). null for non-sandbox messages.

team: str | None

ID of the team this message is scoped to (tem_...). null if the message is not team-scoped.

thread: str | None

ID of the thread this message belongs to (thr_...).

type: str | None

Optional client-defined classification for the message (for example note or status). Free-form string up to 64 characters. The value system is reserved for platform-authored messages and cannot be set by clients. null when unset.

user: str | dict[str, typing.Any] | None

The human user who sent this message. Returns a public ID string (usr_...) when the association is not preloaded, or an expanded user object when it is. null for messages sent by agents.

visibility: Optional[Literal['default', 'private']]

Message-level visibility. default is visible to anyone who can see the parent thread. private is restricted to the sender and explicit ACL read grantees.

class MessageUpdatedPayload(typing.TypedDict):
556class MessageUpdatedPayload(TypedDict, total=False):
557    "Broadcast when a message is updated or removed"
558
559    message: MessageUpdatedPayloadMessage | None
560    "A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata."
561    thread_id: str | None

Broadcast when a message is updated or removed

A chat message posted in a thread, including its content, author, attachments, reactions, and optional reply metadata.

thread_id: str | None
class ThreadEventPayload(typing.TypedDict):
564class ThreadEventPayload(TypedDict, total=False):
565    "Broadcast thread-level events (agent updates, read state, unread counts)"
566
567    payload: dict[str, Any] | None
568    thread_id: str | None
569    type: str | None

Broadcast thread-level events (agent updates, read state, unread counts)

payload: dict[str, typing.Any] | None
thread_id: str | None
type: str | None
class SystemEventPayload(typing.TypedDict):
572class SystemEventPayload(TypedDict, total=False):
573    "Broadcast system-wide events"
574
575    event: dict[str, Any] | None

Broadcast system-wide events

event: dict[str, typing.Any] | None
class TypingPayloadActorProfilePicture(typing.TypedDict):
578class TypingPayloadActorProfilePicture(TypedDict, total=False):
579    file: str | None
580    "ID of the underlying storage file (`fil_...`). `null` when the image is not backed by a platform storage file."
581    height: int | None
582    "Height of the image in pixels. `null` if not known."
583    media: str | None
584    "ID of the associated media record (`med_...`). `null` when the image is not linked to a media entity."
585    mime_type: str | None
586    'MIME type of the image, e.g. `"image/png"` or `"image/jpeg"`. `null` if not known.'
587    refresh_url: str | None
588    "Endpoint URL you can call to obtain a fresh signed `url` when the current one has expired. `null` if the URL does not require refreshing."
589    url: str | None
590    "Signed or public URL for downloading the image. May be time-limited; use `refresh_url` to obtain a new URL when this one expires."
591    width: int | None
592    "Width of the image in pixels. `null` if not known."
file: str | None

ID of the underlying storage file (fil_...). null when the image is not backed by a platform storage file.

height: int | None

Height of the image in pixels. null if not known.

media: str | None

ID of the associated media record (med_...). null when the image is not linked to a media entity.

mime_type: str | None

MIME type of the image, e.g. "image/png" or "image/jpeg". null if not known.

refresh_url: str | None

Endpoint URL you can call to obtain a fresh signed url when the current one has expired. null if the URL does not require refreshing.

url: str | None

Signed or public URL for downloading the image. May be time-limited; use refresh_url to obtain a new URL when this one expires.

width: int | None

Width of the image in pixels. null if not known.

class TypingPayloadActor(typing.TypedDict):
595class TypingPayloadActor(TypedDict, total=False):
596    alias: str | None
597    "Short handle or alias for the actor, used as an alternate display identifier. `null` if not configured."
598    id: str | None
599    'Composite actor identifier. Format is `"user-<usr_...>"` for human users or `"agent-<agi_...>"` for agents.'
600    name: str | None
601    "Display name of the actor shown in the UI. `null` if no name is set."
602    profile_picture: TypingPayloadActorProfilePicture | None
603    "Profile picture for the actor. `null` if the actor has no profile picture."
alias: str | None

Short handle or alias for the actor, used as an alternate display identifier. null if not configured.

id: str | None

Composite actor identifier. Format is "user-<usr_...>" for human users or "agent-<agi_...>" for agents.

name: str | None

Display name of the actor shown in the UI. null if no name is set.

profile_picture: TypingPayloadActorProfilePicture | None

Profile picture for the actor. null if the actor has no profile picture.

class TypingPayload(typing.TypedDict):
606class TypingPayload(TypedDict, total=False):
607    "Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted."
608
609    actor: TypingPayloadActor | None
610    "The entity that authored a message, either a human user or an agent."
611    is_typing: bool | None
612    thread_id: str | None

Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted.

actor: TypingPayloadActor | None

The entity that authored a message, either a human user or an agent.

is_typing: bool | None
thread_id: str | None
class ApiChatChannel:
618class ApiChatChannel:
619    def __init__(self, channel, join_response=None):
620        self._channel = channel
621        self.join_response = join_response
622
623    # Join a team-scoped thread by ID
624    @staticmethod
625    def topic_team_thread(team_id: str, thread_id: str) -> str:
626        return f"api:chat:team:{team_id}:thread:{thread_id}"
627
628    # Join a team-scoped thread by ID
629    @classmethod
630    async def join_team_thread(
631        cls,
632        socket: "Socket",
633        team_id: str,
634        thread_id: str,
635        *,
636        after_cursor: str | None = None,
637        before_cursor: str | None = None,
638        include_metadata: bool | None = None,
639        limit: int | None = None,
640    ) -> "ApiChatChannel":
641        topic = cls.topic_team_thread(team_id, thread_id)
642        channel = socket.channel(topic)
643        payload: dict[str, object] = {}
644        if after_cursor is not None:
645            payload["after_cursor"] = after_cursor
646        if before_cursor is not None:
647            payload["before_cursor"] = before_cursor
648        if include_metadata is not None:
649            payload["include_metadata"] = include_metadata
650        if limit is not None:
651            payload["limit"] = limit
652        join_response = await channel.join(payload)
653        return cls(channel, join_response)
654
655    # Join or create a team-scoped keyed thread
656    @staticmethod
657    def topic_team_keyed(team_id: str, key: str) -> str:
658        return f"api:chat:team:{team_id}:key:{key}"
659
660    # Join or create a team-scoped keyed thread
661    @classmethod
662    async def join_team_keyed(
663        cls,
664        socket: "Socket",
665        team_id: str,
666        key: str,
667        *,
668        after_cursor: str | None = None,
669        before_cursor: str | None = None,
670        include_metadata: bool | None = None,
671        limit: int | None = None,
672    ) -> "ApiChatChannel":
673        topic = cls.topic_team_keyed(team_id, key)
674        channel = socket.channel(topic)
675        payload: dict[str, object] = {}
676        if after_cursor is not None:
677            payload["after_cursor"] = after_cursor
678        if before_cursor is not None:
679            payload["before_cursor"] = before_cursor
680        if include_metadata is not None:
681            payload["include_metadata"] = include_metadata
682        if limit is not None:
683            payload["limit"] = limit
684        join_response = await channel.join(payload)
685        return cls(channel, join_response)
686
687    # Join a team-scoped transient (ephemeral) thread
688    @staticmethod
689    def topic_team_transient(team_id: str, key: str) -> str:
690        return f"api:chat:team:{team_id}:transient:{key}"
691
692    # Join a team-scoped transient (ephemeral) thread
693    @classmethod
694    async def join_team_transient(
695        cls,
696        socket: "Socket",
697        team_id: str,
698        key: str,
699        *,
700        after_cursor: str | None = None,
701        before_cursor: str | None = None,
702        include_metadata: bool | None = None,
703        limit: int | None = None,
704    ) -> "ApiChatChannel":
705        topic = cls.topic_team_transient(team_id, key)
706        channel = socket.channel(topic)
707        payload: dict[str, object] = {}
708        if after_cursor is not None:
709            payload["after_cursor"] = after_cursor
710        if before_cursor is not None:
711            payload["before_cursor"] = before_cursor
712        if include_metadata is not None:
713            payload["include_metadata"] = include_metadata
714        if limit is not None:
715            payload["limit"] = limit
716        join_response = await channel.join(payload)
717        return cls(channel, join_response)
718
719    # Join a user-scoped thread by ID, optionally supplying local tools for a personal thread
720    @staticmethod
721    def topic_user_thread(thread_id: str) -> str:
722        return f"api:chat:user:thread:{thread_id}"
723
724    # Join a user-scoped thread by ID, optionally supplying local tools for a personal thread
725    @classmethod
726    async def join_user_thread(
727        cls,
728        socket: "Socket",
729        thread_id: str,
730        *,
731        after_cursor: str | None = None,
732        before_cursor: str | None = None,
733        include_metadata: bool | None = None,
734        limit: int | None = None,
735        local_tool_provider_id: str | None = None,
736        local_tools: list[dict[str, Any]] | None = None,
737    ) -> "ApiChatChannel":
738        topic = cls.topic_user_thread(thread_id)
739        channel = socket.channel(topic)
740        payload: dict[str, object] = {}
741        if after_cursor is not None:
742            payload["after_cursor"] = after_cursor
743        if before_cursor is not None:
744            payload["before_cursor"] = before_cursor
745        if include_metadata is not None:
746            payload["include_metadata"] = include_metadata
747        if limit is not None:
748            payload["limit"] = limit
749        if local_tool_provider_id is not None:
750            payload["local_tool_provider_id"] = local_tool_provider_id
751        if local_tools is not None:
752            payload["local_tools"] = local_tools
753        join_response = await channel.join(payload)
754        return cls(channel, join_response)
755
756    # Join or create a user-scoped keyed thread
757    @staticmethod
758    def topic_user_keyed(key: str) -> str:
759        return f"api:chat:user:key:{key}"
760
761    # Join or create a user-scoped keyed thread
762    @classmethod
763    async def join_user_keyed(
764        cls,
765        socket: "Socket",
766        key: str,
767        *,
768        after_cursor: str | None = None,
769        before_cursor: str | None = None,
770        include_metadata: bool | None = None,
771        limit: int | None = None,
772    ) -> "ApiChatChannel":
773        topic = cls.topic_user_keyed(key)
774        channel = socket.channel(topic)
775        payload: dict[str, object] = {}
776        if after_cursor is not None:
777            payload["after_cursor"] = after_cursor
778        if before_cursor is not None:
779            payload["before_cursor"] = before_cursor
780        if include_metadata is not None:
781            payload["include_metadata"] = include_metadata
782        if limit is not None:
783            payload["limit"] = limit
784        join_response = await channel.join(payload)
785        return cls(channel, join_response)
786
787    # Join a user-scoped transient (ephemeral) thread
788    @staticmethod
789    def topic_user_transient(key: str) -> str:
790        return f"api:chat:user:transient:{key}"
791
792    # Join a user-scoped transient (ephemeral) thread
793    @classmethod
794    async def join_user_transient(
795        cls,
796        socket: "Socket",
797        key: str,
798        *,
799        after_cursor: str | None = None,
800        before_cursor: str | None = None,
801        include_metadata: bool | None = None,
802        limit: int | None = None,
803    ) -> "ApiChatChannel":
804        topic = cls.topic_user_transient(key)
805        channel = socket.channel(topic)
806        payload: dict[str, object] = {}
807        if after_cursor is not None:
808            payload["after_cursor"] = after_cursor
809        if before_cursor is not None:
810            payload["before_cursor"] = before_cursor
811        if include_metadata is not None:
812            payload["include_metadata"] = include_metadata
813        if limit is not None:
814            payload["limit"] = limit
815        join_response = await channel.join(payload)
816        return cls(channel, join_response)
817
818    # Leave the underlying channel.
819    async def leave(self):
820        await self._channel.leave()
821
822    # Fork a sub-thread from an existing message
823    async def api_chat_fork_thread(self, payload: ApiChatForkThreadInput) -> dict[str, Any]:
824        return await self._channel.push("api:chat:fork_thread", payload)
825
826    # Mark a thread as read up to a given message
827    async def api_chat_mark_thread_read(
828        self, payload: ApiChatMarkThreadReadInput
829    ) -> dict[str, Any]:
830        return await self._channel.push("api:chat:mark_thread_read", payload)
831
832    # List all messages in the current thread
833    async def api_chat_list_messages(self, payload: dict) -> dict[str, Any]:
834        return await self._channel.push("api:chat:list_messages", payload)
835
836    # Load additional messages with cursor-based pagination
837    async def api_chat_load_more_messages(
838        self, payload: ApiChatLoadMoreMessagesInput
839    ) -> dict[str, Any]:
840        return await self._channel.push("api:chat:load_more_messages", payload)
841
842    # Post a new message with optional uploads and reply-to
843    async def api_chat_post_message(self, payload: ApiChatPostMessageInput) -> dict[str, Any]:
844        return await self._channel.push("api:chat:post_message", payload)
845
846    # Post a simple text message
847    async def api_chat_post_simple_message(
848        self, payload: ApiChatPostSimpleMessageInput
849    ) -> dict[str, Any]:
850        return await self._channel.push("api:chat:post_simple_message", payload)
851
852    # Edit an existing message's content
853    async def api_chat_edit_message(self, payload: ApiChatEditMessageInput) -> dict[str, Any]:
854        return await self._channel.push("api:chat:edit_message", payload)
855
856    # Delete a message
857    async def api_chat_delete_message(self, payload: ApiChatDeleteMessageInput) -> dict[str, Any]:
858        return await self._channel.push("api:chat:delete_message", payload)
859
860    # Add an emoji reaction to a message
861    async def api_chat_add_reaction(self, payload: ApiChatAddReactionInput) -> dict[str, Any]:
862        return await self._channel.push("api:chat:add_reaction", payload)
863
864    # Remove an emoji reaction from a message
865    async def api_chat_remove_reaction(self, payload: ApiChatRemoveReactionInput) -> dict[str, Any]:
866        return await self._channel.push("api:chat:remove_reaction", payload)
867
868    # Signal that the current user has started or stopped typing in the thread
869    async def api_chat_typing(self, payload: ApiChatTypingInput) -> dict[str, Any]:
870        return await self._channel.push("api:chat:typing", payload)
871
872    # Broadcast when a new message is added to a thread
873    def on_message_added(
874        self, callback: Callable[[MessageAddedPayload], None]
875    ) -> Callable[[], None]:
876        return self._channel.on("message_added", callback)
877
878    # Broadcast when a message is updated or removed
879    def on_message_updated(
880        self, callback: Callable[[MessageUpdatedPayload], None]
881    ) -> Callable[[], None]:
882        return self._channel.on("message_updated", callback)
883
884    # Broadcast thread-level events (agent updates, read state, unread counts)
885    def on_thread_event(self, callback: Callable[[ThreadEventPayload], None]) -> Callable[[], None]:
886        return self._channel.on("thread_event", callback)
887
888    # Broadcast system-wide events
889    def on_system_event(self, callback: Callable[[SystemEventPayload], None]) -> Callable[[], None]:
890        return self._channel.on("system_event", callback)
891
892    # Broadcast when a participant (human or agent) starts or stops typing. Ephemeral; never persisted.
893    def on_typing(self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]:
894        return self._channel.on("typing", callback)
ApiChatChannel(channel, join_response=None)
619    def __init__(self, channel, join_response=None):
620        self._channel = channel
621        self.join_response = join_response
join_response
@staticmethod
def topic_team_thread(team_id: str, thread_id: str) -> str:
624    @staticmethod
625    def topic_team_thread(team_id: str, thread_id: str) -> str:
626        return f"api:chat:team:{team_id}:thread:{thread_id}"
@classmethod
async def join_team_thread( cls, socket: archastro.phx_channel.Socket, team_id: str, thread_id: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
629    @classmethod
630    async def join_team_thread(
631        cls,
632        socket: "Socket",
633        team_id: str,
634        thread_id: str,
635        *,
636        after_cursor: str | None = None,
637        before_cursor: str | None = None,
638        include_metadata: bool | None = None,
639        limit: int | None = None,
640    ) -> "ApiChatChannel":
641        topic = cls.topic_team_thread(team_id, thread_id)
642        channel = socket.channel(topic)
643        payload: dict[str, object] = {}
644        if after_cursor is not None:
645            payload["after_cursor"] = after_cursor
646        if before_cursor is not None:
647            payload["before_cursor"] = before_cursor
648        if include_metadata is not None:
649            payload["include_metadata"] = include_metadata
650        if limit is not None:
651            payload["limit"] = limit
652        join_response = await channel.join(payload)
653        return cls(channel, join_response)
@staticmethod
def topic_team_keyed(team_id: str, key: str) -> str:
656    @staticmethod
657    def topic_team_keyed(team_id: str, key: str) -> str:
658        return f"api:chat:team:{team_id}:key:{key}"
@classmethod
async def join_team_keyed( cls, socket: archastro.phx_channel.Socket, team_id: str, key: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
661    @classmethod
662    async def join_team_keyed(
663        cls,
664        socket: "Socket",
665        team_id: str,
666        key: str,
667        *,
668        after_cursor: str | None = None,
669        before_cursor: str | None = None,
670        include_metadata: bool | None = None,
671        limit: int | None = None,
672    ) -> "ApiChatChannel":
673        topic = cls.topic_team_keyed(team_id, key)
674        channel = socket.channel(topic)
675        payload: dict[str, object] = {}
676        if after_cursor is not None:
677            payload["after_cursor"] = after_cursor
678        if before_cursor is not None:
679            payload["before_cursor"] = before_cursor
680        if include_metadata is not None:
681            payload["include_metadata"] = include_metadata
682        if limit is not None:
683            payload["limit"] = limit
684        join_response = await channel.join(payload)
685        return cls(channel, join_response)
@staticmethod
def topic_team_transient(team_id: str, key: str) -> str:
688    @staticmethod
689    def topic_team_transient(team_id: str, key: str) -> str:
690        return f"api:chat:team:{team_id}:transient:{key}"
@classmethod
async def join_team_transient( cls, socket: archastro.phx_channel.Socket, team_id: str, key: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
693    @classmethod
694    async def join_team_transient(
695        cls,
696        socket: "Socket",
697        team_id: str,
698        key: str,
699        *,
700        after_cursor: str | None = None,
701        before_cursor: str | None = None,
702        include_metadata: bool | None = None,
703        limit: int | None = None,
704    ) -> "ApiChatChannel":
705        topic = cls.topic_team_transient(team_id, key)
706        channel = socket.channel(topic)
707        payload: dict[str, object] = {}
708        if after_cursor is not None:
709            payload["after_cursor"] = after_cursor
710        if before_cursor is not None:
711            payload["before_cursor"] = before_cursor
712        if include_metadata is not None:
713            payload["include_metadata"] = include_metadata
714        if limit is not None:
715            payload["limit"] = limit
716        join_response = await channel.join(payload)
717        return cls(channel, join_response)
@staticmethod
def topic_user_thread(thread_id: str) -> str:
720    @staticmethod
721    def topic_user_thread(thread_id: str) -> str:
722        return f"api:chat:user:thread:{thread_id}"
@classmethod
async def join_user_thread( cls, socket: archastro.phx_channel.Socket, thread_id: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None, local_tool_provider_id: str | None = None, local_tools: list[dict[str, typing.Any]] | None = None) -> ApiChatChannel:
725    @classmethod
726    async def join_user_thread(
727        cls,
728        socket: "Socket",
729        thread_id: str,
730        *,
731        after_cursor: str | None = None,
732        before_cursor: str | None = None,
733        include_metadata: bool | None = None,
734        limit: int | None = None,
735        local_tool_provider_id: str | None = None,
736        local_tools: list[dict[str, Any]] | None = None,
737    ) -> "ApiChatChannel":
738        topic = cls.topic_user_thread(thread_id)
739        channel = socket.channel(topic)
740        payload: dict[str, object] = {}
741        if after_cursor is not None:
742            payload["after_cursor"] = after_cursor
743        if before_cursor is not None:
744            payload["before_cursor"] = before_cursor
745        if include_metadata is not None:
746            payload["include_metadata"] = include_metadata
747        if limit is not None:
748            payload["limit"] = limit
749        if local_tool_provider_id is not None:
750            payload["local_tool_provider_id"] = local_tool_provider_id
751        if local_tools is not None:
752            payload["local_tools"] = local_tools
753        join_response = await channel.join(payload)
754        return cls(channel, join_response)
@staticmethod
def topic_user_keyed(key: str) -> str:
757    @staticmethod
758    def topic_user_keyed(key: str) -> str:
759        return f"api:chat:user:key:{key}"
@classmethod
async def join_user_keyed( cls, socket: archastro.phx_channel.Socket, key: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
762    @classmethod
763    async def join_user_keyed(
764        cls,
765        socket: "Socket",
766        key: str,
767        *,
768        after_cursor: str | None = None,
769        before_cursor: str | None = None,
770        include_metadata: bool | None = None,
771        limit: int | None = None,
772    ) -> "ApiChatChannel":
773        topic = cls.topic_user_keyed(key)
774        channel = socket.channel(topic)
775        payload: dict[str, object] = {}
776        if after_cursor is not None:
777            payload["after_cursor"] = after_cursor
778        if before_cursor is not None:
779            payload["before_cursor"] = before_cursor
780        if include_metadata is not None:
781            payload["include_metadata"] = include_metadata
782        if limit is not None:
783            payload["limit"] = limit
784        join_response = await channel.join(payload)
785        return cls(channel, join_response)
@staticmethod
def topic_user_transient(key: str) -> str:
788    @staticmethod
789    def topic_user_transient(key: str) -> str:
790        return f"api:chat:user:transient:{key}"
@classmethod
async def join_user_transient( cls, socket: archastro.phx_channel.Socket, key: str, *, after_cursor: str | None = None, before_cursor: str | None = None, include_metadata: bool | None = None, limit: int | None = None) -> ApiChatChannel:
793    @classmethod
794    async def join_user_transient(
795        cls,
796        socket: "Socket",
797        key: str,
798        *,
799        after_cursor: str | None = None,
800        before_cursor: str | None = None,
801        include_metadata: bool | None = None,
802        limit: int | None = None,
803    ) -> "ApiChatChannel":
804        topic = cls.topic_user_transient(key)
805        channel = socket.channel(topic)
806        payload: dict[str, object] = {}
807        if after_cursor is not None:
808            payload["after_cursor"] = after_cursor
809        if before_cursor is not None:
810            payload["before_cursor"] = before_cursor
811        if include_metadata is not None:
812            payload["include_metadata"] = include_metadata
813        if limit is not None:
814            payload["limit"] = limit
815        join_response = await channel.join(payload)
816        return cls(channel, join_response)
async def leave(self):
819    async def leave(self):
820        await self._channel.leave()
async def api_chat_fork_thread( self, payload: ApiChatForkThreadInput) -> dict[str, typing.Any]:
823    async def api_chat_fork_thread(self, payload: ApiChatForkThreadInput) -> dict[str, Any]:
824        return await self._channel.push("api:chat:fork_thread", payload)
async def api_chat_mark_thread_read( self, payload: ApiChatMarkThreadReadInput) -> dict[str, typing.Any]:
827    async def api_chat_mark_thread_read(
828        self, payload: ApiChatMarkThreadReadInput
829    ) -> dict[str, Any]:
830        return await self._channel.push("api:chat:mark_thread_read", payload)
async def api_chat_list_messages(self, payload: dict) -> dict[str, typing.Any]:
833    async def api_chat_list_messages(self, payload: dict) -> dict[str, Any]:
834        return await self._channel.push("api:chat:list_messages", payload)
async def api_chat_load_more_messages( self, payload: ApiChatLoadMoreMessagesInput) -> dict[str, typing.Any]:
837    async def api_chat_load_more_messages(
838        self, payload: ApiChatLoadMoreMessagesInput
839    ) -> dict[str, Any]:
840        return await self._channel.push("api:chat:load_more_messages", payload)
async def api_chat_post_message( self, payload: ApiChatPostMessageInput) -> dict[str, typing.Any]:
843    async def api_chat_post_message(self, payload: ApiChatPostMessageInput) -> dict[str, Any]:
844        return await self._channel.push("api:chat:post_message", payload)
async def api_chat_post_simple_message( self, payload: ApiChatPostSimpleMessageInput) -> dict[str, typing.Any]:
847    async def api_chat_post_simple_message(
848        self, payload: ApiChatPostSimpleMessageInput
849    ) -> dict[str, Any]:
850        return await self._channel.push("api:chat:post_simple_message", payload)
async def api_chat_edit_message( self, payload: ApiChatEditMessageInput) -> dict[str, typing.Any]:
853    async def api_chat_edit_message(self, payload: ApiChatEditMessageInput) -> dict[str, Any]:
854        return await self._channel.push("api:chat:edit_message", payload)
async def api_chat_delete_message( self, payload: ApiChatDeleteMessageInput) -> dict[str, typing.Any]:
857    async def api_chat_delete_message(self, payload: ApiChatDeleteMessageInput) -> dict[str, Any]:
858        return await self._channel.push("api:chat:delete_message", payload)
async def api_chat_add_reaction( self, payload: ApiChatAddReactionInput) -> dict[str, typing.Any]:
861    async def api_chat_add_reaction(self, payload: ApiChatAddReactionInput) -> dict[str, Any]:
862        return await self._channel.push("api:chat:add_reaction", payload)
async def api_chat_remove_reaction( self, payload: ApiChatRemoveReactionInput) -> dict[str, typing.Any]:
865    async def api_chat_remove_reaction(self, payload: ApiChatRemoveReactionInput) -> dict[str, Any]:
866        return await self._channel.push("api:chat:remove_reaction", payload)
async def api_chat_typing( self, payload: ApiChatTypingInput) -> dict[str, typing.Any]:
869    async def api_chat_typing(self, payload: ApiChatTypingInput) -> dict[str, Any]:
870        return await self._channel.push("api:chat:typing", payload)
def on_message_added( self, callback: Callable[[MessageAddedPayload], None]) -> Callable[[], None]:
873    def on_message_added(
874        self, callback: Callable[[MessageAddedPayload], None]
875    ) -> Callable[[], None]:
876        return self._channel.on("message_added", callback)
def on_message_updated( self, callback: Callable[[MessageUpdatedPayload], None]) -> Callable[[], None]:
879    def on_message_updated(
880        self, callback: Callable[[MessageUpdatedPayload], None]
881    ) -> Callable[[], None]:
882        return self._channel.on("message_updated", callback)
def on_thread_event( self, callback: Callable[[ThreadEventPayload], None]) -> Callable[[], None]:
885    def on_thread_event(self, callback: Callable[[ThreadEventPayload], None]) -> Callable[[], None]:
886        return self._channel.on("thread_event", callback)
def on_system_event( self, callback: Callable[[SystemEventPayload], None]) -> Callable[[], None]:
889    def on_system_event(self, callback: Callable[[SystemEventPayload], None]) -> Callable[[], None]:
890        return self._channel.on("system_event", callback)
def on_typing( self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]:
893    def on_typing(self, callback: Callable[[TypingPayload], None]) -> Callable[[], None]:
894        return self._channel.on("typing", callback)