CSharp

Типизированный клиент для Pachca API. Работает в .NET 8+ с поддержкой async/await и CancellationToken.

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

Установка

dotnet add package Pachca.Sdk

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

Получите API-токен в интерфейсе Пачки: Настройки > Автоматизации > API (подробнее — Авторизация).

using Pachca.Sdk; using var client = new PachcaClient("YOUR_TOKEN");

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

using Pachca.Sdk; // Получение профиляvar response = await client.Profile.GetProfileAsync();// → User(Id: int, FirstName: string, LastName: string, Nickname: string, Email: string, PhoneNumber: string, Department: string, Title: string, Role: UserRole, Suspended: bool, InviteStatus: InviteStatus, ListTags: List<string>, CustomProperties: List<CustomProperty(Id: int, Name: string, DataType: CustomPropertyDataType, Value: string)>, UserStatus: UserStatus(Emoji: string, Title: string, ExpiresAt: DateTimeOffset?, IsAway: bool, AwayMessage: UserStatusAwayMessage(Text: string)?)?, Bot: bool, Sso: bool, CreatedAt: DateTimeOffset, LastActivityAt: DateTimeOffset, TimeZone: string, ImageUrl: string?)

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

using Pachca.Sdk; // Стандартное подключениеusing var client = new PachcaClient("YOUR_TOKEN"); // С кастомным базовым URLusing var client = new PachcaClient("YOUR_TOKEN", "https://custom-api.example.com/api/shared/v1");
ПараметрТипПо умолчаниюОписание
tokenstringBearer-токен для авторизации
baseUrlstringhttps://api.pachca.com/api/shared/v1Базовый URL API

Клиент реализует IDisposable — рекомендуется использовать using для автоматического освобождения ресурсов.

Все методы

