Skip to main content
Version: 0.14.0 (Latest)

๐Ÿ›ฃ๏ธ Chat routes

Chat routes are SDK composers, not Express middleware. The inventory and details below document the exact public HTTP and WebSocket endpoints.

Inventoryโ€‹

RouteMethod / protocolPathSchemaValidatorsSuccess status
createChatChannelRoutePOST / HTTP/channelscreateChatChannelSchemaisAuthenticated(), some( checkIdentityType(['admin']), isSelf(['params', 'requestBody', 'ownerId']) )201
findChatChannelsRouteGET / HTTP/channelsfindChatChannelsSchemaisAuthenticated(), some( checkIdentityType(['admin']), isSelf(['params', 'requestQuery', 'ownerId']) )200
getChatChannelRouteGET / HTTP/channels/:channelIdgetChatChannelSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsChannel(['params', 'requestParams', 'channelId']), hasSubscription(['params', 'requestParams', 'channelId']) )200
updateChatChannelRoutePATCH / HTTP/channels/:channelIdupdateChatChannelSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsChannel(['params', 'requestParams', 'channelId']) )200
deleteChatChannelRouteDELETE / HTTP/channels/:channelIddeleteChatChannelSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsChannel(['params', 'requestParams', 'channelId']) )204
getChannelMessagesRouteGET / HTTP/channels/:channelId/messagesgetChannelMessagesSchemaisAuthenticated(), channelExists(['params', 'requestParams', 'channelId']), hasSubscription(['params', 'requestParams', 'channelId'])200
getChatChannelIconUploadUrlRouteGET / HTTP/channels/:channelId/icon-upload-urlgetSignedImageUploadUrlSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsChannel(['params', 'requestParams', 'channelId']) )200
createChatMessageRoutePOST / HTTP/messagescreateChatMessageSchemaisAuthenticated(), isSelf(['params', 'requestBody', 'senderId']), hasSubscription(['params', 'requestBody', 'channelId'])201
findChatMessagesRouteGET / HTTP/messagesfindChatMessagesSchemaisAuthenticated(), some( checkIdentityType(['admin']), hasSubscription(['params', 'requestQuery', 'channelId']), isSelf(['params', 'requestQuery', 'senderId']) )200
getChatMessageRouteGET / HTTP/messages/:messageIdgetChatMessageSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsMessage(['params', 'requestParams', 'messageId']) )200
updateChatMessageRoutePATCH / HTTP/messages/:messageIdupdateChatMessageSchemaisAuthenticated(), ownsMessage(['params', 'requestParams', 'messageId'])200
deleteChatMessageRouteDELETE / HTTP/messages/:messageIddeleteChatMessageSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsMessage(['params', 'requestParams', 'messageId']) )204
getChatMessageAttachmentUploadUrlRouteGET / HTTP/messages/:messageId/attachment-upload-urlgetSignedFileUploadUrlSchemaisAuthenticated(), ownsMessage(['params', 'requestParams', 'messageId'])200
streamChatMessagesRouteWS / WebSocket/messages/listenstreamChatMessagesSchemaNoneMessage stream
createChatMessageAttachmentRoutePOST / HTTP/messages/:messageId/attachmentscreateChatMessageAttachmentSchemaisAuthenticated(), ownsMessage(['params', 'requestParams', 'messageId'])201
deleteChatMessageAttachmentRouteDELETE / HTTP/messages/:messageId/attachments/:attachmentIddeleteChatMessageAttachmentSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsMessage(['params', 'requestParams', 'messageId']) )204
createChatMessageTemplateRoutePOST / HTTP/message-templatescreateChatMessageTemplateSchemaisAuthenticated(), some( checkIdentityType(['admin']), hasOrgRole( ['owner', 'admin'], ['params', 'requestBody', 'organizationId'] ) )201
getChatMessageTemplateRouteGET / HTTP/message-templates/:messageTemplateIdgetChatMessageTemplateSchemaisAuthenticated(), some( checkIdentityType(['admin']), hasOrganizationAccessToMessageTemplate( ['owner', 'admin'], ['params', 'requestParams', 'messageTemplateId'] ) )200
updateChatMessageTemplateRoutePATCH / HTTP/message-templates/:messageTemplateIdupdateChatMessageTemplateSchemaisAuthenticated(), some( checkIdentityType(['admin']), hasOrganizationAccessToMessageTemplate( ['owner', 'admin'], ['params', 'requestParams', 'messageTemplateId'] ) )200
deleteChatMessageTemplateRouteDELETE / HTTP/message-templates/:messageTemplateIddeleteChatMessageTemplateSchemaisAuthenticated(), some( checkIdentityType(['admin']), hasOrganizationAccessToMessageTemplate( ['owner', 'admin'], ['params', 'requestParams', 'messageTemplateId'] ) )204
findChatMessageTemplatesRouteGET / HTTP/message-templatesfindChatMessageTemplatesSchemaisAuthenticated(), checkIdentityType(['admin'])200
findChatMessageTemplatesForOrganizationRouteGET / HTTP/organizations/:organizationId/message-templatesfindChatMessageTemplatesForOrganizationSchemaisAuthenticated(), hasOrgRole( ['owner', 'admin'], ['params', 'requestParams', 'organizationId'] )200
upsertChatChannelReadStateRoutePUT / HTTP/channels/:channelId/read-stateupsertChatChannelReadStateSchemaisAuthenticated(), channelExists(['params', 'requestParams', 'channelId']), hasSubscription(['params', 'requestParams', 'channelId']), isSelf(['params', 'requestBody', 'identityId'])204
createChatSubscriptionRoutePOST / HTTP/subscriptionscreateChatSubscriptionSchemaisAuthenticated(), some( checkIdentityType(['admin']), isSelf(['params', 'requestBody', 'subscribedId']) )201
findChatSubscriptionsRouteGET / HTTP/subscriptionsfindChatSubscriptionsSchemaisAuthenticated(), some( checkIdentityType(['admin']), hasSubscription(['params', 'requestQuery', 'channelId']), ownsChannel(['params', 'requestQuery', 'channelId']), isSelf(['params', 'requestQuery', 'subscribedId']) )200
getChatSubscriptionRouteGET / HTTP/subscriptions/:subscriptionIdgetChatSubscriptionSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsSubscription(['params', 'requestParams', 'subscriptionId']) )200
deleteChatSubscriptionRouteDELETE / HTTP/subscriptions/:subscriptionIddeleteChatSubscriptionSchemaisAuthenticated(), some( checkIdentityType(['admin']), ownsSubscription(['params', 'requestParams', 'subscriptionId']) )204

