Перейти к содержанию
Sursa для разработчиков

Синхронизация вашей системы через свой источник

Свой источник подключает систему, для которой нет встроенного коннектора: вашу вики, систему обращений, базу данных, выгрузку откуда угодно. Ваш код отправляет документы в Sursa, а Sursa превращает их в карточки в той коллекции и тех папках, на которые настроен источник, — с тем же поиском, ответами, верификацией и правами доступа, что и у карточек из любого другого источника.

В отличие от прямой записи карточек, свой источник помнит, что вы отправили. В конце полной синхронизации Sursa отправляет в архив все документы, которые вы не прислали, поэтому удаления в вашей системе доходят до Sursa без дополнительной работы.

Настройка источника

Администратор создаёт источник в Sursa в разделе Источники → Новый источник → Свой источник и выбирает:

  • коллекцию и, если нужно, папку для карточек источника;
  • режим папок: все карточки в выбранной папке или вложенные папки, повторяющие path каждого документа;
  • права доступа: права коллекции или «как в источнике» — тогда каждый документ сам перечисляет, кто может его видеть.

На странице источника виден его идентификатор; в примерах это 42.

Выбор ключа

КлючНачинается сЧто может
Ключ источникаkbsrc_Записывать документы в этот один источник. И больше ничего: он не может читать или искать. Создайте его на странице источника в разделе Ключи API.
Ключ организации с областью import:writekb_Записывать в любой свой источник организации и читать его состояние. Для создания источника и его ключей нужна область admin.

Ключ источника — самый безопасный выбор для отдельной интеграции, особенно если она работает на сторонней платформе. Ключ организации с import:write подходит сервису, который наполняет несколько источников.

Оба ключа передаются одинаково, и все push-операции находятся внутри /v1/push/sources/{source_id}. Проверьте ключ и состояние источника через GET /v1/push/sources/{source_id}:

curl https://api.sursa.com/v1/push/sources/42 \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY"
{"source_id": 42, "name": "Helpdesk articles", "documents": 1840, "open_sync": null}

Документ

ПолеОбязательноеОписание
external_idдаВаш постоянный идентификатор документа, до 512 символов. Повторная отправка с тем же идентификатором заменяет документ.
titleдаЗаголовок карточки, до 500 символов. Для файла по умолчанию — имя файла.
contentда, если нет файлаДо 5 МБ.
content_typeнетhtml (по умолчанию), markdown или text. Из HTML удаляются скрипты, стили, формы и встроенные объекты.
urlнетСсылка на документ в вашей системе, показывается на карточке.
pathнетПапки — массивом (["Support", "Billing"]) или строкой ("Support/Billing"), до 10 уровней.
permissionsнет{"everyone": true} или {"emails": [...], "groups": [...]}. Используется, когда права источника — «как в источнике».
tagsнетДо 50 тегов, они добавляются к тегам источника.
author_emailнетЕсли это пользователь организации, он становится владельцем карточки.
updated_atнетКогда документ изменился в вашей системе (RFC 3339).
metadataнетВаши пары «ключ — значение» у карточки. См. Метаданные.
categoryнетКатегория карточки: ключ одной из категорий организации.
verifiers, verify_interval_daysнетКто верифицирует карточку (пользователи по e-mail и группы) и как часто, в днях.
upload_idнетФайл документа из загрузки по частям вместо content.
attachmentsнетДо 20 дополнительных файлов документа — списком upload_id. Каждый становится отдельной карточкой источника.

Вместо content отдельный документ может нести файл размером до 100 МБ в запросе multipart/form-data: файл — в части file, поля выше в виде JSON — в части document. Загрузки по частям для upload_id и attachments создаются ключом организации с областью import:write и назначением import_attachment; ключ источника создать их не может.

Два способа отправки

Прямые обновления подходят системам, которые сообщают о каждом изменении, например вебхуку вашей службы поддержки или шагу в Zapier. Отправляйте документ, когда он создан или изменён, и удаляйте, когда его убрали:

ЗапросЧто делает
POST /v1/push/sources/{source_id}/documentsСоздаёт или заменяет один документ.
POST /v1/push/sources/{source_id}/documents/batchСоздаёт или заменяет много документов сразу.
DELETE /v1/push/sources/{source_id}/documents/{external_id}Удаляет один документ. Кодируйте идентификатор для URL.

Изменения становятся карточками примерно за минуту; несколько изменений подряд обрабатываются вместе.

