Tiny Shops
Developers

API заказов и вебхуки (RU)

Подключите склад, службу фулфилмента или ИИ-агента к заказам — читайте их, отмечайте оплаченными или отправленными и получайте подписанный вебхук о каждом изменении.

Tiny Shops даёт внешним системам две вещи:

  • HTTP API, чтобы прочитать заказ, изменить его статус и отправить его с трекингом.
  • Вебхук, который отправляет (POST) каждый новый заказ и каждое изменение статуса или трекинга на выбранный вами URL.

Оба используют одну и ту же структуру заказа, поэтому всё, что пришло в вебхуке, можно поле в поле перечитать через API.

Reading in English? See the English version of this page.

Хотите, чтобы агент управлял не только заказами? Откройте инструкцию по подключению ИИ-агента.

1. Создайте API-токен

Откройте API-токены

В панели откройте Настройки → API-токены (/settings/api-tokens). Эту страницу видит только владелец магазина.

Создайте токен с нужным доступом

  • Чтение — только get_order.
  • Запись — всё на этой странице: get_order, set_order_status, ship_order.

Скопируйте токен (tsh_live_…), когда он появится. Повторно он показан не будет.

Токен передаётся в каждом запросе как Bearer:

Authorization: Bearer tsh_live_…

2. Три вызова

Все вызовы — POST https://mcp.tiny-shops.com/v1/<вызов> с JSON-телом. Те же вызовы доступны как MCP-инструменты с теми же именами на https://mcp.tiny-shops.com/mcp.

У каждого ответа одинаковая обёртка:

{ "ok": true, "data": { … } }
{ "ok": false, "error": { "code": "not_found", "message": "Order '42' not found" } }
HTTPerror.codeЗначение
400invalid_inputТело не соответствует входным данным вызова
401invalid_tokenТокен отсутствует, некорректен, истёк или отозван
403forbiddenНедостаточно прав у токена (например, токен на чтение вызывает ship_order)
404not_foundТакого заказа нет в вашем магазине — заказы других магазинов никогда не видны
404unknown_toolТакого вызова нет
429rate_limitedСлишком много вызовов; подождите Retry-After секунд

Лимиты — на токен: 100 вызовов чтения и 30 вызовов записи в минуту.

get_order — прочитать заказ

curl -X POST https://mcp.tiny-shops.com/v1/get_order \
  -H "Authorization: Bearer $TINYSHOPS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"orderId": 1042}'

data — объект заказа.

set_order_status — изменить статус

curl -X POST https://mcp.tiny-shops.com/v1/set_order_status \
  -H "Authorization: Bearer $TINYSHOPS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"orderId": 1042, "status": "PROCESSING"}'

data — { "orderId", "status", "order" }. Смена статуса применяет те же правила магазина, что и в панели, — например, кэшбэк, который начисляется при определённом статусе.

СтатусЗначение
PENDINGНовый заказ, ещё не подтверждён и не оплачен
PROCESSINGОплачен / собирается. «Отметить оплаченным» — значит установить этот статус.
SHIPPEDПередан перевозчику — лучше используйте ship_order, он сохраняет и трекинг
DELIVEREDПолучен покупателем
CANCELLEDОтменён

ship_order — отметить отправленным с трекингом

curl -X POST https://mcp.tiny-shops.com/v1/ship_order \
  -H "Authorization: Bearer $TINYSHOPS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"orderId": 1042, "carrier": "СДЭК", "trackingNumber": "1234567890", "trackingUrl": "https://www.cdek.ru/ru/tracking?order_id=1234567890", "notify": true}'
ПолеОбязательноПримечания
orderIdда
trackingUrlодно из трёхДолжен начинаться с http:// или https://
carrierодно из трёхДо 100 символов
trackingNumberодно из трёхДо 100 символов
notifyнет, по умолчанию falsetrue отправляет покупателю то же сообщение в Telegram, что и кнопка Отправить и уведомить в панели

Статус становится SHIPPED, поля трекинга сохраняются. С notify: true покупатель получает сообщение об отправке вашего магазина; кнопка Отследить заказ появляется, только если есть trackingUrl, иначе перевозчик и трек-номер пишутся в тексте сообщения.

data — { "order", "notified", "notifyFailureReason" }. notified отсутствует, если уведомление не запрашивали, и равно false с причиной (bot_not_configured, unreachable, send_failed), если сообщение отправить не удалось, — заказ в любом случае отмечен отправленным.

3. Вебхуки

Настройка

Откройте Настройки → Вебхуки заказов, укажите публичный https://-адрес и сохраните. Вы один раз получите секрет подписи (whsec_…) — сохраните его. Потеряли? Нажмите Сменить секрет; старый сразу перестанет работать. На странице также видны последние доставки.

Кнопка Отправить тестовое событие синхронно отправляет подписанный пример order.created. В тестовой обёртке есть "test": true и демонстрационные данные; настоящий заказ не создаётся. Ваш адрес всё равно должен проверить подпись и вернуть 2xx.