Detailsโ€‹

createChatChannelRouteโ€‹

Implementation

Endpoint: POST /channels

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ isSelf(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), isSelf(['params', 'requestBody', 'ownerId']) ),.

Request: createChatChannelSchema validates the application/json body: Required name and ownerId; optional nullable icon.

Pipeline: createChatChannel โ†’ getChatChannelById โ†’ createChatChannelTerminator.

Success: 201 with the normalized channel in the response descriptorโ€™s data.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. Pipeline failures follow the linked handler/block contracts and shared error middleware; this route adds no separate inline error mapping.

View complete source
export const createChatChannelRoute = withRoute({
handler: compose(
withLogging(createChatChannel),
flatMapAsync(withLogging(getChatChannelById)),
lift(withLogging(createChatChannelTerminator))
),
method: 'POST',
path: '/channels',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
isSelf(['params', 'requestBody', 'ownerId'])
),
],
});

findChatChannelsRouteโ€‹

Implementation

Endpoint: GET /channels

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ isSelf(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), isSelf(['params', 'requestQuery', 'ownerId']) ),.

Request: findChatChannelsSchema validates query parameters: Optional name, ownerId, and shared pagination fields.

Pipeline: findChatChannels โ†’ normalizeChatChannelsListTerminator.

Success: 200 with normalized channels and pagination metadata.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. Pipeline failures follow the linked handler/block contracts and shared error middleware; this route adds no separate inline error mapping.

View complete source
export const findChatChannelsRoute = withRoute({
handler: compose(
withPagination(withLogging(findChatChannels)),
lift(withLogging(normalizeChatChannelsListTerminator))
),
method: 'GET',
path: '/channels',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
isSelf(['params', 'requestQuery', 'ownerId'])
),
],
});

getChatChannelRouteโ€‹

Implementation

Endpoint: GET /channels/:channelId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ ownsChannel(...) โ†’ hasSubscription(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), ownsChannel(['params', 'requestParams', 'channelId']), hasSubscription(['params', 'requestParams', 'channelId']) ),.

Request: getChatChannelSchema validates path parameters: Required channelId.

Pipeline: getChatChannelById โ†’ normalizeChatChannelTerminator.

Success: 200 with the normalized channel.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. Pipeline failures follow the linked handler/block contracts and shared error middleware; this route adds no separate inline error mapping.

View complete source
export const getChatChannelRoute = withRoute({
handler: compose(
withLogging(getChatChannelById),
lift(withLogging(normalizeChatChannelTerminator))
),
method: 'GET',
path: '/channels/:channelId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsChannel(['params', 'requestParams', 'channelId']),
hasSubscription(['params', 'requestParams', 'channelId'])
),
],
});

updateChatChannelRouteโ€‹

Implementation

Endpoint: PATCH /channels/:channelId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ ownsChannel(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), ownsChannel(['params', 'requestParams', 'channelId']) ),.

Request: updateChatChannelSchema validates path parameters and the application/json body: Required channelId; body permits optional name and nullable icon.

Pipeline: updateChatChannel โ†’ getChatChannelById โ†’ normalizeChatChannelTerminator.

Success: 200 with the updated, normalized channel.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. Pipeline failures follow the linked handler/block contracts and shared error middleware; this route adds no separate inline error mapping.

View complete source
export const updateChatChannelRoute = withRoute({
handler: compose(
withLogging(updateChatChannel),
flatMapAsync(withLogging(getChatChannelById)),
lift(withLogging(normalizeChatChannelTerminator))
),
method: 'PATCH',
path: '/channels/:channelId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsChannel(['params', 'requestParams', 'channelId'])
),
],
});

deleteChatChannelRouteโ€‹

Implementation

Endpoint: DELETE /channels/:channelId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ ownsChannel(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), ownsChannel(['params', 'requestParams', 'channelId']) ),.

Request: deleteChatChannelSchema validates path parameters: Required channelId.

Pipeline: deleteChatChannel โ†’ deleteChatChannelTerminator.

Success: Empty 204 response.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. Pipeline failures follow the linked handler/block contracts and shared error middleware; this route adds no separate inline error mapping.

View complete source
export const deleteChatChannelRoute = withRoute({
handler: compose(
withLogging(deleteChatChannel),
lift(withLogging(deleteChatChannelTerminator))
),
method: 'DELETE',
path: '/channels/:channelId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsChannel(['params', 'requestParams', 'channelId'])
),
],
});

getChannelMessagesRouteโ€‹

Implementation

Endpoint: GET /channels/:channelId/messages

Access: isAuthenticated() โ†’ hasSubscription(...) โ†’ channelExists(...) execute in the source order shown here. Exact invocation: isAuthenticated(), channelExists(['params', 'requestParams', 'channelId']), hasSubscription(['params', 'requestParams', 'channelId']),.

Request: getChannelMessagesSchema validates path and query parameters: Required channelId and optional shared pagination fields.

Pipeline: getChannelMessagesByChannelId โ†’ pagination โ†’ normalizeChatMessages โ†’ response projection.

Success: 200 with normalized messages and pagination metadata.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatChannelUnknownError โ†’ 500 as shown in its source.