МетодМетод API
client.Common.RequestExportAsync()POSTЭкспорт сообщений
client.Common.UploadFileAsync()POSTЗагрузка файла
client.Common.GetUploadParamsAsync()POSTПолучение подписи, ключа и других параметров
client.Common.DownloadExportAsync()GETСкачать архив экспорта
client.Common.ListPropertiesAsync()GETСписок дополнительных полей
client.Profile.GetTokenInfoAsync()GETИнформация о токене
client.Profile.GetProfileAsync()GETИнформация о профиле
client.Profile.GetStatusAsync()GETТекущий статус
client.Profile.UpdateStatusAsync()PUTНовый статус
client.Profile.DeleteStatusAsync()DELETEУдаление статуса
client.Users.CreateUserAsync()POSTСоздать сотрудника
client.Users.ListUsersAsync()GETСписок сотрудников
client.Users.GetUserAsync()GETИнформация о сотруднике
client.Users.GetUserStatusAsync()GETСтатус сотрудника
client.Users.UpdateUserAsync()PUTРедактирование сотрудника
client.Users.UpdateUserStatusAsync()PUTНовый статус сотрудника
client.Users.DeleteUserAsync()DELETEУдаление сотрудника
client.Users.DeleteUserStatusAsync()DELETEУдаление статуса сотрудника
client.GroupTags.CreateTagAsync()POSTНовый тег
client.GroupTags.ListTagsAsync()GETСписок тегов сотрудников
client.GroupTags.GetTagAsync()GETИнформация о теге
client.GroupTags.GetTagUsersAsync()GETСписок сотрудников тега
client.GroupTags.UpdateTagAsync()PUTРедактирование тега
client.GroupTags.DeleteTagAsync()DELETEУдаление тега
client.Chats.CreateChatAsync()POSTНовый чат
client.Chats.ListChatsAsync()GETСписок чатов
client.Chats.GetChatAsync()GETИнформация о чате
client.Chats.UpdateChatAsync()PUTОбновление чата
client.Chats.ArchiveChatAsync()PUTАрхивация чата
client.Chats.UnarchiveChatAsync()PUTРазархивация чата
client.Members.AddTagsAsync()POSTДобавление тегов
client.Members.AddMembersAsync()POSTДобавление пользователей
client.Members.ListMembersAsync()GETСписок участников чата
client.Members.UpdateMemberRoleAsync()PUTРедактирование роли
client.Members.RemoveTagAsync()DELETEИсключение тега
client.Members.LeaveChatAsync()DELETEВыход из беседы или канала
client.Members.RemoveMemberAsync()DELETEИсключение пользователя
client.Threads.CreateThreadAsync()POSTНовый тред
client.Threads.GetThreadAsync()GETИнформация о треде
client.Messages.CreateMessageAsync()POSTНовое сообщение
client.Messages.PinMessageAsync()POSTЗакрепление сообщения
client.Messages.ListChatMessagesAsync()GETСписок сообщений чата
client.Messages.GetMessageAsync()GETИнформация о сообщении
client.Messages.UpdateMessageAsync()PUTРедактирование сообщения
client.Messages.DeleteMessageAsync()DELETEУдаление сообщения
client.Messages.UnpinMessageAsync()DELETEОткрепление сообщения
client.ReadMembers.ListReadMembersAsync()GETСписок прочитавших сообщение
client.Reactions.AddReactionAsync()POSTДобавление реакции
client.Reactions.ListReactionsAsync()GETСписок реакций
client.Reactions.RemoveReactionAsync()DELETEУдаление реакции
client.LinkPreviews.CreateLinkPreviewsAsync()POSTUnfurl (разворачивание ссылок)
client.Search.SearchChatsAsync()GETПоиск чатов
client.Search.SearchMessagesAsync()GETПоиск сообщений
client.Search.SearchUsersAsync()GETПоиск сотрудников
client.Tasks.CreateTaskAsync()POSTНовое напоминание
client.Tasks.ListTasksAsync()GETСписок напоминаний
client.Tasks.GetTaskAsync()GETИнформация о напоминании
client.Tasks.UpdateTaskAsync()PUTРедактирование напоминания
client.Tasks.DeleteTaskAsync()DELETEУдаление напоминания
client.Views.OpenViewAsync()POSTОткрытие представления
client.Bots.GetWebhookEventsAsync()GETИстория событий
client.Bots.UpdateBotAsync()PUTРедактирование бота
client.Bots.DeleteWebhookEventAsync()DELETEУдаление события
client.Security.GetAuditEventsAsync()GETЖурнал аудита событий

Запросы

Все методы — асинхронные и возвращают Task<T>.

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

using Pachca.Sdk; // Список чатовvar response = await client.Chats.ListChatsAsync(SortOrder.Desc, ChatAvailability.IsMember, "2025-01-01T00:00:00.000Z", "2025-02-01T00:00:00.000Z", false, 1, "eyJpZCI6MTAsImRpciI6ImFzYyJ9");// → ListChatsResponse(Data: List<Chat>, Meta: PaginationMeta?)

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

using Pachca.Sdk; // Создание чатаvar request = new ChatCreateRequest{    Chat = new ChatCreateRequestChat    {        Name = "🤿 aqua",        MemberIds = new List<int> { 123 },        GroupTagIds = new List<int> { 123 },        Channel = true,        @Public = false    }};var response = await client.Chats.CreateChatAsync(request);// → Chat(Id: int, Name: string, CreatedAt: DateTimeOffset, OwnerId: int, MemberIds: List<int>, GroupTagIds: List<int>, Channel: bool, Personal: bool, @Public: bool, LastMessageAt: DateTimeOffset, MeetRoomUrl: string)

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

using Pachca.Sdk; // Получение чатаvar response = await client.Chats.GetChatAsync(334);// → Chat(Id: int, Name: string, CreatedAt: DateTimeOffset, OwnerId: int, MemberIds: List<int>, GroupTagIds: List<int>, Channel: bool, Personal: bool, @Public: bool, LastMessageAt: DateTimeOffset, MeetRoomUrl: string)

