Для разработчиков

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-спеке.

На главную · OpenAPI · Поддержка · Оферта