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

Python

Асинхронный типизированный клиент для Pachca API. Построен на httpx с поддержкой type hints и dataclass-моделей. Требуется Python 3.10+.

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

Установка

pip install pachca-sdk

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

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

from pachca.client import PachcaClient client = PachcaClient("YOUR_TOKEN")

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

# Получение профиляresponse = await client.profile.get_profile()# → User(id: int, first_name: str | None, last_name: str | None, nickname: str, email: str | None, phone_number: str | None, department: str | None, title: str | None, role: UserRole, suspended: bool, invite_status: InviteStatus, inviter_id: int | None, list_tags: list[str], custom_properties: list[CustomProperty(id: int, name: str, data_type: CustomPropertyDataType, value: str | None)], user_status: UserStatus(emoji: str, title: str, expires_at: datetime | None, is_away: bool, away_message: UserStatusAwayMessage(text: str) | None) | None, bot: bool, sso: bool, created_at: datetime, last_activity_at: datetime | None, time_zone: str | None, image_url: str | None)
Все методы SDK асинхронные — вызывайте их через await внутри async def.

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

from pachca.client import PachcaClient # Стандартное подключениеclient = PachcaClient("YOUR_TOKEN") # С кастомным базовым URLclient = PachcaClient("YOUR_TOKEN", base_url="https://custom-api.example.com/api/shared/v1")
ПараметрТипПо умолчаниюОписание
tokenstr—Bearer-токен для авторизации
base_urlstrhttps://api.pachca.com/api/shared/v1Базовый URL API

Клиент использует httpx.AsyncClient — после завершения работы закройте его:

await client.close()

Все методы

