Для разработчиков

TypeScript

Типизированный клиент для Pachca API. Работает в Node.js 18+ и в любом окружении с поддержкой fetch. Пакет подключается и как ES Module (import), и как CommonJS (require). TypeScript-декларации входят в пакет.

Быстрый старт

Установка

npm install @pachca/sdk

Создание клиента

Клиенту нужен токен бота или персональный токен. Какой выбрать и кто может его выпустить, описано в разделе Какой токен выбрать.

import { PachcaClient } from "@pachca/sdk" const client = new PachcaClient("YOUR_TOKEN")

Первый запрос

// Получение профиляconst response = await client.profile.getProfile()// → User({ id: number, firstName: string | null, lastName: string | null, nickname: string, email: string | null, phoneNumber: string | null, department: string | null, title: string | null, role: UserRole, suspended: boolean, inviteStatus: InviteStatus, inviterId: number | null, listTags: string[], customProperties: CustomProperty({ id: number, name: string, dataType: CustomPropertyDataType, value: string | null })[], userStatus: UserStatus({ emoji: string, title: string, expiresAt: string | null, isAway: boolean, awayMessage: UserStatusAwayMessage({ text: string }) | null }) | null, bot: boolean, sso: boolean, createdAt: string, lastActivityAt: string | null, timeZone: string | null, imageUrl: string | null })

Инициализация

import { PachcaClient } from "@pachca/sdk" // Стандартное подключениеconst client = new PachcaClient("YOUR_TOKEN") // С кастомным базовым URLconst client = new PachcaClient("YOUR_TOKEN", "https://custom-api.example.com/api/shared/v1")
ПараметрТипПо умолчаниюОписание
tokenstring—Bearer-токен для авторизации
baseUrlstringhttps://api.pachca.com/api/shared/v1Базовый URL API

Клиент не требует явного закрытия — все запросы используют глобальный fetch.

Все методы

