Для разработчиков
QRTap API — управляемые QR-коды из вашего кода
Создавайте QR-коды и короткие ссылки, собирайте клиенту страницу-визитку, забирайте отзывы и заявки на запись, настраивайте умные правила редиректа, меняйте назначение без перепечатки и получайте вебхуки в реальном времени. REST + JSON, Bearer-ключ, OpenAPI 3.1, MCP-сервер для ИИ-агентов.
Как это устроено
Каждый код кодирует стабильную короткую ссылку https://app.qrtap.kg/{slug}. Куда она ведёт — версионируемое назначение, которое меняется одним PATCH-запросом: напечатанный тираж не устаревает. Статистика агрегированная и анонимная: IP и user-agent посетителей не сохраняются вовсе.
Аутентификация
Все запросы — с ключом организации в заголовке Authorization: Bearer <ключ>. Выпустите ключ сами в кабинете: qrtap.kg/workspace/api (нужен платный тариф; раздел доступен владельцу рабочего пространства). Ключ показывается один раз (мы храним только его SHA-256-хеш) и отзывается без простоя: на время ротации могут действовать два ключа.
Authorization: Bearer qrtap_key_XXXX
Быстрый старт
1. Создать управляемый код (он же короткая ссылка):
curl -X POST https://app.qrtap.kg/api/v1/codes \
-H "Authorization: Bearer qrtap_key_XXXX" \
-H "Content-Type: application/json" \
-d '{"target_url":"https://example.com/menu","label":"Меню, стол 5"}'
# 201 → { "id": "...", "short_url": "https://app.qrtap.kg/k3v9tqzx",
# "png_url": ".../qr?format=png", "svg_url": ".../qr?format=svg", ... }2. Сменить назначение — напечатанный код и short_url не меняются:
curl -X PATCH https://app.qrtap.kg/api/v1/codes/<id> \
-H "Authorization: Bearer qrtap_key_XXXX" \
-H "Content-Type: application/json" \
-d '{"target_url":"https://example.com/new-landing"}'3. Статистика сканов (боты исключены, только агрегаты):
curl "https://app.qrtap.kg/api/v1/codes/<id>/stats?from=2026-07-01T00:00:00Z" \
-H "Authorization: Bearer qrtap_key_XXXX"
# 200 → { "total": 128, "by_day": [{"day":"2026-07-20","count":68}],
# "by_device": [{"device":"mobile","count":110}] }4. Готовый артефакт — PNG для шаринга, SVG/PDF для печати:
curl -o qr.png "https://app.qrtap.kg/api/v1/codes/<id>/qr?format=png&size=2000" \ -H "Authorization: Bearer qrtap_key_XXXX" # PNG проверяется декодированием перед отдачей; заголовок X-QRTap-QA # сообщает итог: branded | downgraded | plain.
Что умеет API v1
POST /api/v1/codes— создать код; идемпотентно поexternal_ref(повтор того же ref возвращает существующий код, не дубликат).POST /api/v1/codes/bulk— пакет до 500 кодов, результат по каждому элементу; весь вызов безопасно ретраить.GET /api/v1/codes— список с фильтрамиgroup,external_ref, пагинацией иinclude=stats(счётчик сканов всех кодов одним запросом).GET /api/v1/codes/{id},PATCH /api/v1/codes/{id}— чтение и смена назначения.GET /api/v1/codes/{id}/history— все назначения кода (аудит, новые сверху).GET /api/v1/codes/{id}/statsиGET /api/v1/groups/{group}/stats— агрегаты по коду и по целому типу кодов (partner, equipment, event_menu, bag_insert, training).GET /api/v1/codes/{id}/qr— артефакт PNG / SVG / PDF; брендированный, когда у кода есть шаблон дизайна.POST /api/v1/design-templates,GET /api/v1/design-templates— фирменный вид: цвет модулей + логотип в центре (data URI, санитизируется на сервере). Гейт decode-QA следит, чтобы код оставался считываемым.POST /api/v1/webhooks,GET /api/v1/webhooks,DELETE /api/v1/webhooks/{id}— вебхуки сканов, см. ниже.GET/POST /api/v1/pages,GET/PATCH /api/v1/pages/{slug}— страницы-визитки, см. ниже.GET /api/v1/reviews,POST /api/v1/reviews/{id}/moderate— отзывы гостей и модерация.GET /api/v1/bookings,POST /api/v1/bookings/{id}/status— заявки на запись и решение по ним.GET/PUT /api/v1/codes/{id}/smart-rules— умные правила редиректа (устройство, язык, часы).
Страницы-визитки
Страница живёт по адресу https://app.qrtap.kg/a/{slug}. Один POST собирает её целиком, ответ содержит public_url — наведите на него QR-код, и получится готовая визитка для печати. Повторный POST не создаёт вторую страницу: он публикует новую версию той же страницы, slug не меняется, поэтому напечатанный код продолжает работать. Контент санитизируется на сервере: телефон нормализуется, выживают только http/https-ссылки, лишние поля отбрасываются.
# 1. Собрать и опубликовать страницу
curl -X POST https://app.qrtap.kg/api/v1/pages \
-H "Authorization: Bearer qrtap_key_XXXX" \
-H "Content-Type: application/json" \
-d '{"content":{
"segment":"otzyvy","theme":"emerald",
"name":"Кофейня «Тандем»","specialty":"Спешелти-кофе в центре",
"phone":"0555 123 456","hours":"ежедневно 8:00–22:00",
"address":"Бишкек, ул. Киевская 100",
"services":[{"name":"Фильтр-кофе","price":"180 сом"}],
"reviewLinks":{"twogis":"https://2gis.kg/bishkek/firm/70000001"}}}'
# 201 → { "slug": "cozy-cafe-x1", "public_url": "https://app.qrtap.kg/a/cozy-cafe-x1", ... }
# 2. Навести на неё QR-код
curl -X POST https://app.qrtap.kg/api/v1/codes \
-H "Authorization: Bearer qrtap_key_XXXX" -H "Content-Type: application/json" \
-d '{"target_url":"https://app.qrtap.kg/a/cozy-cafe-x1","label":"Визитка «Тандем»"}'
# 3. Обновить содержимое — slug и печать не меняются
curl -X PATCH https://app.qrtap.kg/api/v1/pages/cozy-cafe-x1 \
-H "Authorization: Bearer qrtap_key_XXXX" -H "Content-Type: application/json" \
-d '{"content":{"name":"Кофейня «Тандем»","phone":"0555 123 456","hours":"пн–сб 9:00–21:00"}}'
# 4. Список страниц (без контента)
curl https://app.qrtap.kg/api/v1/pages -H "Authorization: Bearer qrtap_key_XXXX"PATCH заменяет контент целиком — если нужно поправить одно поле, сначала прочитайте страницу через GET /api/v1/pages/{slug}. Обязательны имя и хотя бы один контакт (телефон, WhatsApp, Instagram или ссылка на запись), иначе 422 invalid_content.
Отзывы и заявки на запись
Всё, что гость оставил на публичной странице, попадает в два ящика организации. Отзыв не виден никому, пока владелец его не опубликовал, — status=pending и есть очередь модерации. Хранится только то, что гость набрал сам: ни IP, ни user-agent.
# Очередь модерации отзывов
curl "https://app.qrtap.kg/api/v1/reviews?status=pending" \
-H "Authorization: Bearer qrtap_key_XXXX"
# 200 → { "data": [{ "id": "...", "author_name": "Айгуль",
# "body": "Очень уютно, спасибо!", "status": "pending", ... }] }
# Опубликовать отзыв (или скрыть: {"action":"hide"})
curl -X POST https://app.qrtap.kg/api/v1/reviews/<id>/moderate \
-H "Authorization: Bearer qrtap_key_XXXX" -H "Content-Type: application/json" \
-d '{"action":"publish"}'
# Новые заявки на запись
curl "https://app.qrtap.kg/api/v1/bookings?status=new" \
-H "Authorization: Bearer qrtap_key_XXXX"
# 200 → { "data": [{ "id": "...", "service": "Маникюр",
# "preferred_at": "завтра после 14:00", "guest_name": "Айгуль",
# "guest_phone": "0555 123 456", "status": "new", ... }] }
# Подтвердить или отклонить
curl -X POST https://app.qrtap.kg/api/v1/bookings/<id>/status \
-H "Authorization: Bearer qrtap_key_XXXX" -H "Content-Type: application/json" \
-d '{"status":"confirmed"}'preferred_at — свободный текст в том виде, как его набрал гость; платформа его не разбирает. Гостю QRTap ничего не отправляет: связаться с ним — по телефону из заявки. Вернуть заявку в статус new через API нельзя.
Умные правила (Smart Links)
Правила проверяются на каждом скане: побеждает первое совпавшее, если не совпало ничего — работает основное назначение кода. Сломанный или нечитаемый набор правил тоже деградирует к основному назначению, поэтому напечатанный код не сломать. Максимум 5 правил; [] или null очищает набор. Нужен платный тариф (иначе 402 plan_required).
curl -X PUT https://app.qrtap.kg/api/v1/codes/<id>/smart-rules \
-H "Authorization: Bearer qrtap_key_XXXX" \
-H "Content-Type: application/json" \
-d '{"rules":[
{"if":{"kind":"device","in":["ios"]},"to":"https://apps.apple.com/app/id1"},
{"if":{"kind":"device","in":["android"]},"to":"https://play.google.com/store/apps/details?id=kg.qrtap"},
{"if":{"kind":"hours","tz":"Asia/Bishkek","from":"22:00","to":"09:00"},
"to":"https://example.com/night-menu"}]}'
# Текущие правила
curl https://app.qrtap.kg/api/v1/codes/<id>/smart-rules \
-H "Authorization: Bearer qrtap_key_XXXX"Условия: device (ios | android | desktop | other), lang (коды вида ru, ky) и hours (tz — IANA-зона, from/to — ЧЧ:ММ, to исключительно, окно через полночь допустимо, days — 1=пн…7=вс). Правила видят только огрублённые признаки: сырые IP, User-Agent и Accept-Language не сохраняются никогда.
Лимит запросов
На каждый API-ключ — 120 запросов в минуту (окно в одну минуту) на все /api/v1/**. Сверх лимита приходит 429 с телом {"error":"rate_limited"} и заголовком Retry-After (секунды до сброса); рядом — X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. Маршруты управления вебхуками из лимита исключены. Счётчик живёт в памяти процесса, который обслужил запрос: при нескольких инстансах веба фактический потолок — 120 × число инстансов, поэтому считайте цифру гарантированным минимумом и всегда уважайте Retry-After, а не собственную арифметику.
Вебхуки сканов
Зарегистрируйте HTTPS-адрес — и на каждый скан ваших кодов придёт POST. В ответе на регистрацию один раз показывается signing_secret; сохраните его для проверки подписи.
POST <ваш url>
X-QRTap-Event: scan
X-QRTap-Signature: sha256=<hex>
Content-Type: application/json
{ "code_id": "...", "external_ref": "SMK-P-001", "group": "partner",
"ts": "2026-07-20T12:34:56.000Z", "device_type": "mobile" }Подпись — HMAC-SHA256 от сырого тела запроса на вашем signing_secret; сравните hex-дайджест со значением после sha256=:
import { createHmac, timingSafeEqual } from "node:crypto";
const expected = createHmac("sha256", process.env.QRTAP_WEBHOOK_SECRET)
.update(rawBody)
.digest("hex");
const given = signatureHeader.replace(/^sha256=/, "");
const ok =
given.length === expected.length &&
timingSafeEqual(Buffer.from(given, "hex"), Buffer.from(expected, "hex"));Доставка — at-least-once: отвечайте любым 2xx в течение 8 секунд, обработчик делайте идемпотентным. Не-2xx или таймаут ретраятся с экспоненциальной паузой (порядка 2n минут, максимум 12 часов между попытками), всего до 8 попыток. В payload нет персональных данных — только идентификаторы кода, ваши референсы, время и класс устройства.
Машиночитаемая спецификация
Полная спека OpenAPI 3.1 — app.qrtap.kg/openapi.json: генерируйте клиентов, импортируйте в Postman/Insomnia, скармливайте ИИ-агентам. Для LLM есть краткий /llms.txt и расширенный /llms-full.txt.
MCP-сервер для ИИ-агентов
QRTap подключается к Claude Desktop, Claude Code и любому MCP-совместимому агенту через наш MCP-сервер (stdio, без внешних зависимостей). Агент получает инструменты: создать код или короткую ссылку, собрать клиенту страницу, разобрать очередь отзывов и заявок, сменить назначение, настроить умные правила, забрать статистику.
{
"mcpServers": {
"qrtap": {
"command": "node",
"args": ["/path/to/mozart-qr-platform/packages/mcp-server/bin/qrtap-mcp.mjs"],
"env": {
"QRTAP_API_KEY": "qrtap_key_XXXX",
"QRTAP_API_URL": "https://app.qrtap.kg"
}
}
}
}Инструменты: qrtap_create_code, qrtap_list_codes, qrtap_change_destination, qrtap_code_stats, qrtap_create_short_link, qrtap_create_page, qrtap_update_page, qrtap_list_pages, qrtap_list_reviews, qrtap_moderate_review, qrtap_list_bookings, qrtap_set_booking_status, qrtap_set_smart_rules. Подробности и установка — в README пакета packages/mcp-server репозитория платформы.
Ошибки
Ошибки — JSON вида {"error":"...","detail":...}: 401 unauthorized (нет или отозван ключ), 400 invalid_request / invalid_url / invalid_id / unknown_design_template / invalid_image, 402 plan_required (умные правила на бесплатном тарифе), 404 not_found (в том числе для чужой организации — ID чужого тенанта неотличим от несуществующего), 422 invalid_content / invalid_rules / prohibited_content (последнее — подпись под кодом со стимулированием, запрещённым ст. 17 Закона КР «О рекламе»), 429 rate_limited. Полный перечень по каждому эндпоинту — в OpenAPI-спеке.