ОбзорОбзор API

Обзор

Обзор API

Каждый endpoint Sofia API v1 с параметрами, scope и примерами, а также playground, чтобы проверить любой из них с вашим собственным ключом API.

Базовый URL https://api.sofia-post.com/api · Документ OpenAPI

Sofia API даёт вашему коду доступ к вашему аккаунту Sofia: заведениям ваших Workspaces, их подключённым сетям, медиатеке, публикациям и статистике. Это REST API по HTTPS; запросы и ответы — в JSON. Оно позволяет:

  • Публиковать: сейчас, в назначенную дату или черновиком, в одну или несколько сетей сразу.
  • Проверять перед публикацией: симуляция сообщает, приняла бы каждая сеть публикацию, ничего не записывая.
  • Управлять медиа: загружайте файл или импортируйте его по URL в медиатеку заведения.
  • Отслеживать публикации: читайте статус каждой сети, её постоянную ссылку или причину сбоя. Подписанный вебхук тоже может вас уведомить.
  • Читать статистику: последние цифры, собранные Sofia по каждому подключённому аккаунту.
  • Подключать сети: создайте размещённую ссылку, по которой владелец подключит недостающую сеть.

SDK и интеграции

  • CLI `sofia`: CLI, опубликованный в npm под именем sofia-post, для управления Sofia из терминала.
  • Сервер MCP: подключите Sofia к вашим ИИ-агентам.
  • Zapier: интеграция Sofia подключается с вашим личным ключом. Она предлагает триггеры «Post Published» и «Post Failed», действие «Create Post» (опубликовать, запланировать или сохранить черновик) и поиск «Find Post». Она ещё не подана в публичный каталог Zapier.

Аутентификация

Каждый вызов несёт токен Bearer в заголовке Authorization. Sofia перечитывает связанные с ним права при каждом вызове: человек, удалённый из Workspace, или отозванное разрешение останавливает токен со следующего запроса.

Аутентифицироваться можно двумя способами:

СпособДляТокен
Личный ключАвтоматизации собственного аккаунта, скриптов, агентовsofia_pk_live_…, действителен до истечения срока
OAuth, код авторизации + PKCEПродукт, действующий от имени своих пользователей в выбранном ими Workspacesofia_live_at_…, один час, обновляемый
OAuth, client credentialsВаш сервер, автоматизирующий Workspace, владельцем которого вы являетесьsofia_live_at_…, один час

Ключ API

Если вы автоматизируете собственный Workspace, достаточно личного ключа. В приложении Sofia откройте Настройки › API, вкладку Личный доступ, и создайте ключ. Выберите его название, срок действия и разрешения. Ключ показывается один раз: скопируйте его и храните в хранилище секретов, но не в коде.

Создайте ключ в Настройки › API › Личный доступ. Он действует от вашего имени во всех ваших Workspaces: названное вами заведение определяет Workspace, а ваша роль в нём — что ключ может делать.

Передавайте ключ в заголовке Authorization как токен Bearer:

curl https://api.sofia-post.com/api/v1/establishments \
  -H "Authorization: Bearer sofia_pk_live_…"

`GET /v1/me` сообщает, от чьего имени действует ключ, его Workspaces и разрешения.

Вызов без нужного разрешения (scope) возвращает 403 insufficient_scope.

Токен OAuth2

Для SaaS-продукта, действующего от имени своих пользователей в их собственных Workspaces, зарегистрируйте клиента OAuth: каждый человек авторизует ваше приложение в выбранном им Workspace. Токены доступа OAuth начинаются с sofia_live_at_…, живут час и используются так же:

curl https://api.sofia-post.com/api/v1/establishments \
  -H "Authorization: Bearer sofia_live_at_…"

Создаёте приложение, к которому подключаются другие аккаунты Sofia? Его процесс authorization code с PKCE, client credentials и scopes описаны в OAuth2.

Базовый URL

Каждый endpoint находится под одним базовым URL:

https://api.sofia-post.com/api/v1

Лимиты запросов

Sofia API считает запросы в трёх окнах: 15 минут, 24 часа и 30 дней.

Токен15 минут24 часа30 дней
Личный ключ (на аккаунт)1501 00010 000
Клиент OAuth10050015 000
Клиент OAuth, на каждого авторизовавшего человека (до 1 000)+10+25+100

Заголовки

Каждый ответ сообщает, сколько осталось, в формате IETF:

RateLimit: "15m";r=144;t=43, "24h";r=994;t=13543, "30d";r=9994;t=13543
RateLimit-Policy: "15m";q=150;w=900, "24h";q=1000;w=86400, "30d";q=10000;w=2592000

r — число оставшихся запросов, t — секунды до сброса окна, q — квота, w — окно в секундах.

Когда окно заполнено

Ответ — 429 rate_limit_exceeded с заголовком Retry-After и исчерпанным окном в details. Подождите указанное время, прежде чем повторять.