МетодМетод API
client.oauth.revokeToken()POSTОтзыв токена
client.oauth.exchangeToken()POSTОбмен кода на токен
client.oauth.getTokenInfo()GETИнформация о токене
client.chats.createChat()POSTНовый чат
client.chats.requestExport()POSTЭкспорт сообщений
client.chats.listChats()GETСписок чатов
client.chats.downloadExport()GETСкачать архив экспорта
client.chats.getChat()GETИнформация о чате
client.chats.listCompanyChats()GETСписок чатов пространства
client.chats.updateChat()PUTРедактирование чата
client.chats.archiveChat()PUTАрхивация чата
client.chats.updateChatAvatar()PUTЗагрузка аватара чата
client.chats.unarchiveChat()PUTРазархивация чата
client.chats.markChatUnread()PUTОтметка чата непрочитанным
client.chats.deleteChatAvatar()DELETEУдаление аватара чата
client.profile.getProfile()GETСвой профиль
client.profile.getStatus()GETСвой статус
client.profile.updateProfileAvatar()PUTЗагрузка своего аватара
client.profile.updateStatus()PUTНовый свой статус
client.profile.deleteProfileAvatar()DELETEУдаление своего аватара
client.profile.deleteStatus()DELETEУдаление своего статуса
client.users.createUser()POSTНовый сотрудник
client.users.listUsers()GETСписок сотрудников
client.users.getUser()GETИнформация о сотруднике
client.users.getUserStatus()GETСтатус сотрудника
client.users.updateUser()PUTРедактирование сотрудника
client.users.updateUserAvatar()PUTЗагрузка аватара сотрудника
client.users.updateUserStatus()PUTНовый статус сотрудника
client.users.deleteUser()DELETEУдаление сотрудника
client.users.deleteUserAvatar()DELETEУдаление аватара сотрудника
client.users.deleteUserStatus()DELETEУдаление статуса сотрудника
client.groupTags.createTag()POSTНовый тег
client.groupTags.listTags()GETСписок тегов
client.groupTags.getTag()GETИнформация о теге
client.groupTags.getTagUsers()GETСписок сотрудников тега
client.groupTags.updateTag()PUTРедактирование тега
client.groupTags.deleteTag()DELETEУдаление тега
client.members.addTags()POSTДобавление тегов
client.members.addMembers()POSTДобавление участников
client.members.listMembers()GETСписок участников чата
client.members.updateMemberRole()PUTРедактирование роли участника
client.members.removeTag()DELETEИсключение тега из чата
client.members.leaveChat()DELETEВыход из чата
client.members.removeMember()DELETEИсключение участника
client.threads.createThread()POSTНовый тред
client.threads.createStandaloneThread()POSTНовый самостоятельный тред
client.threads.listThreads()GETСписок тредов
client.threads.getThread()GETИнформация о треде
client.messages.createMessage()POSTНовое сообщение
client.messages.unfurl()POSTUnfurl (разворачивание ссылок)
client.messages.pinMessage()POSTЗакрепление сообщения
client.messages.listChatMessages()GETСписок сообщений чата
client.messages.getMessage()GETИнформация о сообщении
client.messages.updateMessage()PUTРедактирование сообщения
client.messages.deleteMessage()DELETEУдаление сообщения
client.messages.unpinMessage()DELETEОткрепление сообщения
client.readMembers.listReadMembers()GETСписок прочитавших сообщение
client.reactions.addReaction()POSTДобавление реакции
client.reactions.listReactions()GETСписок реакций
client.reactions.removeReaction()DELETEУдаление реакции
client.importantMessages.addMessageToImportant()POSTДобавление сообщения в важные
client.importantMessages.listImportantMessages()GETСписок важных сообщений
client.importantMessages.removeMessageFromImportant()DELETEУдаление сообщения из важных
client.search.searchChats()GETПоиск чатов
client.search.searchMessages()GETПоиск сообщений
client.search.searchUsers()GETПоиск сотрудников
client.tasks.createTask()POSTНовое напоминание
client.tasks.listTasks()GETСписок напоминаний
client.tasks.getTask()GETИнформация о напоминании
client.tasks.updateTask()PUTРедактирование напоминания
client.tasks.deleteTask()DELETEУдаление напоминания
client.drafts.createDraft()POSTНовый черновик
client.drafts.listDrafts()GETСписок черновиков
client.drafts.getDraft()GETИнформация о черновике
client.drafts.updateDraft()PUTРедактирование черновика
client.drafts.deleteDraft()DELETEУдаление черновика
client.views.openView()POSTОткрытие представления
client.views.submitViewResponse()POSTОтвет на отправку формы
client.bots.selfRecreateBotToken()POSTРотация собственного токена бота
client.bots.createBot()POSTНовый бот
client.bots.recreateBotToken()POSTРотация токена бота
client.bots.rotateBotClientSecret()POSTРотация секрета клиента
client.bots.createBotToken()POSTНовый токен бота
client.bots.reissueBotToken()POSTПеревыпуск токена бота
client.bots.listBots()GETСписок ботов
client.bots.getBot()GETИнформация о боте
client.bots.getBotScopes()GETКаталог прав бота
client.bots.listBotTokens()GETСписок токенов бота
client.bots.listCompanyBots()GETСписок ботов пространства
client.bots.getWebhookEvents()GETИстория событий
client.bots.getWebhookEventsStream()GETПоток событий
client.bots.selfUpdateBotWebhook()PUTСаморегистрация вебхука бота
client.bots.updateBot()PUTРедактирование бота
client.bots.updateBotAvatar()PUTЗагрузка аватара бота
client.bots.updateBotToken()PUTИзменение токена бота
client.bots.deleteBot()DELETEУдаление бота
client.bots.deleteBotAvatar()DELETEУдаление аватара бота
client.bots.deleteBotToken()DELETEУдаление токена бота
client.bots.deleteWebhookEvent()DELETEУдаление события истории
client.security.getAuditEvents()GETЖурнал аудита событий
client.customProperties.listProperties()GETСписок дополнительных полей
client.files.uploadFile()POSTЗагрузка файла
client.files.getUploadParams()POSTПолучение подписи, ключа и других параметров
client.files.downloadFile()GETСкачивание файла

Запросы

Все методы — асинхронные и возвращают Promise.

GET с параметрами:

import { ChatAvailability, ChatSortField, SortOrder } from "@pachca/sdk" // Список чатовconst response = await client.chats.listChats({  sort: ChatSortField.Id,  order: SortOrder.Desc,  availability: ChatAvailability.IsMember,  archived: true,  lastMessageAtAfter: "2025-01-01T00:00:00.000Z",  lastMessageAtBefore: "2025-02-01T00:00:00.000Z",  personal: false,  limit: 1,  cursor: "eyJpZCI6MTAsImRpciI6ImFzYyJ9"})// → ListChatsResponse({ data: Chat[], meta: PaginationMeta })

POST с телом запроса:

import { ChatCreateRequest } from "@pachca/sdk" // Создание чатаconst request: ChatCreateRequest = {  chat: {    name: "🤿 aqua",    memberIds: [123],    groupTagIds: [123],    channel: true,    public: false  }}const response = await client.chats.createChat(request)// → CreateChatResponse({ data: Chat({ id: number, name: string, createdAt: string, ownerId: number, memberIds: number[], groupTagIds: number[], channel: boolean, archived: boolean, personal: boolean, public: boolean, lastMessageAt: string, meetRoomUrl: string, avatarUrl: string | null }), meta: CreateChatResponseMeta({ notFoundMemberIds: number[], notFoundGroupTagIds: number[] }) })

