Синхронизация вашей системы через свой источник
Свой источник подключает систему, для которой нет встроенного коннектора: вашу вики, систему обращений, базу данных, выгрузку откуда угодно. Ваш код отправляет документы в Sursa, а Sursa превращает их в карточки в той коллекции и тех папках, на которые настроен источник, — с тем же поиском, ответами, верификацией и правами доступа, что и у карточек из любого другого источника.
В отличие от прямой записи карточек, свой источник помнит, что вы отправили. В конце полной синхронизации Sursa отправляет в архив все документы, которые вы не прислали, поэтому удаления в вашей системе доходят до Sursa без дополнительной работы.
Настройка источника
Администратор создаёт источник в Sursa в разделе Источники → Новый источник → Свой источник и выбирает:
- коллекцию и, если нужно, папку для карточек источника;
- режим папок: все карточки в выбранной папке или вложенные папки, повторяющие
pathкаждого документа; - права доступа: права коллекции или «как в источнике» — тогда каждый документ сам перечисляет, кто может его видеть.
На странице источника виден его идентификатор; в примерах это 42.
Выбор ключа
| Ключ | Начинается с | Что может |
|---|---|---|
| Ключ источника | kbsrc_ | Записывать документы в этот один источник. И больше ничего: он не может читать или искать. Создайте его на странице источника в разделе Ключи API. |
Ключ организации с областью import:write | kb_ | Записывать в любой свой источник организации и читать его состояние. Для создания источника и его ключей нужна область 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.- Одновременно может быть открыта только одна синхронизация: вторая возвращает
409sources.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} возвращает те же счётчики.
Инкрементальные синхронизации
Между полными синхронизациями отправляйте только изменения:
- Откройте синхронизацию с
"type": "incremental". - Отправьте изменившиеся документы, а идентификаторы удалённых поместите в
deletes(см. ниже). - Закройте с
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 МиБ |
Идентификаторов в запросе touch | 1000 |
| Документов в источнике | Зависит от тарифа: от 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; отправляйте меньше пакетов, но крупнее. |