View complete source
export const getChannelMessagesRoute = withRoute({
handler: compose(
withPagination(
withLogging(
applyPayloadArgs(
getChannelMessagesByChannelId,
[
['context', 'db', 'chatMessages'],
['params', 'requestParams', 'channelId'],
],
'paginatedMessages'
)
)
),
flatMapAsync(
applyPayloadArgs(
normalizeChatMessages,
[
['context', 'fileStorageDriver'],
['context', 'data', 'paginatedMessages', 'data'],
],
'normalizedMessages'
)
),
flatMapAsync(
applyPayloadArgs(
applySpec({ data: nthArg(0), metadata: { pagination: nthArg(1) } }),
[
['context', 'data', 'normalizedMessages'],
['context', 'data', 'paginatedMessages', 'metadata'],
],
'normalizedMessagesWithPagination'
)
),
lift(
orThrow(
[[ChatChannelUnknownError, 500]],
[['context', 'data', 'normalizedMessagesWithPagination'], 200]
)
)
),
method: 'GET',
path: '/channels/:channelId/messages',
validators: [
isAuthenticated(),
channelExists(['params', 'requestParams', 'channelId']),
hasSubscription(['params', 'requestParams', 'channelId']),
],
});

getChatChannelIconUploadUrlRouteโ€‹

Implementation

Endpoint: GET /channels/:channelId/icon-upload-url

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ ownsChannel(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), ownsChannel(['params', 'requestParams', 'channelId']) ),.

Request: The shared getSignedImageUploadUrlSchema validates the path channelId and the signed-image upload request contract.

Pipeline: generateChatChannelIconUploadUrl โ†’ orThrow.

Success: 200 with { objectId, url }.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps FileStorageServiceError โ†’ 500, ChatChannelUnknownError โ†’ 500 as shown in its source.

View complete source
export const getChatChannelIconUploadUrlRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
generateChatChannelIconUploadUrl,
[
['context', 'fileStorageDriver'],
['params', 'requestQuery', 'contentType'],
['params', 'requestQuery', 'contentLength'],
],
'iconUploadUrlResult'
)
),
lift(
orThrow(
[
[FileStorageServiceError, 500],
[ChatChannelUnknownError, 500],
],
[['context', 'data', 'iconUploadUrlResult'], 200]
)
)
),
method: 'GET',
path: '/channels/:channelId/icon-upload-url',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsChannel(['params', 'requestParams', 'channelId'])
),
],
});

createChatMessageRouteโ€‹

Implementation

Endpoint: POST /messages

Access: isAuthenticated() โ†’ isSelf(...) โ†’ hasSubscription(...) execute in the source order shown here. Exact invocation: isAuthenticated(), isSelf(['params', 'requestBody', 'senderId']), hasSubscription(['params', 'requestBody', 'channelId']),.

Request: createChatMessageSchema validates the application/json body: Required content, senderId, and channelId; optional title.

Pipeline: createChatMessage โ†’ getChatMessageById โ†’ normalizeChatMessage โ†’ orThrow.

Success: 201 with the normalized message and signed attachment URLs.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageNotFoundBlockError โ†’ 404, FileStorageServiceError โ†’ 500, ChatMessageUnexpectedDBError โ†’ 500 as shown in its source.

View complete source
export const createChatMessageRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
createChatMessage,
[
['context', 'db', 'chatMessages'],
['params', 'requestBody'],
],
'messageId'
)
),

flatMapAsync(
applyPayloadArgs(
getChatMessageByIdBlock,
[
['context', 'db', 'chatMessages'],
['context', 'data', 'messageId'],
],
'message'
)
),

flatMapAsync(
applyPayloadArgs(
normalizeChatMessage,
[
['context', 'fileStorageDriver'],
['context', 'data', 'message'],
],
'normalizedMessage'
)
),

lift(
orThrow(
[
[ChatMessageNotFoundBlockError, 404],
[FileStorageServiceError, 500],
[ChatMessageUnexpectedDBError, 500],
],
[['context', 'data', 'normalizedMessage'], 201]
)
)
),
method: 'POST',
path: '/messages',
validators: [
isAuthenticated(),
isSelf(['params', 'requestBody', 'senderId']),
hasSubscription(['params', 'requestBody', 'channelId']),
],
});

findChatMessagesRouteโ€‹

Implementation

Endpoint: GET /messages

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ isSelf(...) โ†’ hasSubscription(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), hasSubscription(['params', 'requestQuery', 'channelId']), isSelf(['params', 'requestQuery', 'senderId']) ),.

Request: findChatMessagesSchema validates query parameters: Required channelId; optional message filters and pagination.

Pipeline: findChatMessages.

Success: 200 with normalized messages and pagination metadata.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageNotFoundBlockError โ†’ 404, FileStorageServiceError โ†’ 500, ChatMessageBlockError โ†’ 500 as shown in its source.

View complete source
export const findChatMessagesRoute = withRoute({
handler: compose(
withPagination(
withLogging(
applyPayloadArgs(
findChatMessages,
[
['context', 'db', 'chatMessages'],
['params', 'requestQuery'],
],
'paginatedMessages'
)
)
),
flatMapAsync(
applyPayloadArgs(
normalizeChatMessages,
[
['context', 'fileStorageDriver'],
['context', 'data', 'paginatedMessages', 'data'],
],
'normalizedMessages'
)
),
flatMapAsync(
applyPayloadArgs(
applySpec({ data: nthArg(0), metadata: { pagination: nthArg(1) } }),
[
['context', 'data', 'normalizedMessages'],
['context', 'data', 'paginatedMessages', 'metadata'],
],
'normalizedMessagesWithPagination'
)
),
lift(
orThrow(
[
[ChatMessageNotFoundBlockError, 404],
[FileStorageServiceError, 500],
[ChatMessageBlockError, 500],
],
[['context', 'data', 'normalizedMessagesWithPagination']]
)
)
),
method: 'GET',
path: '/messages',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
hasSubscription(['params', 'requestQuery', 'channelId']),
isSelf(['params', 'requestQuery', 'senderId'])
),
],
});