Пагинация

Методы, возвращающие списки, используют cursor-based пагинацию. Ответ содержит поле Meta.Paginate.NextPage — курсор для следующей страницы.

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

var chats = new List<Chat>();string? cursor = null; do{    var response = await client.Chats.ListChatsAsync(cursor: cursor);    chats.AddRange(response.Data);    cursor = response.Meta?.Paginate?.NextPage;} while (cursor != null);

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

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

// Все чаты одним спискомvar chats = await client.Chats.ListChatsAllAsync();Console.WriteLine($"Всего: {chats.Count}");

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

МетодВозвращает
Security.GetAuditEventsAllAsync()List<AuditEvent>
Bots.GetWebhookEventsAllAsync()List<WebhookEvent>
Chats.ListChatsAllAsync()List<Chat>
GroupTags.ListTagsAllAsync()List<GroupTag>
GroupTags.GetTagUsersAllAsync()List<User>
Members.ListMembersAllAsync()List<User>
Messages.ListChatMessagesAllAsync()List<Message>
Reactions.ListReactionsAllAsync()List<Reaction>
Search.SearchChatsAllAsync()List<Chat>
Search.SearchMessagesAllAsync()List<Message>
Search.SearchUsersAllAsync()List<User>
Tasks.ListTasksAllAsync()List<Task>
Users.ListUsersAllAsync()List<User>

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

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

ApiError

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