Синхронизации подходят для регулярных выгрузок: вы отправляете всё или всё изменившееся, а Sursa сверяет. Остальная часть руководства посвящена синхронизациям.

Полная синхронизация

Синхронизация состоит из трёх шагов: открыть, отправить, закрыть.

1. Открытие

POST /v1/push/sources/{source_id}/syncs открывает синхронизацию и возвращает её номер:

curl -X POST https://api.sursa.com/v1/push/sources/42/syncs \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Idempotency-Key: sync-2026-10-24" \
  -H "Content-Type: application/json" \
  -d '{"type": "initial", "max_minutes": 180}'
{"sync_number": 7, "type": "initial", "status": "open", "opened_at": "2026-10-24T02:00:00Z",
 "expires_at": "2026-10-24T05:00:00Z", "pushed": 0, "removed": 0}
  • type — initial для первой синхронизации источника и incremental после того, как хотя бы одна синхронизация завершилась. Синхронизация incremental до первой завершённой возвращает sources.push_initial_required.
  • Одновременно может быть открыта только одна синхронизация: вторая возвращает 409 sources.push_sync_open.
  • Синхронизация остаётся открытой max_minutes минут (по умолчанию 60, до 1440). После этого её срок истекает: ничего не отправляется в архив, а её номер больше не принимается. Выбирайте время с хорошим запасом сверх длительности вашей выгрузки.

2. Отправка документов

Отправляйте документы пакетами до 500 штук вместе с номером синхронизации. Тело пакета — до 50 МиБ; файлы внутри пакета передаются как upload_id.

curl -X POST https://api.sursa.com/v1/push/sources/42/documents/batch \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Idempotency-Key: sync-7-batch-1" \
  -H "Content-Type: application/json" \
  -d '{
    "sync_number": 7,
    "documents": [
      {"external_id": "faq-shipping", "title": "Shipping", "content_type": "markdown",
       "content": "# Shipping\n\nWe ship worldwide within 5 days.", "path": ["Support"]},
      {"external_id": "faq-refunds", "title": "Refunds", "content_type": "html",
       "content": "<p>Refunds take 14 days.</p>", "path": ["Support", "Billing"],
       "permissions": {"groups": ["Support"]}, "metadata": {"article_id": "1042"}}
    ]
  }'

В ответе есть результат по каждому документу — в том порядке, в котором вы их отправили:

{
  "results": [
    {"external_id": "faq-shipping", "result": "unchanged", "status": "indexed", "card_id": "8c1d…", "version": "5e0f…"},
    {"external_id": "faq-refunds", "result": "updated", "status": "pending", "card_id": "3f2c…", "version": "a91b…"}
  ]
}
ПолеЗначение
resultЧто сейчас произошло с документом: created, updated, unchanged, deleted, not_found, error или skipped (в атомарном пакете, который не применён).
statusНа каком этапе пути к карточке документ: pending, processing (карточка записана и индексируется), indexed, failed или removed.
card_idКарточка документа, когда она уже существует.
versionХеш содержимого и полей. Тот же документ, отправленный снова, имеет ту же версию и получает unchanged.
error, reasonПочему документ не обработан. reason — стабильный код: content_invalid, file_rejected, too_large, limit_reached, index_failed или batch_rejected (не прошёл другой документ атомарного пакета).

По умолчанию каждый документ пакета применяется отдельно: один неудачный документ не останавливает остальные.

Документы, которые не изменились с прошлой синхронизации, отправлять заново не нужно. Подтвердите их по идентификатору через POST /v1/push/sources/{source_id}/syncs/{sync_number}/touch, до 1000 идентификаторов на запрос, чтобы полная сверка их сохранила:

curl -X POST https://api.sursa.com/v1/push/sources/42/syncs/7/touch \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_ids": ["faq-contact", "faq-hours"]}'

3. Закрытие

POST /v1/push/sources/{source_id}/syncs/{sync_number}/close завершает синхронизацию:

curl -X POST https://api.sursa.com/v1/push/sources/42/syncs/7/close \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "complete"}'
statusРезультат
completeПолная сверка. Каждый документ источника, который в этой синхронизации не был ни отправлен, ни подтверждён, отправляется в архив. Используйте, только если отправили всё.
complete_incrementalПрименяет то, что вы отправили, и ничего не отправляет в архив.
failedНичего не отправляет в архив и сохраняет то, что уже отправлено. Добавьте reason (например, API_ERROR) и error_details для журнала.

Закрывайте с failed каждый раз, когда выгрузка останавливается на полпути: complete после частичной выгрузки отправит в архив все документы, до которых вы не дошли. Повтор того же закрытия возвращает тот же результат.