getChatMessageRouteโ€‹

Implementation

Endpoint: GET /messages/:messageId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ ownsMessage(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), ownsMessage(['params', 'requestParams', 'messageId']) ),.

Request: getChatMessageSchema validates path parameters: Required messageId.

Pipeline: getChatMessageById โ†’ normalizeChatMessage โ†’ orThrow.

Success: 200 with the normalized message.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageNotFoundBlockError โ†’ 404, FileStorageServiceError โ†’ 500, ChatMessageBlockError โ†’ 500 as shown in its source.

View complete source
export const getChatMessageRoute = withRoute({
handler: compose(
applyPayloadArgs(
getChatMessageByIdBlock,
[
['context', 'db', 'chatMessages'],
['params', 'requestParams', 'messageId'],
],
'message'
),

flatMapAsync(
applyPayloadArgs(
normalizeChatMessage,
[
['context', 'fileStorageDriver'],
['context', 'data', 'message'],
],
'normalizedMessage'
)
),

lift(
orThrow(
[
[ChatMessageNotFoundBlockError, 404],
[FileStorageServiceError, 500],
[ChatMessageBlockError, 500],
],
[['context', 'data', 'normalizedMessage']]
)
)
),
method: 'GET',
path: '/messages/:messageId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsMessage(['params', 'requestParams', 'messageId'])
),
],
});

updateChatMessageRouteโ€‹

Implementation

Endpoint: PATCH /messages/:messageId

Access: isAuthenticated() โ†’ ownsMessage(...) execute in the source order shown here. Exact invocation: isAuthenticated(), ownsMessage(['params', 'requestParams', 'messageId']),.

Request: updateChatMessageSchema validates path parameters and the application/json body: Required messageId; optional content, senderId, and title.

Pipeline: updateChatMessage โ†’ getChatMessageById.

Success: 200 with the updated, normalized message.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageNotFoundBlockError โ†’ 404, FileStorageServiceError โ†’ 500, ChatMessageBlockError โ†’ 500 as shown in its source.

View complete source
export const updateChatMessageRoute = withRoute({
handler: compose(
withLogging(updateChatMessage),
flatMapAsync(withLogging(getChatMessageById)),

flatMapAsync(
applyPayloadArgs(
normalizeChatMessage,
[
['context', 'fileStorageDriver'],
['context', 'data', 'chatMessage'],
],
'normalizedMessage'
)
),

lift(
orThrow(
[
[ChatMessageNotFoundBlockError, 404],
[FileStorageServiceError, 500],
[ChatMessageBlockError, 500],
],
[['context', 'data', 'normalizedMessage'], 200]
)
)
),
method: 'PATCH',
path: '/messages/:messageId',
validators: [
isAuthenticated(),
ownsMessage(['params', 'requestParams', 'messageId']),
],
});

deleteChatMessageRouteโ€‹

Implementation

Endpoint: DELETE /messages/:messageId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ ownsMessage(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), ownsMessage(['params', 'requestParams', 'messageId']) ),.

Request: deleteChatMessageSchema validates path parameters: Required messageId.

Pipeline: deleteChatMessage โ†’ deleteChatMessageTerminator.

Success: Empty 204 response.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. Pipeline failures follow the linked handler/block contracts and shared error middleware; this route adds no separate inline error mapping.

View complete source
export const deleteChatMessageRoute = withRoute({
handler: compose(
withLogging(deleteChatMessage),
lift(withLogging(deleteChatMessageTerminator))
),
method: 'DELETE',
path: '/messages/:messageId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsMessage(['params', 'requestParams', 'messageId'])
),
],
});

getChatMessageAttachmentUploadUrlRouteโ€‹

Implementation

Endpoint: GET /messages/:messageId/attachment-upload-url

Access: isAuthenticated() โ†’ ownsMessage(...) execute in the source order shown here. Exact invocation: isAuthenticated(), ownsMessage(['params', 'requestParams', 'messageId']),.

Request: The shared getSignedFileUploadUrlSchema validates the path messageId and the signed-file upload request contract.

Pipeline: generateChatMessageAttachmentUploadUrl โ†’ orThrow.

Success: 200 with { objectId, url }.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps FileStorageServiceError โ†’ 500, ChatMessageBlockError โ†’ 500 as shown in its source.

View complete source
export const getChatMessageAttachmentUploadUrlRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
generateChatMessageAttachmentUploadUrl,
[
['context', 'fileStorageDriver'],
['params', 'requestQuery', 'contentType'],
['params', 'requestQuery', 'contentLength'],
],
'attachmentUploadUrlResult'
)
),
lift(
orThrow(
[
[FileStorageServiceError, 500],
[ChatMessageBlockError, 500],
],
[['context', 'data', 'attachmentUploadUrlResult'], 200]
)
)
),
method: 'GET',
path: '/messages/:messageId/attachment-upload-url',
validators: [
isAuthenticated(),
ownsMessage(['params', 'requestParams', 'messageId']),
],
});

streamChatMessagesRouteโ€‹

Implementation

Endpoint: ws /messages/listen

Access: No active validator. The source comments out isAuthenticated() and hasSubscription(...), so the current WebSocket endpoint does not enforce Bearer/cookie authentication or channel membership.

Request: streamChatMessagesSchema validates query parameters: Required channelId.

Pipeline: streamChatMessages โ†’ normalizeChatMessageStream โ†’ orThrow, producing the WebSocket subject.

Success: A WebSocket subject that emits normalized inserted messages and serializes each value as JSON.

Failure: No access validator runs. Pipeline mappings are ChatMessageBadRequestError โ†’ 400, ChatMessageUnknownError โ†’ 500; subject/database failures can also terminate the WebSocket stream.