Как эффективно использовать API

  • Зарегистрируйте вебхук вместо того, чтобы в цикле опрашивать публикацию.
  • Используйте limit до 100, чтобы читать списки за меньшее число страниц.
  • Кэшируйте то, что меняется редко: платформы, заведения, подключённые аккаунты.

Ошибки

Каждая ошибка возвращается со статусом HTTP и одним и тем же JSON-конвертом. code — контракт, который читает ваш код; message написано для человека и может меняться.

{
  "error": {
    "code": "invalid_request",
    "message": "The request is invalid",
    "details": [
      { "field": "targets[0].text", "code": "required", "message": "targets[0].text is required" }
    ],
    "docUrl": "https://www.sofia-post.com/developers/errors#invalid_request",
    "requestId": "2aab5943-6333-4caf-8478-25a85ebd4abf"
  }
}
  • details сообщает больше: отклонённое поле, причину сети, исчерпанное окно лимита.
  • docUrl ведёт к объяснению кода на этой странице.
  • requestId идентифицирует запрос: сообщите его в поддержку.

Коды ошибок

СтатусКодЗначение
400invalid_requestТело, запрос или параметр недопустимы. Одна запись на каждое отклонённое поле: field, code (required, is_string, max…) и message.
400invalid_jsonТело не является корректным JSON.
400invalid_cursorКурсор не был возвращён Sofia API.
400webhook_url_invalidАдрес webhook не https или ведёт на частный адрес.
400webhook_event_invalidНеизвестное событие webhook.
400application_name_invalidНастройки › API: название клиента отсутствует или слишком длинное.
400application_profile_invalidНастройки › API: одно из полей профиля клиента недопустимо; в подробностях указано, какое.
400logo_invalidНастройки › API: логотип отклонён (не менее 180×180 px, PNG, JPEG или WebP, не более 2 МБ).
401invalid_tokenТокен отсутствует, неизвестен, истёк или отозван, либо человек, стоящий за ним, потерял права, которые он несёт. См. заголовок WWW-Authenticate.
401unauthorizedТребуется аутентификация.
402subscription_requiredРеальная запись (включая пробный запуск) требует активной подписки или пробного периода.
403insufficient_scopeУ токена нет scope эндпоинта; он указан в деталях.
403insufficient_roleВаша роль в этом Workspace не позволяет это действие.
403live_mode_requiredРеальная запись с тестовым токеном.
403route_closed_to_applicationsЭтот эндпоинт недоступен приложениям.
403forbiddenДоступ запрещён.
403session_requiredНастройки › API: для этого действия нужна сессия Sofia, а не токен.
403impersonation_forbiddenНастройки › API: это действие отклоняется в заимствованной сессии.
403workspace_owner_requiredЭто может сделать только владелец Workspace.
403workspace_member_requiredВы не участник этого Workspace.
404route_not_foundНи один эндпоинт не отвечает на этот метод и путь.
404establishment_not_foundНеизвестное заведение или недоступно этому токену.
404media_not_foundНеизвестное медиа или недоступно этому токену.
404post_not_foundНеизвестная публикация или недоступна этому токену.
404webhook_not_foundНеизвестный webhook или принадлежит другому приложению.
404not_foundРесурс не существует.
404application_not_foundНастройки › API: неизвестный клиент.
404grant_not_foundНеизвестная авторизация или уже отозвана.
404personal_key_not_foundНастройки › API: неизвестный личный ключ.
405method_not_allowedЭтот метод не разрешён для этого эндпоинта.
409post_in_flightСеть отправляет публикацию: повторите удаление чуть позже.
409post_not_editableСеть уже отправила публикацию: её больше нельзя изменить.
409account_already_connectedЭта сеть уже подключена к заведению.
409webhook_limit_reachedУ приложения уже 10 webhook.
409idempotency_in_progressПервый запрос с этим Idempotency-Key ещё выполняется.
409conflictЗапрос противоречит текущему состоянию ресурса.
409scope_removal_forbiddenНастройки › API: разрешение клиента можно добавить, но нельзя удалить.
409public_client_has_no_secretНастройки › API: у публичного клиента нет секрета.
413payload_too_largeФайл превышает лимит загрузки.
415unsupported_media_typeНеподдерживаемый тип содержимого.
422post_refusedСеть, заведение или ваш тариф отклонили бы публикацию. Одна запись на каждую причину, у каждой свой message.
422media_import_refusedНе удалось импортировать медиа по этому URL; причина указана в деталях.
422media_upload_refusedЗагруженный файл не является поддерживаемым изображением или видео.
422platform_not_offeredЭту сеть нельзя подключить.
422idempotency_key_reusedЭтот Idempotency-Key уже использовался с другим телом.
422unprocessable_entityЗапрос понятен, но не может быть обработан.
429rate_limit_exceededОкно лимита заполнено: подождите время из Retry-After.

Причины отклонения публикации

post_refused перечисляет каждую причину в details[].code:

КодЗначение
unsupported_platformЭта сеть не поддерживается.
duplicate_platformОдна и та же сеть указана дважды.
unsupported_formatЭта сеть не предлагает этот формат.
caption_too_longТекст слишком длинный (max, actual).
too_many_hashtagsСлишком много хештегов (max, actual).
too_many_mentionsСлишком много упоминаний (max, actual).
too_many_thread_postsТред слишком длинный (max, actual).
too_many_mediaСлишком много медиа (max, actual).
media_mix_refusedВ этой сети нельзя смешивать фото и видео.
media_requiredЭтой сети нужно фото или видео.
media_type_refusedЭта сеть не принимает этот тип медиа.
scheduled_in_pastscheduledAt находится в прошлом.
scheduled_at_invalidЗапланированной публикации нужен будущий scheduledAt.
account_not_connectedДля этого заведения в этой сети нет подключённого аккаунта.
account_ambiguousВ этой сети подключено несколько аккаунтов: укажите один через accountId.
media_not_foundМедиа нет в медиатеке этого заведения.
media_url_invalidURL медиа должен быть публичным https-адресом.
platform_unavailableЭта сеть ещё не открыта в Sofia.
publication_quota_exceededКвота публикаций вашего тарифа исчерпана.

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

  • Стройте логику на code, а не на message.
  • Повторяйте 429 после Retry-After, а 5xx — с экспоненциальной задержкой.
  • Никогда не повторяйте 4xx без изменений: сначала исправьте запрос.
  • Записывайте requestId при каждой ошибке.

Пагинация

Списки (GET /v1/posts, GET /v1/media) возвращаются начиная с самых свежих, по одной странице. limit задаёт размер страницы от 1 до 100 (по умолчанию 20).

{
  "data": [ … ],
  "nextCursor": "WyIyMDI2LTEwLTI3VDE1OjExOjU5LjAxN1oiLCIx…"
}

Передайте nextCursor как cursor, чтобы прочитать следующую страницу. Когда nextCursor отсутствует, вы достигли конца.

let cursor;
do {
  const url = new URL('https://api.sofia-post.com/api/v1/posts');
  url.searchParams.set('limit', '100');
  if (cursor) url.searchParams.set('cursor', cursor);
  const page = await globalThis.fetch(url, { headers: { Authorization: `Bearer ${key}` } }).then((r) => r.json());
  handle(page.data);
  cursor = page.nextCursor;
} while (cursor);

Курсор непрозрачен: никогда не составляйте и не изменяйте его. Курсор, который Sofia не возвращала, получает ответ 400 invalid_cursor.

Идемпотентность

Сетевая ошибка может оставить вас в сомнении, создана ли публикация. Отправьте заголовок Idempotency-Key с POST /v1/posts и POST /v1/media и повторите с тем же ключом: Sofia вернёт то, что ответила в первый раз, и ничего не создаст дважды.

curl -X POST https://api.sofia-post.com/api/v1/posts \
  -H "Authorization: Bearer $SOFIA_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d @post.json
  • Используйте новый случайный ключ (UUID) для каждой новой публикации и тот же ключ для каждой повторной попытки этой публикации.
  • Тот же ключ с другим телом возвращает 422 idempotency_key_reused.
  • Пока первый запрос выполняется, тот же ключ возвращает 409 idempotency_in_progress: повторите чуть позже.

Тестовый режим и симуляция

Симуляция. Добавьте ?dry_run=true к POST /v1/posts: Sofia выполняет проверки настоящей публикации (правила каждой сети, подключённый аккаунт, медиа, дату и ваш тариф), ничего не сохраняя и не отправляя. Подробности ответов — на странице Публикация.

Тестовый режим. Приложение, созданное в режиме test (токены sofia_test_at_…), читает ваш настоящий Workspace, но никогда в него не пишет: его публикации симулируются и оцениваются по тем же правилам, а любая реальная запись возвращает 403 live_mode_required. Ключи и клиенты, созданные в Настройки › API, работают в режиме live; для проверки без публикации используйте симуляцию.

OAuth

Аккаунт

Платформы

Заведения

Медиа

Публикации

GET/v1/postsСписок публикацийПеречисляет публикации заведений, доступных токену, сначала новые, с фильтрами по заведению, статусу, датам и автору, постранично.POST/v1/postsОпубликовать публикациюLive-приложения публикуют сразу, планируют по дате или сохраняют черновик; тестовые только имитируют. С ?dry_run=true всё проверяется, ничего не записывается.GET/v1/posts/{id}Получить публикациюВозвращает публикацию с состоянием каждой сети, ссылкой после публикации и ошибкой. В live любая публикация Workspace, в тесте имитированная.PATCH/v1/posts/{id}Изменить публикациюПока ни одна сеть не начала отправку, заменяет targets, задаёт scheduledAt или переводит публикацию между draft и scheduled. Пропущенное сохраняется.DELETE/v1/posts/{id}Удалить публикациюУдаляет публикацию, как это делает приложение: запланированное не отправится, а опубликованное снимается с сети. У тестовых приложений нечего удалять.

Вебхуки