メインコンテンツまでスキップ
バージョン: 🚧 Canary

🛣️ 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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() → channelExists(...) → hasSubscription(...) 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(...) are composed as shown here, with alternative checks inside some(...). Exact invocation: isAuthenticated(), some( checkIdentityType(['admin']), ownsChannel(['params', 'requestParams', 'channelId']) ),.

Request: The shared getSignedImageUploadUrlSchema validates contentType and contentLength query parameters. It does not validate the path channelId; the ownership validator reads that ID.

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(...) → hasSubscription(...) → isSelf(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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 contentType and contentLength query parameters. It does not validate the path messageId; the ownership validator reads that ID.

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 observable.

Success: A WebSocket observable 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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() → channelExists(...) → hasSubscription(...) → isSelf(...) 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) → hasSubscription(...) → ownsChannel(...) → isSelf(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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(...) are composed as shown here, with alternative checks inside some(...). 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'])
),
],
});