После закрытия Sursa превращает документы в карточки примерно за минуту. На странице источника показана каждая синхронизация с числом отправленных и заархивированных документов, а GET /v1/push/sources/{source_id}/syncs/{sync_number} возвращает те же счётчики.

Инкрементальные синхронизации

Между полными синхронизациями отправляйте только изменения:

  1. Откройте синхронизацию с "type": "incremental".
  2. Отправьте изменившиеся документы, а идентификаторы удалённых поместите в deletes (см. ниже).
  3. Закройте с complete_incremental.

Время от времени, например раз в неделю, выполняйте полную синхронизацию, чтобы поймать всё, что пропустил инкрементальный запуск.

Удаление документов в пакете

Пакет может удалять документы вместе с теми, которые записывает. Перечислите их идентификаторы в deletes:

{
  "sync_number": 8,
  "documents": [{"external_id": "faq-shipping", "title": "Shipping", "content": "…"}],
  "deletes": ["faq-old-returns", "faq-fax"]
}

Каждый удалённый идентификатор получает свою запись в results с result deleted или not_found, если такого документа в источнике нет. Повторное удаление того же идентификатора ничему не вредит.

Атомарные пакеты

Некоторые изменения имеют смысл только вместе, например документ и тот, что его заменяет. Передайте "atomic": true, и пакет применится целиком или не применится вовсе: если хоть один документ не прошёл проверки, ничего из пакета не применяется, а ответ — 422 с кодом sources.push_batch_rejected и результатом по каждому документу в error.details.results: у документов, которые не прошли, — error, у остальных — skipped.

{"sync_number": 8, "atomic": true, "documents": […], "deletes": ["faq-old-returns"]}

Без atomic пакет применяется документ за документом. Используйте атомарные пакеты для связанных изменений, а обычные — для массовых загрузок, где один неудачный документ не должен задерживать остальные.

Результат по каждому документу

Ответ на пакет сообщает, как записан каждый документ. Превращение в карточку происходит позже, поэтому status ещё может быть pending. Чтобы найти документы синхронизации, которые не стали карточками:

curl "https://api.sursa.com/v1/push/sources/42/syncs/7/documents?status=failed" \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY"

У каждой записи есть external_id, title, status, card_id, version и reason, а список читается по страницам через cursor. status принимает также pending, processing, indexed и removed.

Можно не спрашивать, а получать уведомления: события import.sync_completed (синхронизация закрыта и обработана, с итогами) и import.document_failed (документ не стал карточкой, с его reason) приходят через вебхук и в ленту событий.

Повторы

  • Передавайте Idempotency-Key с каждым POST. Повторный пакет с тем же ключом возвращает первый результат.
  • Даже без ключа документ с тем же external_id никогда не станет второй карточкой: он получит unchanged или updated.
  • Если процесс остановился посреди синхронизации, откройте новую после того, как истечёт срок старой, или закройте старую с failed, если её номер у вас сохранился, и выполните синхронизацию снова.

Ограничения

ОграничениеЗначение
Содержимое одного документа5 МБ
Файл в запросе с одним документом100 МБ
Документов в пакете500
Тело запроса с пакетом50 МиБ
Идентификаторов в запросе touch1000
Документов в источникеЗависит от тарифа: от 100 000
Открытая синхронизацияПо умолчанию 60 минут, до 24 часов

Документ сверх лимита документов источника в пакете не обрабатывается с причиной limit_reached; запрос с одним документом получает ответ 409 с кодом sources.push_too_many_documents. Лимиты частоты действуют так же, как для любого другого запроса; см. Лимиты частоты и квоты.

Частые ошибки

ОтветПричинаЧто делать
401 sources.push_key_invalidКлюч неверен, отозван или принадлежит другому источнику.Проверьте ключ и идентификатор источника в пути.
400 sources.push_document_invalidНе хватает обязательного поля или превышено ограничение.Исправьте документ; в обычном пакете ошибкой завершится только он.
409 sources.push_sync_openОткрыта другая синхронизация.Закройте её или дождитесь, пока истечёт её срок.
409 sources.push_sync_closedСинхронизация закрыта, или её срок истёк.Откройте новую синхронизацию; для долгих выгрузок увеличьте max_minutes.
403 sources.feature_disabledИсточники не входят в тариф организации.Обратитесь к администратору.
429Слишком много запросов.Дождитесь Retry-After; отправляйте меньше пакетов, но крупнее.