View complete source
export const streamChatMessagesRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
streamChatMessages,
[
['context', 'db', 'chatMessages'],
['params', 'requestQuery', 'channelId'],
],
'streamSubject'
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
normalizeChatMessageStream,
[
['context', 'fileStorageDriver'],
['context', 'data', 'streamSubject'],
],
'normalizedStreamSubject'
)
)
),
lift(
withLogging(
orThrow(
[
[ChatMessageBadRequestError, 400],
[ChatMessageUnknownError, 500],
],
[['context', 'data', 'normalizedStreamSubject']]
)
)
)
),
path: '/messages/listen',
protocol: 'ws',
validators: [
// @TODO: Websockets have different IP address format than the regular HTTP requests. So once that is fixed, we can uncomment this.
// isAuthenticated(),
// hasSubscription(['params', 'requestQuery', 'channelId']),
],
});

createChatMessageAttachmentRouteโ€‹

Implementation

Endpoint: POST /messages/:messageId/attachments

Access: isAuthenticated() โ†’ ownsMessage(...) execute in the source order shown here. Exact invocation: isAuthenticated(), ownsMessage(['params', 'requestParams', 'messageId']),.

Request: createChatMessageAttachmentSchema validates the application/json body: Required UUID objectId and string type.

Pipeline: createChatMessageAttachment โ†’ getChatMessageAttachmentById โ†’ normalizeChatMessageAttachment โ†’ orThrow.

Success: 201 with the normalized attachment { type, url }.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageNotFoundBlockError โ†’ 404, ChatMessageAttachmentNotFoundBlockError โ†’ 404, ChatMessageUnexpectedDBError โ†’ 500 as shown in its source.

View complete source
export const createChatMessageAttachmentRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
createChatMessageAttachment,
[
['context', 'db', 'chatMessages'],
['params', 'requestParams', 'messageId'],
['params', 'requestBody'],
],
'messageAttachmentId'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getChatMessageAttachmentById,
[
['context', 'db', 'chatMessages'],
['params', 'requestParams', 'messageId'],
['context', 'data', 'messageAttachmentId'],
],
'messageAttachment'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
normalizeChatMessageAttachment,
[
['context', 'fileStorageDriver'],
['context', 'data', 'messageAttachment'],
],
'normalizedMessageAttachment'
)
)
),

lift(
orThrow(
[
[ChatMessageNotFoundBlockError, 404],
[ChatMessageAttachmentNotFoundBlockError, 404],
[ChatMessageUnexpectedDBError, 500],
],
[['context', 'data', 'normalizedMessageAttachment'], 201]
)
)
),
method: 'POST',
path: '/messages/:messageId/attachments',
validators: [
isAuthenticated(),
ownsMessage(['params', 'requestParams', 'messageId']),
],
});

deleteChatMessageAttachmentRouteโ€‹

Implementation

Endpoint: DELETE /messages/:messageId/attachments/:attachmentId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ ownsMessage(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), ownsMessage(['params', 'requestParams', 'messageId']) ),.

Request: deleteChatMessageAttachmentSchema validates path parameters: Required messageId and attachmentId.

Pipeline: deleteChatMessageAttachment โ†’ orThrow.

Success: Empty 204 response after metadata and stored file deletion.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageNotFoundBlockError โ†’ 404, ChatMessageAttachmentNotFoundBlockError โ†’ 404, ChatMessageUnexpectedDBError โ†’ 500, ChatMessageBlockError โ†’ 500, FileStorageServiceError โ†’ 500 as shown in its source.

View complete source
export const deleteChatMessageAttachmentRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
deleteChatMessageAttachment,
[
['context', 'fileStorageDriver'],
['context', 'db', 'chatMessages'],
['params', 'requestParams', 'messageId'],
['params', 'requestParams', 'attachmentId'],
],
'messageAttachmentDeleted'
)
),

lift(
orThrow(
[
[ChatMessageNotFoundBlockError, 404],
[ChatMessageAttachmentNotFoundBlockError, 404],
[ChatMessageUnexpectedDBError, 500],
[ChatMessageBlockError, 500],
[FileStorageServiceError, 500],
],
[['context', 'data', 'messageAttachmentDeleted'], 204]
)
)
),
method: 'DELETE',
path: '/messages/:messageId/attachments/:attachmentId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsMessage(['params', 'requestParams', 'messageId'])
),
],
});

createChatMessageTemplateRouteโ€‹

Implementation

Endpoint: POST /message-templates

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ hasOrgRole(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), hasOrgRole( ['owner', 'admin'], ['params', 'requestBody', 'organizationId'] ) ),.

Request: createChatMessageTemplateSchema validates the application/json body: Required content and title; optional organizationId.

Pipeline: createChatMessageTemplate โ†’ getChatMessageTemplateById โ†’ orThrow.

Success: 201 with the created template, excluding MongoDB _id.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageTemplateNotFoundError โ†’ 404, ChatMessageTemplateInternalError โ†’ 500, ChatMessageTemplateDbError โ†’ 500 as shown in its source.

View complete source
export const createChatMessageTemplateRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
createChatMessageTemplate,
[
['context', 'db', 'chatMessageTemplates'],
['params', 'requestBody'],
],
'messageTemplateId'
)
),
flatMapAsync(
applyPayloadArgs(
getChatMessageTemplateById,
[
['context', 'db', 'chatMessageTemplates'],
['context', 'data', 'messageTemplateId'],
],
'messageTemplate'
)
),
lift(
withLogging(
orThrow(
[
[ChatMessageTemplateNotFoundError, 404],
[ChatMessageTemplateInternalError, 500],
[ChatMessageTemplateDbError, 500],
],
[['context', 'data', 'messageTemplate'], 201]
)
)
)
),
method: 'POST',
path: '/message-templates',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
hasOrgRole(
['owner', 'admin'],
['params', 'requestBody', 'organizationId']
)
),
],
});