Простой вызов по ID:

// Получение чатаconst response = await client.chats.getChat(334)// → Chat({ id: number, name: string, createdAt: string, ownerId: number, memberIds: number[], groupTagIds: number[], channel: boolean, archived: boolean, personal: boolean, public: boolean, lastMessageAt: string, meetRoomUrl: string, avatarUrl: string | null })

Пагинация

Поля пагинации называются как в API, но в camelCase: meta.paginate.nextPage, prevPage, hasNext и hasPrev, а у методов поиска ещё meta.total. Какие поля приходят у каждого метода и как понять, что страницы закончились, описано в разделе Пагинация.

Ручная пагинация

let cursor: string | undefinedlet hasNext = true while (hasNext) {  const response = await client.chats.listChats({ limit: 50, cursor })  if (response.data.length === 0) break  for (const chat of response.data) {    console.log(chat.id, chat.name)  }  cursor = response.meta.paginate.nextPage  hasNext = response.meta.paginate.hasNext ?? true}

В типах поле hasNext необязательное. Если его нет, цикл идёт дальше и останавливается на пустой странице. Так же обходят страницы методы *All().

Автопагинация

Для каждого метода с пагинацией есть *All() вариант, который автоматически обходит все страницы и возвращает плоский массив:

// Все чаты одним массивомconst chats = await client.chats.listChatsAll()console.log(`Всего: ${chats.length}`)

Доступные методы автопагинации:

МетодВозвращает
security.getAuditEventsAll()AuditEvent[]
bots.listBotsAll()BotResponse[]
bots.listBotTokensAll()BotAccessToken[]
bots.listCompanyBotsAll()CompanyBotResponse[]
bots.getWebhookEventsAll()WebhookEvent[]
chats.listChatsAll()Chat[]
chats.listCompanyChatsAll()Chat[]
drafts.listDraftsAll()Draft[]
groupTags.listTagsAll()GroupTag[]
groupTags.getTagUsersAll()User[]
importantMessages.listImportantMessagesAll()Message[]
members.listMembersAll()User[]
messages.listChatMessagesAll()Message[]
reactions.listReactionsAll()Reaction[]
search.searchChatsAll()Chat[]
search.searchMessagesAll()Message[]
search.searchUsersAll()User[]
tasks.listTasksAll()Task[]
threads.listThreadsAll()Thread[]
users.listUsersAll()User[]

Обработка ошибок

SDK выбрасывает два типа ошибок:

ApiError

Возникает при ошибках 400, 403, 404, 409, 410, 422 с массивом errors в теле:

import { PachcaClient, ApiError } from "@pachca/sdk" try {  await client.chats.createChat(request)} catch (error) {  if (error instanceof ApiError) {    for (const e of error.errors) {      console.log(e.key, e.message)   // name не может быть пустым      console.log(e.code)             // blank    }  }}

Поля ApiErrorItem:

ПолеТипОписание
keystringПоле, вызвавшее ошибку
valueunknownПереданное значение
messagestringТекст ошибки
codeValidationErrorCodeКод валидации (blank, invalid, taken, ...)
payloadRecord<string, unknown> | nullДополнительные данные

Код статуса ответа — в поле status. Если ответ с ошибкой пришёл без тела или не в JSON, SDK тоже выбрасывает ApiError: с кодом статуса и пустым errors. Так бывает с ответом 404 без тела от метода GETПоток событий, когда история событий выключена, и со страницей прокси при 502.

OAuthError

Возникает при ошибке авторизации: 401, а также 403 с кодом insufficient_scope или role_forbidden и отказе при обмене кода на токен. Код — в поле error, список кодов — в разделе Ошибки авторизации:

import { OAuthError } from "@pachca/sdk" try {  await client.profile.getProfile()} catch (error) {  if (error instanceof OAuthError) {    console.log(error.error) // "insufficient_scope"  }}

Поле error — строка, её значения перечисляет OAuthErrorCode.

Повторные запросы

Паузы между попытками, к каждой SDK добавляет случайную величину до 25%:

  • 429 с заголовком Retry-After: срок из заголовка
  • 429 без заголовка: 1 сек, 2 сек, 4 сек
  • 5xx: 1 сек, 2 сек, 3 сек

Сетевую ошибку без ответа сервера SDK не повторяет: исключение из fetch приходит сразу.

Общие правила повторов — в разделе Повторные запросы.

Сериализация

SDK автоматически конвертирует имена полей между camelCase (TypeScript) и snake_case (API):

// Вы пишете:{ memberIds: [123], groupTagIds: [456] } // SDK отправляет:{ "member_ids": [123], "group_tag_ids": [456] } // API возвращает:{ "last_message_at": "2025-01-01T00:00:00Z" } // Вы получаете:{ lastMessageAt: "2025-01-01T00:00:00Z" }

Типы

Все типы экспортируются из пакета:

import {  // Модели  Chat, Message, User, Task, GroupTag, Thread, Reaction,  // Запросы  ChatCreateRequest, MessageCreateRequest, TaskCreateRequest,  // Параметры  ListChatsParams, ListUsersParams, SearchMessagesParams,  // Ответы  ListChatsResponse, ListMembersResponse, CreateChatResponse,  // Перечисления  AuditEventKey, ChatAvailability, ChatMemberRole, ChatMemberRoleFilter,  ChatSubtype, CustomPropertyDataType, FileType, InviteStatus,  MemberEventType, MessageEntityType, OAuthScope, ReactionEventType,  SearchEntityType, SearchSortOrder, SortOrder, TaskKind, TaskStatus,  UserEventType, UserRole, ValidationErrorCode, WebhookEventType,  // Ошибки  ApiError, OAuthError,} from "@pachca/sdk"

Перечисление в ответе принимает и значение, которого SDK ещё не знает: такое значение приходит строкой как есть.

Тело вебхука

Тела событий вебхука описаны типами MessageWebhookPayload, ReactionWebhookPayload, ButtonWebhookPayload, ViewSubmitWebhookPayload, ChatMemberWebhookPayload, CompanyMemberWebhookPayload, LinkSharedWebhookPayload и VideoCallWebhookPayload. Тип WebhookPayloadUnion объединяет их, а вид события задают поля type и event. Поля в этих типах названы в camelCase, поэтому тело запроса сначала пропустите через функцию deserialize:

import { deserialize, WebhookPayloadUnion } from "@pachca/sdk" // rawBody — тело запроса строкойconst payload = deserialize(JSON.parse(rawBody)) as WebhookPayloadUnionif (payload.type === "button") {  console.log(payload.messageId, payload.data)}

Подпись проверяйте по исходному телу до разбора, как в разделе Безопасность. Те же типы отдают методы pollWebhookEvents и pollWebhookPayloads, которые забирают события из истории: пример — в разделе Пример поллинга.

Примеры

Отправка сообщения:

import { MessageCreateRequest, PachcaClient } from "@pachca/sdk" const client = new PachcaClient("YOUR_TOKEN") const request: MessageCreateRequest = { message: { entityId: 334, content: "Вчера мы продали 756 футболок (что на 10% больше, чем в прошлое воскресенье)" } }const response = await client.messages.createMessage(request)// → Message({ id: number, entityType: MessageEntityType, entityId: number, chatId: number, rootChatId: number, content: string, userId: number, createdAt: string, url: string, files: File({ id: number, key: string, name: string, fileType: FileType, url: string, width?: number | null, height?: number | null, durationMs?: number | null })[], voiceContent: VoiceContent({ durationMs: number, waveform: string, transcript: string | null, transcriptStatus: VoiceTranscriptStatus | null }) | null, buttons: Button({ text: string, url?: string, data?: string })[][] | null, thread: MessageThread({ id: number, chatId: number }) | null, forwarding: Forwarding({ originalMessageId: number, originalChatId: number, authorId: number, originalCreatedAt: string, originalThreadId: number | null, originalThreadMessageId: number | null, originalThreadParentChatId: number | null }) | null, parentMessageId: number | null, displayAvatarUrl?: string | null, displayName?: string | null, changedAt: string | null, deletedAt: string | null, important: boolean })

Список сотрудников:

import { PachcaClient } from "@pachca/sdk" const client = new PachcaClient("YOUR_TOKEN") const response = await client.users.listUsers({  query: "Олег",  limit: 1,  cursor: "eyJpZCI6MTAsImRpciI6ImFzYyJ9"})// → ListUsersResponse({ data: User[], meta: PaginationMeta })

Создание напоминания:

import { PachcaClient, TaskCreateRequest, TaskCreateRequestCustomProperty, TaskKind } from "@pachca/sdk" const client = new PachcaClient("YOUR_TOKEN") const request: TaskCreateRequest = {  task: {    kind: TaskKind.Reminder,    content: "Забрать со склада 21 заказ",    dueAt: "2020-06-05T12:00:00.000+03:00",    priority: 2,    performerIds: [123],    chatId: 456,    allDay: false,    customProperties: [{ id: 78, value: "Синий склад" }]  }}const response = await client.tasks.createTask(request)// → Task({ id: number, kind: TaskKind, content: string, dueAt: string | null, priority: number, userId: number, chatId: number | null, status: TaskStatus, createdAt: string, performerIds: number[], allDay: boolean, customProperties: CustomProperty({ id: number, name: string, dataType: CustomPropertyDataType, value: string | null })[] })