МетодМетод API
client.oauth.revoke_token()POSTОтзыв токена
client.oauth.exchange_token()POSTОбмен кода на токен
client.oauth.get_token_info()GETИнформация о токене
client.chats.create_chat()POSTНовый чат
client.chats.request_export()POSTЭкспорт сообщений
client.chats.list_chats()GETСписок чатов
client.chats.download_export()GETСкачать архив экспорта
client.chats.get_chat()GETИнформация о чате
client.chats.list_company_chats()GETСписок чатов пространства
client.chats.update_chat()PUTРедактирование чата
client.chats.archive_chat()PUTАрхивация чата
client.chats.update_chat_avatar()PUTЗагрузка аватара чата
client.chats.unarchive_chat()PUTРазархивация чата
client.chats.mark_chat_unread()PUTОтметка чата непрочитанным
client.chats.delete_chat_avatar()DELETEУдаление аватара чата
client.profile.get_profile()GETСвой профиль
client.profile.get_status()GETСвой статус
client.profile.update_profile_avatar()PUTЗагрузка своего аватара
client.profile.update_status()PUTНовый свой статус
client.profile.delete_profile_avatar()DELETEУдаление своего аватара
client.profile.delete_status()DELETEУдаление своего статуса
client.users.create_user()POSTНовый сотрудник
client.users.list_users()GETСписок сотрудников
client.users.get_user()GETИнформация о сотруднике
client.users.get_user_status()GETСтатус сотрудника
client.users.update_user()PUTРедактирование сотрудника
client.users.update_user_avatar()PUTЗагрузка аватара сотрудника
client.users.update_user_status()PUTНовый статус сотрудника
client.users.delete_user()DELETEУдаление сотрудника
client.users.delete_user_avatar()DELETEУдаление аватара сотрудника
client.users.delete_user_status()DELETEУдаление статуса сотрудника
client.group_tags.create_tag()POSTНовый тег
client.group_tags.list_tags()GETСписок тегов
client.group_tags.get_tag()GETИнформация о теге
client.group_tags.get_tag_users()GETСписок сотрудников тега
client.group_tags.update_tag()PUTРедактирование тега
client.group_tags.delete_tag()DELETEУдаление тега
client.members.add_tags()POSTДобавление тегов
client.members.add_members()POSTДобавление участников
client.members.list_members()GETСписок участников чата
client.members.update_member_role()PUTРедактирование роли участника
client.members.remove_tag()DELETEИсключение тега из чата
client.members.leave_chat()DELETEВыход из чата
client.members.remove_member()DELETEИсключение участника
client.threads.create_thread()POSTНовый тред
client.threads.create_standalone_thread()POSTНовый самостоятельный тред
client.threads.list_threads()GETСписок тредов
client.threads.get_thread()GETИнформация о треде
client.messages.create_message()POSTНовое сообщение
client.messages.unfurl()POSTUnfurl (разворачивание ссылок)
client.messages.pin_message()POSTЗакрепление сообщения
client.messages.list_chat_messages()GETСписок сообщений чата
client.messages.get_message()GETИнформация о сообщении
client.messages.update_message()PUTРедактирование сообщения
client.messages.delete_message()DELETEУдаление сообщения
client.messages.unpin_message()DELETEОткрепление сообщения
client.read_members.list_read_members()GETСписок прочитавших сообщение
client.reactions.add_reaction()POSTДобавление реакции
client.reactions.list_reactions()GETСписок реакций
client.reactions.remove_reaction()DELETEУдаление реакции
client.important_messages.add_message_to_important()POSTДобавление сообщения в важные
client.important_messages.list_important_messages()GETСписок важных сообщений
client.important_messages.remove_message_from_important()DELETEУдаление сообщения из важных
client.search.search_chats()GETПоиск чатов
client.search.search_messages()GETПоиск сообщений
client.search.search_users()GETПоиск сотрудников
client.tasks.create_task()POSTНовое напоминание
client.tasks.list_tasks()GETСписок напоминаний
client.tasks.get_task()GETИнформация о напоминании
client.tasks.update_task()PUTРедактирование напоминания
client.tasks.delete_task()DELETEУдаление напоминания
client.drafts.create_draft()POSTНовый черновик
client.drafts.list_drafts()GETСписок черновиков
client.drafts.get_draft()GETИнформация о черновике
client.drafts.update_draft()PUTРедактирование черновика
client.drafts.delete_draft()DELETEУдаление черновика
client.views.open_view()POSTОткрытие представления
client.views.submit_view_response()POSTОтвет на отправку формы
client.bots.self_recreate_bot_token()POSTРотация собственного токена бота
client.bots.create_bot()POSTНовый бот
client.bots.recreate_bot_token()POSTРотация токена бота
client.bots.rotate_bot_client_secret()POSTРотация секрета клиента
client.bots.create_bot_token()POSTНовый токен бота
client.bots.reissue_bot_token()POSTПеревыпуск токена бота
client.bots.list_bots()GETСписок ботов
client.bots.get_bot()GETИнформация о боте
client.bots.get_bot_scopes()GETКаталог прав бота
client.bots.list_bot_tokens()GETСписок токенов бота
client.bots.list_company_bots()GETСписок ботов пространства
client.bots.get_webhook_events()GETИстория событий
client.bots.get_webhook_events_stream()GETПоток событий
client.bots.self_update_bot_webhook()PUTСаморегистрация вебхука бота
client.bots.update_bot()PUTРедактирование бота
client.bots.update_bot_avatar()PUTЗагрузка аватара бота
client.bots.update_bot_token()PUTИзменение токена бота
client.bots.delete_bot()DELETEУдаление бота
client.bots.delete_bot_avatar()DELETEУдаление аватара бота
client.bots.delete_bot_token()DELETEУдаление токена бота
client.bots.delete_webhook_event()DELETEУдаление события истории
client.security.get_audit_events()GETЖурнал аудита событий
client.custom_properties.list_properties()GETСписок дополнительных полей
client.files.upload_file()POSTЗагрузка файла
client.files.get_upload_params()POSTПолучение подписи, ключа и других параметров
client.files.download_file()GETСкачивание файла

Запросы

Все методы — асинхронные (async def) и возвращают корутины.

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

from datetime import datetime from pachca.models import ChatAvailability, ChatSortField, ListChatsParams, SortOrder # Список чатовparams = ListChatsParams(    sort=ChatSortField.ID,    order=SortOrder.DESC,    availability=ChatAvailability.IS_MEMBER,    archived=True,    last_message_at_after=datetime.fromisoformat("2025-01-01T00:00:00.000+00:00"),    last_message_at_before=datetime.fromisoformat("2025-02-01T00:00:00.000+00:00"),    personal=False,    limit=1,    cursor="eyJpZCI6MTAsImRpciI6ImFzYyJ9")response = await client.chats.list_chats(params=params)# → ListChatsResponse(data: list[Chat], meta: PaginationMeta)

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