getChatMessageTemplateRouteโ€‹

Implementation

Endpoint: GET /message-templates/:messageTemplateId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ hasOrganizationAccessToMessageTemplate(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), hasOrganizationAccessToMessageTemplate( ['owner', 'admin'], ['params', 'requestParams', 'messageTemplateId'] ) ),.

Request: getChatMessageTemplateSchema validates path parameters: Required messageTemplateId.

Pipeline: getChatMessageTemplateById โ†’ orThrow.

Success: 200 with the template, excluding MongoDB _id.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageTemplateNotFoundError โ†’ 404, ChatMessageTemplateUnauthorizedError โ†’ 403, ChatMessageTemplateInternalError โ†’ 500, ChatMessageTemplateDbError โ†’ 500 as shown in its source.

View complete source
export const getChatMessageTemplateRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getChatMessageTemplateById,
[
['context', 'db', 'chatMessageTemplates'],
['params', 'requestParams', 'messageTemplateId'],
],
'messageTemplate'
)
),
lift(
withLogging(
orThrow(
[
[ChatMessageTemplateNotFoundError, 404],
[ChatMessageTemplateUnauthorizedError, 403],
[ChatMessageTemplateInternalError, 500],
[ChatMessageTemplateDbError, 500],
],
[['context', 'data', 'messageTemplate']]
)
)
)
),
method: 'GET',
path: '/message-templates/:messageTemplateId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
hasOrganizationAccessToMessageTemplate(
['owner', 'admin'],
['params', 'requestParams', 'messageTemplateId']
)
),
],
});

updateChatMessageTemplateRouteโ€‹

Implementation

Endpoint: PATCH /message-templates/:messageTemplateId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ hasOrganizationAccessToMessageTemplate(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), hasOrganizationAccessToMessageTemplate( ['owner', 'admin'], ['params', 'requestParams', 'messageTemplateId'] ) ),.

Request: updateChatMessageTemplateSchema validates path parameters and the application/json body: Required template ID; optional content and title.

Pipeline: updateChatMessageTemplate โ†’ getChatMessageTemplateById โ†’ orThrow.

Success: 200 with the updated template, excluding MongoDB _id.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageTemplateNotFoundError โ†’ 404, ChatMessageTemplateUnauthorizedError โ†’ 403, ChatMessageTemplateInternalError โ†’ 500, ChatMessageTemplateDbError โ†’ 500 as shown in its source.

View complete source
export const updateChatMessageTemplateRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
updateChatMessageTemplate,
[
['context', 'db', 'chatMessageTemplates'],
['params', 'requestParams', 'messageTemplateId'],
['params', 'requestBody'],
],
'hasUpdatedMessageTemplate'
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
getChatMessageTemplateById,
[
['context', 'db', 'chatMessageTemplates'],
['params', 'requestParams', 'messageTemplateId'],
],
'messageTemplate'
)
)
),
lift(
withLogging(
orThrow(
[
[ChatMessageTemplateNotFoundError, 404],
[ChatMessageTemplateUnauthorizedError, 403],
[ChatMessageTemplateInternalError, 500],
[ChatMessageTemplateDbError, 500],
],
[['context', 'data', 'messageTemplate'], 200]
)
)
)
),
method: 'PATCH',
path: '/message-templates/:messageTemplateId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
hasOrganizationAccessToMessageTemplate(
['owner', 'admin'],
['params', 'requestParams', 'messageTemplateId']
)
),
],
});

deleteChatMessageTemplateRouteโ€‹

Implementation

Endpoint: DELETE /message-templates/:messageTemplateId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ hasOrganizationAccessToMessageTemplate(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), hasOrganizationAccessToMessageTemplate( ['owner', 'admin'], ['params', 'requestParams', 'messageTemplateId'] ) ),.

Request: deleteChatMessageTemplateSchema validates path parameters: Required messageTemplateId.

Pipeline: deleteChatMessageTemplate โ†’ orThrow.

Success: Empty 204 response.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageTemplateNotFoundError โ†’ 404, ChatMessageTemplateUnauthorizedError โ†’ 403, ChatMessageTemplateInternalError โ†’ 500, ChatMessageTemplateDbError โ†’ 500 as shown in its source.

View complete source
export const deleteChatMessageTemplateRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
deleteChatMessageTemplate,
[
['context', 'db', 'chatMessageTemplates'],
['params', 'requestParams', 'messageTemplateId'],
],
'hasDeletedMessageTemplate'
)
),
lift(
withLogging(
orThrow(
[
[ChatMessageTemplateNotFoundError, 404],
[ChatMessageTemplateUnauthorizedError, 403],
[ChatMessageTemplateInternalError, 500],
[ChatMessageTemplateDbError, 500],
],
[[''], 204]
)
)
)
),
method: 'DELETE',
path: '/message-templates/:messageTemplateId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
hasOrganizationAccessToMessageTemplate(
['owner', 'admin'],
['params', 'requestParams', 'messageTemplateId']
)
),
],
});

findChatMessageTemplatesRouteโ€‹

Implementation

Endpoint: GET /message-templates

Access: isAuthenticated() โ†’ checkIdentityType(...) execute in the source order shown here. Exact invocation: isAuthenticated(), checkIdentityType(['admin']).

Request: findChatMessageTemplatesSchema validates query parameters: Shared pagination fields only.

Pipeline: findChatMessageTemplates โ†’ pagination โ†’ shared normalizeDocuments โ†’ orThrow.

Success: 200 with normalized templates and pagination metadata.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageTemplateNotFoundError โ†’ 404, ChatMessageTemplateUnauthorizedError โ†’ 403, ChatMessageTemplateInternalError โ†’ 500, ChatMessageTemplateDbError โ†’ 500 as shown in its source.