У каждого магазина один URL вебхука.

События

СобытиеКогда
order.createdКаждый новый заказ, при любом способе оформления и оплаты
order.updatedИзменился статус, трекинг или состав заказа — в панели, через API или через MCP. Если выставить заказу тот же статус, что у него уже есть, событие не отправляется

Запрос

POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: TinyShops-Webhooks/1
X-TinyShops-Event: order.updated
X-TinyShops-Delivery: 3b0c4f7e-5a41-4d8e-9f0e-2d6b1c1a9e27
X-TinyShops-Signature: 5f1c…e9a2

{
  "id": "3b0c4f7e-5a41-4d8e-9f0e-2d6b1c1a9e27",
  "event": "order.updated",
  "createdAt": "2026-09-23T10:15:02.114Z",
  "data": { …объект заказа… }
}
  • X-TinyShops-Signature — HMAC-SHA256 от тела запроса как есть, в нижнем регистре hex, с секретом подписи в качестве ключа.
  • X-TinyShops-Delivery и id в теле — идентификатор доставки. Он одинаков при всех повторах этой доставки.

Проверка подписи

Считайте HMAC по сырым байтам полученного тела, до разбора JSON, и сравнивайте за постоянное время.

import { createHmac, timingSafeEqual } from 'node:crypto';
import express from 'express';

const app = express();

app.post(
  '/tinyshops/orders',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const expected = createHmac('sha256', process.env.TINYSHOPS_WEBHOOK_SECRET)
      .update(req.body)
      .digest('hex');
    const received = req.get('X-TinyShops-Signature') ?? '';
    const valid =
      received.length === expected.length &&
      timingSafeEqual(Buffer.from(received), Buffer.from(expected));
    if (!valid) return res.status(401).end();

    const event = JSON.parse(req.body.toString('utf8'));
    res.status(204).end(); // ответьте быстро, работу сделайте потом
    handleOrderEvent(event);
  },
);
import hashlib, hmac, json, os
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["TINYSHOPS_WEBHOOK_SECRET"].encode()

@app.post("/tinyshops/orders")
def tinyshops_orders():
    expected = hmac.new(SECRET, request.get_data(), hashlib.sha256).hexdigest()
    received = request.headers.get("X-TinyShops-Signature", "")
    if not hmac.compare_digest(received, expected):
        abort(401)
    event = json.loads(request.get_data())
    handle_order_event(event)
    return "", 204

Повторы

  • Доставка успешна, если ваш адрес отвечает любым кодом 2xx в течение 10 секунд.
  • Иначе она повторяется до 5 раз: через 1 минуту, 5 минут, 30 минут, 2 часа и 6 часов. После шестой неудачной попытки доставка отмечается как неудачная и показывается на странице настроек.
  • События отправляются примерно в течение минуты после изменения.
  • Редиректы не выполняются: URL должен отвечать сам.

Идемпотентность и порядок

  • Используйте идентификатор доставки (X-TinyShops-Delivery или id в теле), чтобы отсеивать повторы. Сохраняйте состояние с самым новым data.updatedAt. Не убирайте дубликаты только по ID заказа и статусу: трекинг или состав заказа могут измениться без смены статуса.
  • Если установить заказу тот же статус, который уже установлен, событие не отправляется.
  • Что-то пропустили? Вызовите get_order — он возвращает ровно тот объект, который пришёл бы в вебхуке.

Объект заказа

{
  "id": 1042,
  "status": "SHIPPED",
  "paymentMethod": "Stripe",
  "totalPrice": 459000,
  "tipAmount": null,
  "currency": "RUB",
  "telegramUserId": "5566778899",
  "shippingAddress": { "name": "Иван Петров", "phone_number": "+79991234567" },
  "checkoutData": [{ "id": "f1", "label": "Адрес", "type": "text", "value": "ул. Ленина, 1" }],
  "items": [
    {
      "productId": "sneaker-01",
      "title": "Кроссовки для бега",
      "quantity": 1,
      "price": 459000,
      "size": "42",
      "variantOptionTitle": null,
      "variantValue": null
    }
  ],
  "trackingUrl": "https://www.cdek.ru/ru/tracking?order_id=1234567890",
  "carrier": "СДЭК",
  "trackingNumber": "1234567890",
  "createdAt": "2026-09-23T09:58:40.512Z",
  "updatedAt": "2026-09-23T10:15:02.101Z"
}
ПолеПримечания
totalPrice, items[].price, tipAmountВ минимальных единицах currency (копейки, центы; целые звёзды для XTR)
currencyКод валюты магазина
telegramUserIdTelegram user id покупателя, строкой (может превышать 2^53)
shippingAddressДанные доставки, собранные при оплате (например, order info от Telegram), или null
checkoutDataЗаполненные покупателем поля формы оформления, или null
trackingUrl, carrier, trackingNumbernull, пока не заданы