using Pachca.Sdk; try{    await client.Chats.CreateChatAsync(request);}catch (ApiError e){    foreach (var err in e.Errors)    {        Console.WriteLine($"{err.Key}: {err.Message}");  // "name", "не может быть пустым"        Console.WriteLine(err.Code);                     // "blank"    }}

Поля ApiErrorItem:

ПолеТипОписание
KeystringПоле, вызвавшее ошибку
Valuestring?Переданное значение
MessagestringТекст ошибки
CodeValidationErrorCodeКод валидации (Blank, Invalid, Taken, ...)
Payloadstring?Дополнительные данные

OAuthError

Возникает при ошибке авторизации (401):

using Pachca.Sdk; try{    await client.Profile.GetProfileAsync();}catch (OAuthError e){    Console.WriteLine(e.ErrorDescription); // "Token not found"}

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

SDK автоматически повторяет запрос при получении 429 Too Many Requests и ошибок сервера 5xx:

  • До 3 повторов на каждый запрос
  • Если сервер вернул заголовок Retry-After — ждёт указанное время
  • Иначе — экспоненциальный backoff: 1 сек, 2 сек, 4 сек
  • Ошибки клиента (4xx, кроме 429) возвращаются сразу без повторов

Отмена запросов

Все асинхронные методы поддерживают CancellationToken:

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));var users = await client.Users.ListUsersAsync(cancellationToken: cts.Token);

Загрузка файлов

Загрузка файла — трёхшаговый процесс:

// 1. Получить параметры загрузкиvar uploadParams = await client.Common.GetUploadParamsAsync(); // 2. Загрузить файл на S3using var fileStream = File.OpenRead("photo.png");await client.Common.UploadFileAsync(uploadParams, fileStream, "photo.png"); // 3. Прикрепить к сообщению (используя key из uploadParams)await client.Messages.CreateMessageAsync(new MessageCreateRequest{    Message = new MessageCreateRequestMessage    {        EntityId = chatId,        Content = "Фото",        Files = new List<MessageFile> { new() { Key = uploadParams.Key, Name = "photo.png" } }    }});

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

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

// Вы пишете:new ChatCreateRequestChat { MemberIds = new List<int> { 123 }, GroupTagIds = new List<int> { 456 } } // SDK отправляет:{ "member_ids": [123], "group_tag_ids": [456] } // API возвращает:{ "last_message_at": "2025-01-01T00:00:00Z" } // Вы получаете:chat.LastMessageAt // DateTimeOffset

Типы

Все типы доступны из пространства имён Pachca.Sdk:

using Pachca.Sdk; // МоделиChat chat;Message message;User user;Task task;GroupTag groupTag; // ЗапросыChatCreateRequest chatRequest;MessageCreateRequest messageRequest;TaskCreateRequest taskRequest; // ПеречисленияAuditEventKey eventKey;ChatAvailability availability;ChatMemberRole role;TaskStatus status;ValidationErrorCode errorCode; // ОшибкиApiError apiError;OAuthError oauthError;

Примеры

using Pachca.Sdk; using var client = new PachcaClient("YOUR_TOKEN"); // Отправка сообщенияvar request = new MessageCreateRequest{    Message = new MessageCreateRequestMessage    {        EntityType = MessageEntityType.Discussion,        EntityId = 334,        Content = "Вчера мы продали 756 футболок (что на 10% больше, чем в прошлое воскресенье)",        Files = new List<MessageCreateRequestFile> { new MessageCreateRequestFile        {            Key = "attaches/files/93746/e354fd79-4f3e-4b5a-9c8d-1a2b3c4d5e6f/logo.png",            Name = "logo.png",            FileType = FileType.Image,            Size = 12345,            Width = 800,            Height = 600        } },        Buttons = new List<List<Button>> { new List<Button> { new Button        {            Text = "Подробнее",            Url = "https://example.com/details",            Data = "awesome"        } } },        ParentMessageId = 194270,        DisplayAvatarUrl = "https://example.com/avatar.png",        DisplayName = "Бот Поддержки",        SkipInviteMentions = false    },    LinkPreview = false};var response = await client.Messages.CreateMessageAsync(request);// → Message(Id: int, EntityType: MessageEntityType, EntityId: int, ChatId: int, RootChatId: int, Content: string, UserId: int, CreatedAt: DateTimeOffset, Url: string, Files: List<File(Id: int, Key: string, Name: string, FileType: FileType, Url: string, Width: int?, Height: int?)>, Buttons: List<List<Button(Text: string, Url: string?, Data: string?)>>?, Thread: MessageThread(Id: long, ChatId: long)?, Forwarding: Forwarding(OriginalMessageId: int, OriginalChatId: int, AuthorId: int, OriginalCreatedAt: DateTimeOffset, OriginalThreadId: int?, OriginalThreadMessageId: int?, OriginalThreadParentChatId: int?)?, ParentMessageId: int?, DisplayAvatarUrl: string?, DisplayName: string?, ChangedAt: DateTimeOffset?, DeletedAt: DateTimeOffset?) // Список сотрудниковvar response = await client.Users.ListUsersAsync("Олег", 1, "eyJpZCI6MTAsImRpciI6ImFzYyJ9");// → ListUsersResponse(Data: List<User>, Meta: PaginationMeta?) // Создание задачиvar request = new TaskCreateRequest{    Task = new TaskCreateRequestTask    {        Kind = TaskKind.Reminder,        Content = "Забрать со склада 21 заказ",        DueAt = "2020-06-05T12:00:00.000+03:00",        Priority = 2,        PerformerIds = new List<int> { 123 },        ChatId = 456,        AllDay = false,        CustomProperties = new List<TaskCreateRequestCustomProperty> { new TaskCreateRequestCustomProperty { Id = 78, Value = "Синий склад" } }    }};var response = await client.Tasks.CreateTaskAsync(request);// → Task(Id: int, Kind: TaskKind, Content: string, DueAt: DateTimeOffset?, Priority: int, UserId: int, ChatId: int?, Status: TaskStatus, CreatedAt: DateTimeOffset, PerformerIds: List<int>, AllDay: bool, CustomProperties: List<CustomProperty(Id: int, Name: string, DataType: CustomPropertyDataType, Value: string)>)