from pachca.models import ChatCreateRequest, ChatCreateRequestChat # Создание чатаrequest = ChatCreateRequest(    chat=ChatCreateRequestChat(        name="🤿 aqua",        member_ids=[123],        group_tag_ids=[123],        channel=True,        public=False    ))response = await client.chats.create_chat(request=request)# → CreateChatResponse(data: Chat(id: int, name: str, created_at: datetime, owner_id: int, member_ids: list[int], group_tag_ids: list[int], channel: bool, archived: bool, personal: bool, public: bool, last_message_at: datetime, meet_room_url: str, avatar_url: str | None), meta: CreateChatResponseMeta(not_found_member_ids: list[int], not_found_group_tag_ids: list[int]))

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

# Получение чатаresponse = await client.chats.get_chat(id=334)# → Chat(id: int, name: str, created_at: datetime, owner_id: int, member_ids: list[int], group_tag_ids: list[int], channel: bool, archived: bool, personal: bool, public: bool, last_message_at: datetime, meet_room_url: str, avatar_url: str | None)

Значение null

Необязательное поле, которое не задано, в запрос не попадает, поэтому метод обновления меняет только то, что вы передали. Некоторые поля API принимают null: например, null в oauth_client выключает у бота авторизацию от имени сотрудника, а в webhook.kind удаляет входящий вебхук. Чтобы отправить null, назовите поле в null_fields той же модели запроса так, как оно называется в Python:

request = BotUpdateRequest(    webhook=BotUpdateRequestWebhook(null_fields=["kind"]),)

null_fields есть у моделей запроса с такими полями, а в комментарии к нему перечислены имена, которые в нём можно указать. С другим именем SDK выбрасывает ValueError и запрос не отправляет.

Пагинация

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

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

from pachca.models import ListChatsParams cursor = Nonehas_next = Truewhile has_next:    response = await client.chats.list_chats(ListChatsParams(limit=50, cursor=cursor))    if not response.data:        break    for chat in response.data:        print(chat.id, chat.name)    cursor = response.meta.paginate.next_page    has_next = response.meta.paginate.has_next is not False

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

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

Для каждого метода с пагинацией есть *_all() вариант:

# Все чаты одним спискомchats = await client.chats.list_chats_all()print(f"Всего: {len(chats)}")

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

МетодВозвращает
security.get_audit_events_all()list[AuditEvent]
bots.list_bots_all()list[BotResponse]
bots.list_bot_tokens_all()list[BotAccessToken]
bots.list_company_bots_all()list[CompanyBotResponse]
bots.get_webhook_events_all()list[WebhookEvent]
chats.list_chats_all()list[Chat]
chats.list_company_chats_all()list[Chat]
drafts.list_drafts_all()list[Draft]
group_tags.list_tags_all()list[GroupTag]
group_tags.get_tag_users_all()list[User]
important_messages.list_important_messages_all()list[Message]
members.list_members_all()list[User]
messages.list_chat_messages_all()list[Message]
reactions.list_reactions_all()list[Reaction]
search.search_chats_all()list[Chat]
search.search_messages_all()list[Message]
search.search_users_all()list[User]
tasks.list_tasks_all()list[Task]
threads.list_threads_all()list[Thread]
users.list_users_all()list[User]

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

SDK выбрасывает два типа исключений:

ApiError

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

from pachca.models import ApiError try:    await client.chats.create_chat(request)except ApiError as error:    for e in error.errors:        print(e.key, e.message)   # name не может быть пустым        print(e.code)             # blank

Поля ApiErrorItem:

ПолеТипОписание
keystrПоле, вызвавшее ошибку
valueAny | NoneПереданное значение
messagestrТекст ошибки
codeValidationErrorCodeКод валидации
payloaddict[str, Any] | NoneДополнительные данные

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

OAuthError

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

