REST API
Programmatic-доступ к данным магазина и автоматизация публикаций: заказы, товары, покупатели, блог, загрузка изображений. Bearer-токены, cursor-пагинация. База URL — домен вашего магазина:
https://<your-shop>.rercon.tech/api/v1
На своём домене API живёт там же: https://mystore.com/api/v1.
Быстрый старт
- Создайте токен в админке магазина:
/admin/settings/api-tokens. Для чтения хватит scope read; для публикаций в блог и загрузки картинок нужен write. - Токен показывается один раз и выглядит как
rcn_a1b2c3…— сохраните его в секретах вашей интеграции. - Проверьте, что токен работает:
curl -H "Authorization: Bearer rcn_..." \
https://<your-shop>.rercon.tech/api/v1/meВ ответе придёт ваш магазин и метаданные токена — значит всё настроено. Дальше любой endpoint из таблицы ниже.
Аутентификация
Каждый запрос несёт токен в заголовке. Поддерживаются два равнозначных варианта:
| Заголовок | Пример |
|---|---|
Authorization | Bearer rcn_a1b2c3… |
X-Rercon-Token | rcn_a1b2c3… |
Права токена задаются при создании и определяют доступное:
| Scope | Что разрешено | Персональные данные |
|---|---|---|
| read | Все GET-endpoints | Маскируются: email → a***@…, tg_username и ФИО скрыты |
| write | Всё из read + POST (посты блога, загрузка картинок) | Полные данные покупателей |
Write-токены — только для доверенных server-side интеграций. Никогда не вшивайте токен в браузерный код или мобильное приложение: его увидит любой пользователь.
Доступ и лимиты
| Ограничение | Значение |
|---|---|
| Тариф | «Автопилот» и «Флагман» (на «Старте» запросы вернут 403) |
| Запросы | 60 в минуту на токен → 429 при превышении |
| Загрузки картинок | 30 в минуту, файл до 20 MB |
| Размер тела запроса | до 2 MB (тело статьи — до 200 000 символов HTML) |
Остаток лимита приходит в заголовках каждого ответа:
X-RateLimit-Limit: 60 X-RateLimit-Remaining: 47 X-RateLimit-Reset: 1716210000 # unix-время сброса окна
При 429 подождите до времени из X-RateLimit-Reset и повторите запрос. CORS открыт (Access-Control-Allow-Origin: *) — авторизация токеном, не cookie. Версия API — в заголовке X-Rercon-API-Version: v1.
Формат ответов и пагинация
Все ответы — один конверт. Успех: data заполнен, error: null. Ошибка: data: null и объект error с машинным code и человекочитаемым message.
{
"data": [ ... ],
"pagination": { "limit": 50, "next_cursor": 12345, "has_more": true },
"error": null
}Списки отдаются страницами от новых к старым (по убыванию id). Чтобы получить следующую страницу, передайте next_cursor из ответа параметром ?cursor=:
GET /api/v1/orders?limit=50 # первая страница GET /api/v1/orders?limit=50&cursor=12345 # следующая # повторяйте, пока has_more == true (next_cursor == null в конце)
Все даты — ISO 8601 в UTC (2026-06-12T10:00:00.000Z). Все суммы — в копейках (total_kopecks: 990000 = 9 900 ₽): целые числа, без проблем с плавающей точкой.
Ошибки
| HTTP | code | Когда |
|---|---|---|
| 400 | invalid_query / invalid_body | Невалидные параметры или тело запроса; в message — какое поле |
| 400 | invalid_published_at | Дата отложенной публикации не в будущем / нечитаема |
| 400 | invalid_category | category_id не из вашего магазина |
| 401 | unauthorized | Токен не передан, не найден или истёк |
| 403 | unauthorized | Тариф без API или у токена не хватает scope |
| 409 | slug_taken | Пост с таким slug уже существует |
| 413 | file_too_large / storage_quota_exceeded | Файл больше 20 MB или закончилась квота хранилища тарифа |
| 415 | unsupported_type | Файл не jpeg/png/webp/avif/gif (тип определяется по содержимому) |
| 429 | rate_limited | Превышен лимит запросов или загрузок |
| 5xx | … | Наша сторона; безопасно повторить с экспоненциальной задержкой |
{
"data": null,
"pagination": null,
"error": { "code": "slug_taken", "message": "a post with this slug already exists" }
}Все endpoints
| Метод | Путь | Scope | Назначение |
|---|---|---|---|
| GET | /api/v1/me | read | Проверка токена, инфо о магазине |
| GET | /api/v1/orders | read | Заказы |
| GET | /api/v1/products | read | Каталог товаров |
| GET | /api/v1/customers | read | Покупатели |
| GET | /api/v1/blog/posts | read | Посты блога (вкл. черновики) |
| POST | /api/v1/blog/posts | write | Создать пост: черновик / сразу / по расписанию |
| POST | /api/v1/uploads/images | write | Загрузить картинку, получить CDN-URL |
GET/api/v1/me
Информация о магазине и токене. Идеален как health-check интеграции: дешёвый, без побочных эффектов.
curl -H "Authorization: Bearer rcn_..." \
https://<your-shop>.rercon.tech/api/v1/meответ · 200
{
"data": {
"tenant": { "id": 7, "slug": "alice", "name": "Alice School", "plan": "pro", "status": "active" },
"token": {
"name": "n8n integration", "prefix": "rcn_a1b2",
"scope": "write", "last_used_at": "2026-06-11T18:02:11.000Z", "expires_at": null
}
},
"error": null
}GET/api/v1/orders
| Параметр | Тип | Default | Описание |
|---|---|---|---|
limit | integer | 50 | Размер страницы, максимум 200 |
cursor | integer | — | id заказа из next_cursor предыдущей страницы |
status | string | — | draft · pending_payment · paid · refunded · cancelled |
curl -H "Authorization: Bearer rcn_..." \
"https://<your-shop>.rercon.tech/api/v1/orders?limit=50&status=paid"ответ · 200
{
"data": [{
"id": 1042,
"total_kopecks": 990000,
"status": "paid",
"email": "a***@gmail.com", // полный — только для scope write
"customer_name": null, // только для scope write
"created_at": "2026-06-10T09:31:00.000Z",
"paid_at": "2026-06-10T09:33:12.000Z",
"refunded_at": null,
"utm": { "source": "tg_channel", "campaign": "june_launch" },
"payment_provider": "prodamus"
}],
"pagination": { "limit": 50, "next_cursor": 1042, "has_more": true },
"error": null
}GET/api/v1/products
| Параметр | Тип | Default | Описание |
|---|---|---|---|
limit | integer | 50 | Размер страницы, максимум 200 |
cursor | integer | — | id товара из next_cursor |
active | boolean | — | true — только активные, false — только выключенные |
curl -H "Authorization: Bearer rcn_..." \
"https://<your-shop>.rercon.tech/api/v1/products?active=true"ответ · 200
{
"data": [{
"id": 3, "slug": "python-course", "name": "Курс по Python",
"description": "…", "cover_url": "https://cdn.rercon.tech/i/…",
"is_active": true, "is_featured": false, "is_listed": true,
"delivery_mode": "course", "scope": "tenant",
"created_at": "2026-03-02T12:00:00.000Z", "updated_at": "2026-06-01T08:15:00.000Z"
}],
"pagination": { "limit": 50, "next_cursor": null, "has_more": false },
"error": null
}id товаров из этого списка подставляются в recommended_product_ids при создании поста блога — блок рекламы ваших товаров под статьёй.
GET/api/v1/customers
| Параметр | Тип | Default | Описание |
|---|---|---|---|
limit | integer | 50 | Размер страницы, максимум 200 |
cursor | integer | — | id покупателя из next_cursor |
opted_in | boolean | — | Фильтр по согласию на email-рассылки |
curl -H "Authorization: Bearer rcn_..." \
"https://<your-shop>.rercon.tech/api/v1/customers?opted_in=true"ответ · 200
{
"data": [{
"id": 215,
"email": "i***@yandex.ru", // полный — только для scope write
"created_at": "2026-05-20T14:00:00.000Z",
"last_login_at": "2026-06-11T20:44:00.000Z",
"bonus_balance": 1500,
"email_opt_in": true,
"email_opted_in_at": "2026-05-20T14:01:00.000Z",
"tg_username": null, // только для scope write
"tg_linked_at": "2026-05-21T10:00:00.000Z"
}],
"pagination": { "limit": 50, "next_cursor": 215, "has_more": true },
"error": null
}GET/api/v1/blog/posts
Все посты блога, включая черновики и запланированные — удобно для синхронизации с внешней CMS и проверки «не публиковали ли мы уже такое».
| Параметр | Тип | Default | Описание |
|---|---|---|---|
limit | integer | 50 | Размер страницы, максимум 200 |
cursor | integer | — | id поста из next_cursor |
status | string | — | draft · scheduled · published |
curl -H "Authorization: Bearer rcn_..." \
"https://<your-shop>.rercon.tech/api/v1/blog/posts?status=published"ответ · 200
{
"data": [{
"id": 18, "slug": "kak-vybrat-kurs", "title": "Как выбрать курс",
"excerpt": "Короткий анонс", "cover_url": "https://cdn.rercon.tech/i/…",
"status": "published", "published_at": "2026-06-10T07:00:00.000Z",
"author_name": "Артём", "category_id": 2,
"url": "/blog/kak-vybrat-kurs",
"created_at": "2026-06-09T16:20:00.000Z", "updated_at": "2026-06-10T07:00:00.000Z"
}],
"pagination": { "limit": 50, "next_cursor": 18, "has_more": false },
"error": null
}POST/api/v1/blog/posts
Создать пост блога — ядро автоматизации публикаций. Тело можно слать как HTML, как Markdown (body_format: "markdown"), или целым .md-документом в поле markdown — для AI-генерации это самый удобный путь: модель пишет markdown с frontmatter, вы шлёте его как есть. Категорию можно указывать по имени (category) — если такой ещё нет, она создастся сама. Управление судьбой поста — поле status:
| status | Что произойдёт | published_at |
|---|---|---|
draft | Пост ляжет в черновики — на проверку перед публикацией (default) | игнорируется |
publish | Сразу на сайт. С датой в прошлом — публикация «задним числом» | опционально |
schedule | Отложенная публикация: сам появится на сайте в назначенное время, cron не нужен | обязательно, в будущем |
Поля тела запроса:
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
title | string ≤300 | да* | Заголовок (*или придёт из frontmatter, если передан markdown) |
body | string ≤200k | нет | Тело статьи. По умолчанию HTML (санитайзится как в редакторе). Если body_format=markdown — трактуется как Markdown и конвертится |
body_format | enum | нет | html (default) · markdown. markdown → безопасный HTML тем же санитайзером |
markdown | string ≤200k | нет | Целый .md-документ (frontmatter + тело) вместо отдельных полей. Распарсится в title/slug/excerpt/seo/category/author/body; явные поля приоритетнее frontmatter |
slug | string ≤160 | нет | Адрес /blog/<slug> (латиница, цифры, дефис). Не указан — сгенерируется из title с транслитерацией; конфликт решится суффиксом -2 |
excerpt | string ≤1000 | нет | Анонс в карточке и description по умолчанию |
cover_url | string | нет | Обложка (URL от /uploads/images); только http(s) или относительный путь |
status | enum | нет | draft · publish · schedule (default draft) |
published_at | ISO 8601 | см. выше | Дата публикации, с таймзоной: 2026-07-01T10:00:00+03:00 |
author_name | string ≤200 | нет | Автор под статьёй (author — алиас) |
category_id | integer | нет | Категория блога по id (из админки) |
category | string ≤200 | нет | Категория по ИМЕНИ. Если такой ещё нет — создастся автоматически (удобно для AI: не нужно знать id) |
seo_title | string ≤300 | нет | Переопределяет <title> (default — title) |
seo_description | string ≤1000 | нет | Meta description (default — excerpt) |
og_image_url | string | нет | Картинка для соцсетей (default — обложка) |
recommended_product_ids | int[] ≤12 | нет | id товаров для блока рекламы под статьёй |
show_view_count | boolean | нет | Публичный счётчик просмотров (default true) |
curl -X POST \
-H "Authorization: Bearer rcn_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Как выбрать курс",
"body": "<p>Текст статьи…</p>",
"excerpt": "Разбираем критерии выбора онлайн-курса.",
"cover_url": "https://cdn.rercon.tech/i/abc123.webp",
"status": "schedule",
"published_at": "2026-07-01T10:00:00+03:00"
}' \
https://<your-shop>.rercon.tech/api/v1/blog/postsответ · 201
{
"data": {
"id": 19,
"slug": "kak-vybrat-kurs",
"url": "/blog/kak-vybrat-kurs",
"status": "scheduled",
"published_at": "2026-07-01T07:00:00.000Z",
"created_at": "2026-06-12T09:00:00.000Z"
},
"pagination": null,
"error": null
}Вариант для AI-генерации — целый .md-файл одним полем markdown (frontmatter + тело). Заголовок, slug, excerpt, SEO, категория и автор берутся из frontmatter:
curl -X POST \
-H "Authorization: Bearer rcn_..." \
-H "Content-Type: application/json" \
-d '{
"status": "draft",
"markdown": "---\ntitle: Как выбрать курс\nslug: kak-vybrat-kurs\nexcerpt: Разбираем критерии выбора.\ncategory: Гайды\nauthor: Артём\n---\n\n## С чего начать\n\nТекст в **markdown**. Категория «Гайды» создастся, если её ещё нет."
}' \
https://<your-shop>.rercon.tech/api/v1/blog/postsЭквивалент без markdown: те же поля по отдельности + "body_format": "markdown" и body с markdown-телом. h1 в теле понижается до h2 — заголовок статьи живёт в title.
Частые ошибки: 409 slug_taken — передан занятый slug (уберите поле, и адрес сгенерируется свободный); 400 invalid_published_at — для schedule дата обязана быть в будущем; 400 invalid_category — передан category_id не из вашего магазина (а вот category по имени просто создаётся).
POST/api/v1/uploads/images
Загрузка картинки на CDN — multipart/form-data с одним полем file. Вернувшийся url подставляется в cover_url, og_image_url или в <img src=…> внутри тела поста.
| Требование | Значение |
|---|---|
| Форматы | jpeg, png, webp, avif, gif (тип проверяется по байтам файла, svg не принимается) |
| Размер | до 20 MB; заголовок Content-Length обязателен |
| Квота | объём считается в квоту хранилища тарифа (как загрузки из админки) |
| Частота | до 30 загрузок в минуту |
curl -X POST -H "Authorization: Bearer rcn_..." \
-F "file=@cover.webp" \
https://<your-shop>.rercon.tech/api/v1/uploads/imagesответ · 201
{
"data": {
"url": "https://cdn.rercon.tech/i/abc123.webp",
"thumb_url": "https://cdn.rercon.tech/i/abc123_t.webp",
"slug": "abc123",
"size_bytes": 184220,
"width": 1600,
"height": 900
},
"pagination": null,
"error": null
}Сценарий: полная автопубликация статьи
Связка двух endpoint'ов — генератор контента сам выкладывает статью с обложкой по расписанию. Один write-токен, два запроса:
#!/usr/bin/env bash
TOKEN="rcn_..."
SHOP="https://<your-shop>.rercon.tech"
# 1. Загружаем обложку, забираем CDN-URL из ответа
COVER=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-F "file=@cover.webp" "$SHOP/api/v1/uploads/images" \
| jq -r '.data.url')
# 2. Создаём пост: в черновики на проверку…
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Заголовок статьи\",
\"body\": \"<p>Текст…</p>\",
\"cover_url\": \"$COVER\",
\"status\": \"draft\"
}" "$SHOP/api/v1/blog/posts"
# …или сразу в публикацию: "status": "publish"
# …или по расписанию: "status": "schedule",
# "published_at": "2026-07-01T10:00:00+03:00"Паттерн «на проверку»: автоматика постит со status: draft, вы просматриваете в админке (/admin/blog) и публикуете в один клик. Паттерн «полный автопилот»: schedule с разнесёнными датами — контент-план на месяц одним скриптом.
Рекомендации по интеграции
- Храните токен в секретах (env-переменная, vault) — не в коде и не в репозитории. Утёк — отзовите в админке и создайте новый.
- Для чтения создавайте отдельный read-токен: меньше прав — меньше последствий при утечке.
- Обрабатывайте 429: подождите до
X-RateLimit-Resetи повторите. На 5xx — повтор с экспоненциальной задержкой. - Идемпотентность публикаций: задавайте
slugявно — при повторном запуске скрипта дубль вернёт 409 вместо второго поста. - Пагинация: останавливайтесь по
has_more: false, а не по пустой странице.