View complete source
export const findChatMessageTemplatesRoute = withRoute({
handler: compose(
withPagination(
withLogging(
applyPayloadArgs(
findChatMessageTemplates,
[
['context', 'db', 'chatMessageTemplates'],
['params', 'requestQuery'],
],
'messageTemplates'
)
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
normalizeDocuments,
[['context', 'data', 'messageTemplates', 'data']],
'normalizedMessageTemplates'
)
)
),
flatMapAsync(
applyPayloadArgs(
applySpec({ data: nthArg(0), metadata: { pagination: nthArg(1) } }),
[
['context', 'data', 'normalizedMessageTemplates'],
['context', 'data', 'messageTemplates', 'metadata'],
],
'normalizedBody'
)
),
lift(
withLogging(
orThrow(
[
[ChatMessageTemplateNotFoundError, 404],
[ChatMessageTemplateUnauthorizedError, 403],
[ChatMessageTemplateInternalError, 500],
[ChatMessageTemplateDbError, 500],
],
[['context', 'data', 'normalizedBody']]
)
)
)
),
method: 'GET',
path: '/message-templates',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
});

findChatMessageTemplatesForOrganizationRouteโ€‹

Implementation

Endpoint: GET /organizations/:organizationId/message-templates

Access: isAuthenticated() โ†’ hasOrgRole(...) execute in the source order shown here. Exact invocation: isAuthenticated(), hasOrgRole( ['owner', 'admin'], ['params', 'requestParams', 'organizationId'] ),.

Request: findChatMessageTemplatesForOrganizationSchema validates path and query parameters: Required organizationId and shared pagination fields.

Pipeline: buildFilterToGetChatMessageTemplatesByOrganizationId โ†’ findChatMessageTemplates โ†’ pagination โ†’ shared normalizeDocuments โ†’ orThrow.

Success: 200 with normalized templates for the path organization and pagination metadata.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatMessageTemplateNotFoundError โ†’ 404, ChatMessageTemplateUnauthorizedError โ†’ 403, ChatMessageTemplateInternalError โ†’ 500, ChatMessageTemplateDbError โ†’ 500 as shown in its source.

View complete source
export const findChatMessageTemplatesForOrganizationRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
buildFilterToGetChatMessageTemplatesByOrganizationId,
[['params', 'requestParams', 'organizationId']],
'filter'
)
),
flatMapAsync(
withPagination(
withLogging(
applyPayloadArgs(
findChatMessageTemplates,
[
['context', 'db', 'chatMessageTemplates'],
['context', 'data', 'filter'],
],
'messageTemplates'
)
)
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
normalizeDocuments,
[['context', 'data', 'messageTemplates', 'data']],
'normalizedMessageTemplates'
)
)
),
flatMapAsync(
applyPayloadArgs(
applySpec({ data: nthArg(0), metadata: { pagination: nthArg(1) } }),
[
['context', 'data', 'normalizedMessageTemplates'],
['context', 'data', 'messageTemplates', 'metadata'],
],
'normalizedBody'
)
),
lift(
withLogging(
orThrow(
[
[ChatMessageTemplateNotFoundError, 404],
[ChatMessageTemplateUnauthorizedError, 403],
[ChatMessageTemplateInternalError, 500],
[ChatMessageTemplateDbError, 500],
],
[['context', 'data', 'normalizedBody']]
)
)
)
),
method: 'GET',
path: '/organizations/:organizationId/message-templates',
validators: [
isAuthenticated(),
hasOrgRole(
['owner', 'admin'],
['params', 'requestParams', 'organizationId']
),
],
});

upsertChatChannelReadStateRouteโ€‹

Implementation

Endpoint: PUT /channels/:channelId/read-state

Access: isAuthenticated() โ†’ isSelf(...) โ†’ hasSubscription(...) โ†’ channelExists(...) execute in the source order shown here. Exact invocation: isAuthenticated(), channelExists(['params', 'requestParams', 'channelId']), hasSubscription(['params', 'requestParams', 'channelId']), isSelf(['params', 'requestBody', 'identityId']),.

Request: upsertChatChannelReadStateSchema validates path parameters and the application/json body: Required channelId, identityId, and lastReadMessageId.

Pipeline: findChatMessages.

Success: Empty 204 response after creating or updating the read state.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. This route maps ChatChannelReadStateNotFoundError โ†’ 404, ChatChannelReadStateDatabaseError โ†’ 500, ChatChannelReadStateUnknownError โ†’ 500 as shown in its source.

