Аутентификация
Создайте ключ API в панели управления в разделе «Настройки», «API и вебхуки». Ключи начинаются с ss_live_, принадлежат одному рабочему пространству и могут быть ограничены определёнными проектами. Передавайте ключ как bearer-токен в каждом запросе:
curl https://www.amaposta.com/api/v1/me \
-H "Authorization: Bearer ss_live_…"
Ответ содержит название ключа, его разрешения и рабочее пространство. У каждого ключа есть одно или несколько разрешений:
| Область доступа | Разрешает |
|---|---|
content:read | Читать проекты, каналы, публикации и медиафайлы. |
content:write | Загружать медиафайлы, создавать и редактировать публикации. |
posts:publish | Планировать и публиковать без проверки. Без этого разрешения публикации идут на проверку в проектах с согласованием, а в остальных остаются черновиками. |
Каждый ключ позволяет делать 120 запросов в минуту. При превышении возвращается 429 с кодом rate_limited; немного подождите и повторите попытку.
Проекты и каналы
Публикации находятся в проекте. Получите список проектов, доступных вашему ключу, затем список каналов одного из них. Идентификатор канала нужен, чтобы выбрать его для публикации или переопределить текст.
curl https://www.amaposta.com/api/v1/projects \
-H "Authorization: Bearer $AMAPOSTA_KEY"
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/channels \
-H "Authorization: Bearer $AMAPOSTA_KEY"
У канала есть provider (youtube, instagram, tiktok, x, facebook, custom или sandbox), status (active, expiring, needs_reconnect, error или disabled) и типы публикаций, которые он supports.
Загрузить медиафайлы
Изображения и видео загружаются один раз на проект и могут использоваться в любом количестве публикаций. Есть три способа загрузки.
Прямая загрузка
Отправьте файл как multipart form data в поле с именем file. Подходит для файлов размером до нескольких сотен МБ.
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media \
-H "Authorization: Bearer $AMAPOSTA_KEY" \
-F "[email protected]"
Предварительно подписанная загрузка больших файлов
Запросите URL для загрузки, отправьте байты прямо в хранилище с помощью PUT, затем сообщите Amaposta о завершении.
# 1. Ask for an upload URL. The response is {"media": { … }, "upload_url": "…"}
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media/uploads \
-H "Authorization: Bearer $AMAPOSTA_KEY" \
-H "Content-Type: application/json" \
-d '{"filename": "batch-142.mp4", "content_type": "video/mp4", "size_bytes": 48213844}'
# 2. Upload the bytes to upload_url
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: video/mp4" \
--upload-file batch-142.mp4
# 3. Finish the upload
curl -X POST https://www.amaposta.com/api/v1/media/$MEDIA_ID/complete \
-H "Authorization: Bearer $AMAPOSTA_KEY"
Импорт по URL
Дайте Amaposta публичный URL, и файл скачается в фоновом режиме. Можно пропустить этот шаг и передать media_url при создании публикации.
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media/import \
-H "Authorization: Bearer $AMAPOSTA_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://cdn.example.com/batch-142.mp4"}'
Каждая загрузка возвращает объект медиафайла. Его status меняется с pending на processing и ready (или failed с полем error). После обработки он содержит width, height, duration_ms и миниатюру. Опрашивайте GET /api/v1/media/{id} или слушайте вебхук media.ready. Возвращаемый url подписан и действителен 12 часов.
Создать публикацию
POST /api/v1/projects/{project_id}/posts создаёт одну публикацию с отдельной отправкой на каждый канал. Amaposta адаптирует её к платформе: заголовки передаются только при наличии поля заголовка, хештеги становятся тегами в YouTube и частью текста на остальных платформах.
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/posts \
-H "Authorization: Bearer $AMAPOSTA_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: episode-142" \
-d @- <<'JSON'
{
"kind": "video",
"title": "How we roast a batch in 12 minutes",
"description": "From green beans to first crack. We filmed one batch start to finish.",
"hashtags": ["coffee", "behindthescenes"],
"media_url": "https://cdn.example.com/batch-142.mp4",
"cover_url": "https://cdn.example.com/batch-142-cover.jpg",
"overrides": {
"<x-channel-id>": {
"description": "Batch 142 is out: green beans to first crack in 12 minutes."
},
"<youtube-channel-id>": {
"options": { "privacy": "unlisted" }
}
},
"schedule": { "mode": "queue" },
"external_ref": "episode-142",
"metadata": {
"pipeline": { "name": "nightly-cut", "run": "2026-10-17" }
}
}
JSON
Поля
| Поле | Что делает |
|---|---|
kind | text, image или video. Обязательное поле. |
title | Используется платформами с полем заголовка: YouTube, фотопубликации TikTok, видео Facebook. |
description | Подпись, описание или текст публикации. Также принимается как body, caption или text. |
hashtags | Список со знаком # или без него. |
media_id, media_url | Идентификатор загруженного медиафайла или публичный URL, по которому Amaposta его скачает. Необходим для публикаций с изображением или видео. |
cover_media_id, cover_url | Обложка для видео, если платформа её поддерживает. |
channels | Идентификаторы каналов для публикации. Если не указать, используются все активные каналы проекта, поддерживающие этот тип публикации. |
overrides | Изменения для отдельных каналов по их идентификаторам: title, description, hashtags и options платформы, например privacy в YouTube. |
schedule | mode принимает значение at (с полем at в формате ISO 8601), now или queue для следующего свободного слота. Не указывайте его, чтобы создать черновик. |
external_ref | Ваш собственный идентификатор, возвращаемый в публикации и отправках пользовательского API. |
metadata | JSON произвольной структуры, сохраняемый с публикацией. Включите "pipeline" для пометки сгенерированного контента; панель управления покажет её, а вебхуки укажут источник как pipeline. |
Ответ — объект публикации с одной записью в deliveries на каждый канал.
Безопасные повторные попытки
Запросы могут теряться в сети. При создании публикации отправляйте заголовок Idempotency-Key: повторный запрос с тем же ключом вернёт публикацию из первой попытки, не создавая вторую. Используйте стабильное значение со своей стороны, например идентификатор задания или выпуска. Ключи обрезаются до 200 символов.
Планирование и публикация
Запланируйте черновик, опубликуйте его сейчас, добавьте в очередь или уберите из календаря через POST /api/v1/posts/{post_id}/schedule.
curl https://www.amaposta.com/api/v1/posts/$POST_ID/schedule \
-H "Authorization: Bearer $AMAPOSTA_KEY" \
-H "Content-Type: application/json" \
-d '{"mode": "at", "at": "2026-10-20T07:00:00Z"}'
atПубликует в указанное время, на срок до двух лет вперёд.nowПубликует сразу.queueЗанимает следующий свободный слот каналов публикации в ближайшие 12 недель.unscheduleВозвращает публикацию в черновики.
Перед планированием выполняются те же проверки, что и в редакторе. Если проверка не пройдена, запрос возвращает 400 с сообщением вроде Исправьте перед планированием: для YouTube нужен заголовок.
Для планирования требуется разрешение posts:publish или проект с включённым согласованием; в этом случае публикация ожидает в очереди на проверку.
Читать публикации и отправки
Список публикаций проекта с фильтрами по status (статусы через запятую либо inbox, queue или history), диапазону from/to, limit и offset. Формат ответа: {"items": […], "total": n}.
curl "https://www.amaposta.com/api/v1/projects/$PROJECT_ID/posts?status=queue" \
-H "Authorization: Bearer $AMAPOSTA_KEY"
curl https://www.amaposta.com/api/v1/posts/$POST_ID \
-H "Authorization: Bearer $AMAPOSTA_KEY"
У публикации есть общий status (draft, in_review, changes_requested, ready, scheduled, publishing, published, partial или failed) и отдельная отправка на каждый канал:
"deliveries": [
{
"id": "0192b7d4-…",
"channel_id": "0192b7a1-…",
"status": "published",
"url": "https://www.youtube.com/watch?v=…",
"error": null,
"published_at": "2026-10-20T07:00:41Z"
},
{
"id": "0192b7d4-…",
"channel_id": "0192b7a9-…",
"status": "failed",
"url": null,
"error": {
"code": "endpoint_unavailable",
"message": "Your endpoint returned 503 Service Unavailable",
"hint": "Amaposta will retry with backoff."
},
"published_at": null
}
]
Статусы отправки: pending, scheduled, publishing, processing, published, failed и cancelled. DELETE /api/v1/posts/{post_id} удаляет публикацию.
Ошибки
Все ошибки имеют одинаковую структуру: объект error со стабильным code, сообщением message для пользователей, а иногда и fields (сообщения по полям) и hint.
{
"error": {
"code": "validation",
"message": "Please fix the highlighted fields.",
"fields": {
"kind": "Use text, image or video."
}
}
}
| Статус | Код | Значение |
|---|---|---|
| 400 | bad_request | Запрос нельзя выполнить в указанном виде, например запланировать публикацию, не прошедшую проверки. |
| 401 | unauthorized | Ключ API отсутствует, неверен или отозван. |
| 402 | plan_limit | Тариф рабочего пространства не позволяет это действие; limit указывает лимит. |
| 403 | forbidden | У ключа нет нужного разрешения или доступа к проекту. |
| 404 | not_found | Такого проекта, публикации, медиафайла или конечной точки нет. |
| 409 | conflict | Изменение конфликтует с текущим состоянием, например редактируется публикация, которая уже выходит. |
| 422 | validation | Нужно исправить одно или несколько полей; см. fields. |
| 429 | rate_limited | Более 120 запросов в минуту с этим ключом. |
| 502 | provider_error | Платформа отклонила запрос или произошёл сбой; в hint указано, что делать. |
Вебхуки
Добавьте конечную точку в настройках, в разделе API и вебхуков, выберите нужные события и скопируйте секрет подписи. Для каждого события Amaposta отправляет JSON POST-запросом на ваш URL. При ответе вне диапазона 2xx или отсутствии ответа в течение 15 секунд запрос повторяется с увеличением интервалов. Конечные точки с постоянными сбоями отключаются с уведомлением администраторов.
| Событие | Когда отправляется |
|---|---|
post.created | Публикация создана (в панели управления или через API) |
post.scheduled | Публикация запланирована |
post.approved | Публикация одобрена |
post.published | Публикация вышла на канале |
post.failed | Не удалось опубликовать запись на канале |
channel.connected | Канал подключён впервые или повторно |
channel.disconnected | Канал потерял подключение |
media.ready | Обработка загруженного файла завершена |
media.failed | Не удалось обработать загруженный файл |
POST /your/webhook HTTP/1.1
Content-Type: application/json
User-Agent: Amaposta-Webhooks/1.0
X-Amaposta-Event: post.published
X-Amaposta-Event-Id: 0192b7e0-…
X-Amaposta-Signature: t=1792479641,v1=5f2b0c…
{
"id": "0192b7e0-…",
"type": "post.published",
"created_at": "2026-10-20T07:00:41Z",
"project_id": "0192a3f1-…",
"data": {
"post_id": "0192b7c8-…",
"delivery_id": "0192b7d4-…",
"channel_id": "0192b7a1-…",
"provider": "youtube",
"external_id": "…",
"url": "https://www.youtube.com/watch?v=…"
}
}
Проверить подпись
X-Amaposta-Signature имеет вид t=<unix seconds>,v1=<hex>, где v1 — это HMAC_SHA256(secret, t + "." + body) для исходного тела запроса. Вычисляйте подпись самостоятельно, сравнивайте за постоянное время и отклоняйте старые временные метки для защиты от повторной отправки.
import crypto from "node:crypto";
// rawBody: the request body exactly as received (string or Buffer).
export function verifySignature(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest();
const given = Buffer.from(parts.v1 ?? "", "hex");
return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}
import hashlib
import hmac
import time
def verify_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t = int(parts.get("t", "0"))
if abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
Каналы с пользовательским API
Канал с пользовательским API публикует данные на вашей конечной точке HTTPS: в вашей CMS, инструменте рассылок или на платформе, которую Amaposta пока не поддерживает. Он отображается в календаре, проходит проверки, согласование и повторные попытки, как любой другой канал. При публикации Amaposta отправляет по одному POST-запросу на каждую отправку:
POST /amaposta/publish HTTP/1.1
Authorization: Bearer <your API key>
Content-Type: application/json
Idempotency-Key: 0192b7d4-…
X-Amaposta-Delivery: 0192b7d4-…
X-Amaposta-Signature: t=1792479600,v1=9ac41e…
{
"id": "0192b7d4-…",
"type": "post.publish",
"created_at": "2026-10-20T07:00:00Z",
"content": {
"kind": "video",
"title": "How we roast a batch in 12 minutes",
"text": "From green beans to first crack. We filmed one batch start to finish.",
"hashtags": ["coffee", "behindthescenes"],
"media": {
"url": "https://…",
"content_type": "video/mp4",
"size_bytes": 48213844,
"width": 1080,
"height": 1920,
"duration_ms": 58000,
"filename": "batch-142.mp4"
},
"cover": null,
"options": {}
},
"post": {
"id": "0192b7c8-…",
"external_ref": "episode-142",
"scheduled_at": "2026-10-20T07:00:00Z",
"metadata": {
"pipeline": { "name": "nightly-cut" }
}
},
"project": { "id": "0192a3f1-…", "name": "Spring launch" },
"channel": { "id": "0192b7a9-…", "name": "api.example.com" }
}
Authorization- Ваш ключ API. Имя заголовка и префикс (
Bearerпо умолчанию) задаются в канале. Idempotency-Key- Идентификатор отправки. При повторной попытке той же отправки передаётся тот же ключ, чтобы вы могли пропустить уже выполненную работу.
X-Amaposta-Delivery- Идентификатор отправки ещё раз, для ваших журналов.
X-Amaposta-Signature- Подписывается как вебхуки, собственным секретом подписи канала.
Как отвечать
- 2xxОпубликовано. При желании верните
{"id": "…", "url": "…"}, и ссылка появится в публикации. - 401, 403Ключ отклонён. Канал помечается для повторного подключения, администраторы получают уведомление. Не используйте эти статусы для неверной подписи.
- 5xx, 408, 429Временный сбой. Amaposta повторит попытку с увеличенным интервалом.
- Другие 4xxПубликация помечается как неудачная с вашим сообщением; её можно исправить и нажать «Повторить».
app.post("/amaposta/publish", express.raw({ type: "application/json" }), async (req, res) => {
const signature = req.get("X-Amaposta-Signature") ?? "";
if (!verifySignature(req.body, signature, process.env.AMAPOSTA_SIGNING_SECRET)) {
return res.status(400).json({ message: "Bad signature" });
}
const delivery = JSON.parse(req.body);
const item = await publishToYourSystem(delivery.content, req.get("Idempotency-Key"));
res.json({ id: item.id, url: item.url });
});
Ваша конечная точка должна быть доступна из интернета; перенаправления не выполняются. При подключении канала Amaposta проверяет её GET-запросом к URL проверки работоспособности, если он задан, или OPTIONS-запросом к конечной точке.
Каналы песочницы
Канал песочницы работает как настоящий, но никакие данные не покидают Amaposta. С его помощью можно создать и проверить интеграцию целиком: публикации планируются, выходят в эфир, публикуются или завершаются ошибкой и вызывают те же вебхуки. Каналы песочницы бесплатны на любом тарифе и не учитываются в лимите каналов.
Выберите поведение при добавлении:
- Всегда успешноВсе публикации выходят успешно.
- Иногда завершать с ошибкойПосмотрите на сбои и повторные попытки, не дожидаясь настоящей аварии.
- Всегда завершать с ошибкойПроверьте обработку
post.failed. - Срок подключения истёкПосмотрите, как выглядят уведомления и ссылки для повторного подключения при истечении доступа.
Подключите X через собственное приложение
X взимает плату за каждый вызов API, включая каждую публикацию, с приложения разработчика, которое выполняет этот вызов, списывая её из предоплаченных кредитов приложения. Поэтому X подключается через ваше собственное приложение разработчика X: настройте его один раз для рабочего пространства, а затем подключайте сколько угодно аккаунтов X. В каждый аккаунт нужно войти на X, а его публикации оплачиваются из кредитов вашего приложения.
- Войдите на console.x.com с аккаунтом X, которому должно принадлежать приложение, например с аккаунтом вашего агентства, и создайте приложение.
-
Откройте User authentication settings приложения и укажите:
- App permissionsRead and write
- Type of AppWeb App, Automated App or Bot
- Callback URI / Redirect URL
https://www.amaposta.com/oauth/x/callback - Website URLВаш сайт
Затем сохраните. Только этот тип приложения получает секрет клиента, с которым Amaposta выполняет вход.
- Скопируйте OAuth 2.0 Client ID и Client Secret, которые X покажет после сохранения. Не используйте API Key, API Key Secret или Bearer Token: они предназначены для других видов доступа. X показывает секрет только один раз.
- Купите кредиты API в консоли X. Автопополнение позволяет продолжать публикации, когда кредитов становится мало, а лимит расходов ограничивает затраты. Когда кредиты закончатся, X будет отклонять публикации, пока вы не пополните баланс.
- В Amaposta откройте Каналы проекта, выберите Подключить канал, а затем X. Вставьте ID клиента и секрет клиента, перейдите в X, войдите в аккаунт, который хотите подключить, и разрешите доступ.
Полезно знать
- Приложение сохраняется для всего рабочего пространства, а его секрет шифруется. Любой другой аккаунт X можно подключить одним нажатием из любого проекта.
- Каналы сохраняют приложение, через которое были подключены. При повторном подключении канала, в том числе по ссылке для клиента, снова используется это приложение; ваш клиент его не видит.
- Чтобы сменить приложение, выберите «Использовать другое приложение» при подключении X. Если вы создадите новый Client Secret, введите его там и переподключите свои каналы X.
- Консоль X выдаёт токены только для аккаунта, которому принадлежит приложение. Чтобы подключить аккаунты клиентов, нужно войти через ваше приложение; для собственного аккаунта по-прежнему можно ввести токены вручную.
Правила платформ
Требования каждого типа канала. Проверки используют те же лимиты, поэтому большинство проблем обнаруживается до планирования, а не при выходе публикации.
| Платформа | Публикация | Заголовок | Текст | Хештеги | Медиафайлы |
|---|---|---|---|---|---|
| YouTube | Видео | ЗаголовокОбязательно, до 100 символов | ОписаниеДо 5 000 символов | Передаются как теги, до 60 | MP4, MOV, WebM, AVI, MKV; до 256 GB; От 1 с до 12 ч |
| Аккаунты без подтверждённого номера телефона могут загружать видео длительностью до 15 минут и не могут задавать собственные миниатюры. Каждая загрузка расходует квоту API; стандартной квоты Google хватает примерно на шесть загрузок в день на приложение. | |||||
| Изображение | Без заголовка | ПодписьДо 2 200 символов | Добавляются в текст, до 30 | JPEG; до 8 MB | |
| Видео | Без заголовка | ПодписьДо 2 200 символов | Добавляются в текст, до 30 | MP4, MOV; до 300 MB; От 3 с до 15 мин | |
| Требуется бизнес-аккаунт Instagram или аккаунт автора. Instagram разрешает 50 публикаций через API на аккаунт за скользящий период в 24 часа. В Instagram нельзя публиковать только текст. | |||||
| TikTok | Изображение | ЗаголовокДо 90 символов | ОписаниеДо 4 000 символов | Добавляются в текст | JPEG, WebP; до 20 MB |
| Видео | Без заголовка | ПодписьДо 2 200 символов | Добавляются в текст | MP4, MOV, WebM; до 4 GB; От 3 с до 10 мин | |
| Публикации из приложений, не прошедших аудит TikTok, видны только автору. TikTok выбирает обложку из кадра видео; собственные изображения обложки не поддерживаются. В TikTok нельзя публиковать только текст. | |||||
| X | Текст | Без заголовка | ПубликацияОбязательно, до 280 (ссылки — 23 символа, эмодзи — 2) | Добавляются в текст | Нет |
| Изображение | Без заголовка | ПубликацияДо 280 (ссылки — 23 символа, эмодзи — 2) | Добавляются в текст | JPEG, PNG, WebP, GIF; до 5 MB | |
| Видео | Без заголовка | ПубликацияДо 280 (ссылки — 23 символа, эмодзи — 2) | Добавляются в текст | MP4, MOV; до 512 MB; От 0,5 с до 2:20 мин | |
| При использовании собственного приложения разработчика X публикации расходуют его оплачиваемые по мере использования средства API. Хештеги учитываются в лимите символов. | |||||
| Текст | Без заголовка | ПубликацияОбязательно, до 63 206 символов | Добавляются в текст | Нет | |
| Изображение | Без заголовка | ПодписьДо 63 206 символов | Добавляются в текст | JPEG, PNG, GIF, WebP; до 10 MB | |
| Видео | ЗаголовокДо 255 символов | ОписаниеДо 63 206 символов | Добавляются в текст | MP4, MOV; до 1 GB; От 1 с до 20 мин | |
| Публикует только на страницах; API не позволяет публиковать в личных профилях. | |||||
| Пользовательский API | Текст | ЗаголовокБез ограничения длины | ОписаниеБез ограничения длины | Передаются как теги | Нет |
| Изображение | ЗаголовокБез ограничения длины | ОписаниеБез ограничения длины | Передаются как теги | JPEG, PNG, WebP, GIF | |
| Видео | ЗаголовокБез ограничения длины | ОписаниеБез ограничения длины | Передаются как теги | MP4, MOV, WebM | |
| Каждый запрос подписывается заголовком HMAC-SHA256, чтобы вы могли проверить, что он поступил от Amaposta. Ответьте статусом 2xx и при желании JSON {"id": "…", "url": "…"}. Ответы 408, 429 и 5xx вызывают повторные попытки с увеличением интервалов; 401/403 помечают канал для повторного подключения; другие 4xx завершают отправку с ошибкой, чтобы вы могли исправить её и повторить попытку. | |||||
| Песочница | Текст | ЗаголовокДо 200 символов | ОписаниеДо 5 000 символов | Добавляются в текст, до 30 | Нет |
| Изображение | ЗаголовокДо 200 символов | ОписаниеДо 5 000 символов | Добавляются в текст, до 30 | JPEG, PNG, WebP, GIF | |
| Видео | ЗаголовокДо 200 символов | ОписаниеДо 5 000 символов | Добавляются в текст, до 30 | MP4, MOV, WebM | |
| Публикация занимает некоторое время, чтобы вы могли наблюдать изменение статуса. | |||||