from pachca.models import OAuthError try:    await client.profile.get_profile()except OAuthError as error:    print(error.error)              # "insufficient_scope"    print(error.error_description)  # описание ошибки

Поле error — перечисление OAuthErrorCode: его сравнивают с элементом, например OAuthErrorCode.INSUFFICIENT_SCOPE, или со строкой.

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

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

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

Сетевую ошибку без ответа сервера SDK не повторяет: исключение httpx приходит сразу. Повторы делает RetryTransport, обёртка над транспортом httpx.

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

Типы

Все модели — Python dataclass'ы с type hints:

from pachca.models 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,)

Поле-перечисление в ответе приходит элементом своего перечисления, в том числе со значением, которого SDK ещё не знает: у такого элемента в value исходная строка, а is_known равно False. Так же работает и вызов вроде OAuthScope("x"): проверить, знает ли SDK значение, помогает is_known.

Тело вебхука

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

import json from pachca.models import ButtonWebhookPayload, WebhookPayloadUnionfrom pachca.utils import deserialize # raw_body — тело запроса в байтахpayload = deserialize(WebhookPayloadUnion, json.loads(raw_body))if isinstance(payload, ButtonWebhookPayload):    print(payload.message_id, payload.data)

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

Зависимости

ПакетВерсияНазначение
httpx>=0.23.0, <0.29.0Асинхронный HTTP-клиент

Примеры

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

from pachca.client import PachcaClientfrom pachca.models import MessageCreateRequest, MessageCreateRequestMessage client = PachcaClient("YOUR_TOKEN") request = MessageCreateRequest(message=MessageCreateRequestMessage(entity_id=334, content="Вчера мы продали 756 футболок (что на 10% больше, чем в прошлое воскресенье)"))response = await client.messages.create_message(request=request)# → Message(id: int, entity_type: MessageEntityType, entity_id: int, chat_id: int, root_chat_id: int, content: str, user_id: int, created_at: datetime, url: str, files: list[File(id: int, key: str, name: str, file_type: FileType, url: str, width: int | None, height: int | None, duration_ms: int | None)], voice_content: VoiceContent(duration_ms: int, waveform: str, transcript: str | None, transcript_status: VoiceTranscriptStatus | None) | None, buttons: list[list[Button(text: str, url: str | None, data: str | None)]] | None, thread: MessageThread(id: int, chat_id: int) | None, forwarding: Forwarding(original_message_id: int, original_chat_id: int, author_id: int, original_created_at: datetime, original_thread_id: int | None, original_thread_message_id: int | None, original_thread_parent_chat_id: int | None) | None, parent_message_id: int | None, display_avatar_url: str | None, display_name: str | None, changed_at: datetime | None, deleted_at: datetime | None, important: bool)

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

from pachca.client import PachcaClientfrom pachca.models import ListUsersParams client = PachcaClient("YOUR_TOKEN") params = ListUsersParams(    query="Олег",    limit=1,    cursor="eyJpZCI6MTAsImRpciI6ImFzYyJ9")response = await client.users.list_users(params=params)# → ListUsersResponse(data: list[User], meta: PaginationMeta)

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

from datetime import datetime from pachca.client import PachcaClientfrom pachca.models import TaskCreateRequest, TaskCreateRequestCustomProperty, TaskCreateRequestTask, TaskKind client = PachcaClient("YOUR_TOKEN") request = TaskCreateRequest(    task=TaskCreateRequestTask(        kind=TaskKind.REMINDER,        content="Забрать со склада 21 заказ",        due_at=datetime.fromisoformat("2020-06-05T12:00:00.000+03:00"),        priority=2,        performer_ids=[123],        chat_id=456,        all_day=False,        custom_properties=[TaskCreateRequestCustomProperty(id=78, value="Синий склад")]    ))response = await client.tasks.create_task(request=request)# → Task(id: int, kind: TaskKind, content: str, due_at: datetime | None, priority: int, user_id: int, chat_id: int | None, status: TaskStatus, created_at: datetime, performer_ids: list[int], all_day: bool, custom_properties: list[CustomProperty(id: int, name: str, data_type: CustomPropertyDataType, value: str | None)])