View complete source
export const upsertChatChannelReadStateRoute = withRoute({
handler: compose(
// Find the existing chat channel read state by channel and identity
compose(
withLogging(
applyPayloadArgs(
buildChatChannelReadStateQuery,
[
['params', 'requestParams', 'channelId'],
['params', 'requestBody', 'identityId'],
],
'chatChannelReadStateQuery'
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
findChatChannelReadStates,
[
['context', 'db', 'chatChannelReadStates'],
['context', 'data', 'chatChannelReadStateQuery'],
],
'findChatChannelReadStatesResult'
)
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
head,
[['context', 'data', 'findChatChannelReadStatesResult']],
'chatChannelReadState'
)
)
)
),
// Find the last read message by message id, channel id, and identity id
compose(
flatMapAsync(
withLogging(
applyPayloadArgs(
buildLastReadMessageQuery,
[
['params', 'requestBody', 'lastReadMessageId'],
['params', 'requestParams', 'channelId'],
['params', 'requestBody', 'identityId'],
],
'lastReadMessageQuery'
)
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
findChatMessages,
[
['context', 'db', 'chatMessages'],
['context', 'data', 'lastReadMessageQuery'],
],
'findChatMessagesResult'
)
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
head,
[['context', 'data', 'findChatMessagesResult']],
'lastReadMessage'
)
)
)
),
flatMapAsync(
ifElse(
match(hasValue, ['context', 'data', 'chatChannelReadState']),
// Update the existing chat channel read state
compose(
withLogging(
applyPayloadArgs(
buildUpdateChatChannelReadStatePayload,
[['context', 'data', 'lastReadMessage']],
'updateChatChannelReadStatePayload'
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
updateChatChannelReadState,
[
['context', 'db', 'chatChannelReadStates'],
['context', 'data', 'chatChannelReadState', 'id'],
['context', 'data', 'updateChatChannelReadStatePayload'],
],
'upsertChatChannelReadStateResult'
)
)
)
),
// Create a new chat channel read state
compose(
withLogging(
applyPayloadArgs(
buildCreateChatChannelReadStatePayload,
[
['params', 'requestParams', 'channelId'],
['params', 'requestBody', 'identityId'],
['context', 'data', 'lastReadMessage'],
],
'createChatChannelReadStatePayload'
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
createChatChannelReadState,
[
['context', 'db', 'chatChannelReadStates'],
['context', 'data', 'createChatChannelReadStatePayload'],
],
'upsertChatChannelReadStateResult'
)
)
)
)
)
),
lift(
withLogging(
orThrow(
[
[ChatChannelReadStateNotFoundError, 404],
[ChatChannelReadStateDatabaseError, 500],
[ChatChannelReadStateUnknownError, 500],
],
[['context', 'data', 'upsertChatChannelReadStateResult'], 204]
)
)
)
),
method: 'PUT',
path: '/channels/:channelId/read-state',
validators: [
isAuthenticated(),
channelExists(['params', 'requestParams', 'channelId']),
hasSubscription(['params', 'requestParams', 'channelId']),
isSelf(['params', 'requestBody', 'identityId']),
],
});

createChatSubscriptionRouteโ€‹

Implementation

Endpoint: POST /subscriptions

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ isSelf(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), isSelf(['params', 'requestBody', 'subscribedId']) ),.

Request: createChatSubscriptionSchema validates the application/json body: Required channelId and subscribedId; other subscription fields optional.

Pipeline: createChatSubscription โ†’ getChatSubscriptionById โ†’ createChatSubscriptionTerminator.

Success: 201 with the created subscription in the response descriptorโ€™s data.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. Pipeline failures follow the linked handler/block contracts and shared error middleware; this route adds no separate inline error mapping.

View complete source
export const createChatSubscriptionRoute = withRoute({
handler: compose(
// TODO: need to check if the user is already subscribed to the channel
withLogging(createChatSubscription),
flatMapAsync(withLogging(getChatSubscriptionById)),
lift(withLogging(createChatSubscriptionTerminator))
),
method: 'POST',
path: '/subscriptions',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
isSelf(['params', 'requestBody', 'subscribedId'])
),
],
});

findChatSubscriptionsRouteโ€‹

Implementation

Endpoint: GET /subscriptions

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ isSelf(...) โ†’ ownsChannel(...) โ†’ hasSubscription(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), hasSubscription(['params', 'requestQuery', 'channelId']), ownsChannel(['params', 'requestQuery', 'channelId']), isSelf(['params', 'requestQuery', 'subscribedId']) ),.

Request: findChatSubscriptionsSchema validates query parameters: Optional subscription filters and shared pagination fields.

Pipeline: findChatSubscriptions โ†’ normalizeChatSubscriptionsListTerminator.

Success: 200 with normalized subscriptions and pagination metadata.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. Pipeline failures follow the linked handler/block contracts and shared error middleware; this route adds no separate inline error mapping.

View complete source
export const findChatSubscriptionsRoute = withRoute({
handler: compose(
withPagination(withLogging(findChatSubscriptions)),
lift(withLogging(normalizeChatSubscriptionsListTerminator))
),
method: 'GET',
path: '/subscriptions',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
hasSubscription(['params', 'requestQuery', 'channelId']),
ownsChannel(['params', 'requestQuery', 'channelId']),
isSelf(['params', 'requestQuery', 'subscribedId'])
),
],
});

getChatSubscriptionRouteโ€‹

Implementation

Endpoint: GET /subscriptions/:subscriptionId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ ownsSubscription(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), ownsSubscription(['params', 'requestParams', 'subscriptionId']) ),.

Request: getChatSubscriptionSchema validates path parameters: Required subscriptionId.

Pipeline: getChatSubscriptionById โ†’ normalizeChatSubscriptionTerminator.

Success: 200 with the subscription excluding MongoDB _id.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. Pipeline failures follow the linked handler/block contracts and shared error middleware; this route adds no separate inline error mapping.

View complete source
export const getChatSubscriptionRoute = withRoute({
handler: compose(
withLogging(getChatSubscriptionById),
lift(withLogging(normalizeChatSubscriptionTerminator))
),
method: 'GET',
path: '/subscriptions/:subscriptionId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsSubscription(['params', 'requestParams', 'subscriptionId'])
),
],
});

deleteChatSubscriptionRouteโ€‹

Implementation

Endpoint: DELETE /subscriptions/:subscriptionId

Access: isAuthenticated() โ†’ some(...) โ†’ checkIdentityType(...) โ†’ ownsSubscription(...) execute in the source order shown here. Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), ownsSubscription(['params', 'requestParams', 'subscriptionId']) ),.

Request: deleteChatSubscriptionSchema validates path parameters: Required subscriptionId.

Pipeline: deleteChatSubscription โ†’ deleteChatSubscriptionTerminator.

Success: Empty 204 response.

Failure: Authentication and authorization failures follow the linked validators before the handler pipeline. Pipeline failures follow the linked handler/block contracts and shared error middleware; this route adds no separate inline error mapping.

View complete source
export const deleteChatSubscriptionRoute = withRoute({
handler: compose(
withLogging(deleteChatSubscription),
lift(withLogging(deleteChatSubscriptionTerminator))
),
method: 'DELETE',
path: '/subscriptions/:subscriptionId',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
ownsSubscription(['params', 'requestParams', 'subscriptionId'])
),
],
});