Для разработчиков и ИИ
QRTap API — управляемые QR-коды из вашего кода
REST + JSON, ключ в заголовке Authorization: Bearer, спека OpenAPI 3.1, вебхуки сканов и MCP-сервер для ИИ-агентов. Ключ выпускается сразу и на бесплатном тарифе: первый рабочий код — за пять минут, без звонков, счетов и интеграционных совещаний.
Что такое QRTap
QRTap — платформа управляемых QR-кодов, коротких ссылок и публичных страниц, сделанная в Кыргызстане. Каждый код кодирует постоянную короткую ссылку https://app.qrtap.kg/{slug}. Куда она ведёт — это отдельная, версионируемая сущность: назначение меняется одним PATCH-запросом, а напечатанный тираж — меню, наклейки на оборудование, вкладыши в пакеты, визитки, наружная реклама — продолжает работать. Печать перестаёт быть точкой невозврата.
Поверх этого API отдаёт то, ради чего обычно и приходят интеграторы: пакетное создание до 500 кодов за вызов (идемпотентно по вашему external_ref), готовые артефакты PNG/SVG/PDF с проверкой читаемости, сборку клиенту страницы-визитки, два ящика мини-CRM (отзывы гостей и заявки на запись), умные правила редиректа по устройству, языку и часам, вебхуки на каждый скан и анонимную агрегированную статистику. Персональные данные посетителей не собираются вовсе: ни IP, ни user-agent не сохраняются — ни в статистике, ни в вебхуках.
Первый код за 5 минут
1Выпустить ключ — бесплатно
Регистрируетесь и выпускаете ключ сами: app.qrtap.kg/workspace/api. Платить, чтобы попробовать, не нужно: на бесплатном тарифе ключ выдаётся сразу — один ключ и 100 запросов в сутки, этого хватает, чтобы собрать интеграцию и показать её заказчику. Раздел доступен владельцу рабочего пространства. Ключ вида qrt_live_… показывается один раз: мы храним только его SHA-256-хеш. Ключ авторизует всю организацию и отзывается без простоя — на время ротации могут работать два ключа сразу.
export QRTAP_KEY=qrt_live_XXXX
2Создать код одним запросом
Ответ приходит сразу — с постоянной ссылкой и адресами картинок:
curl -X POST https://app.qrtap.kg/api/v1/codes \
-H "Authorization: Bearer $QRTAP_KEY" \
-H "Content-Type: application/json" \
-d '{"target_url":"https://example.com/menu",
"label":"Меню, стол 5",
"external_ref":"table-5",
"group":"event_menu"}'
# 201 → {
# "id": "8f1c…", "slug": "k3v9tqzx",
# "short_url": "https://app.qrtap.kg/k3v9tqzx",
# "png_url": "https://app.qrtap.kg/api/v1/codes/8f1c…/qr?format=png",
# "svg_url": "https://app.qrtap.kg/api/v1/codes/8f1c…/qr?format=svg",
# "target_url": "https://example.com/menu", "state": "deployed", …
# }external_ref — ваш идентификатор (SKU, id партнёра, номер стола). Создание идемпотентно по нему: повтор того же external_ref вернёт существующий код, а не дубликат, поэтому вызов безопасно ретраить.
3Забрать готовый QR и отдать в печать
PNG для экрана и мессенджеров, SVG и PDF — для типографии. Каждый PNG перед отдачей декодируется обратно: если брендирование ухудшило читаемость, сервер откатится к простому коду и скажет об этом в заголовке X-QRTap-QA (branded | downgraded | plain).
curl -o qr.png -H "Authorization: Bearer $QRTAP_KEY" \ "https://app.qrtap.kg/api/v1/codes/<id>/qr?format=png&size=2000" # format: png | svg | pdf size: 256…3000 (только png, по умолчанию 1024) # errorCorrection: M | Q | H (по умолчанию Q) caption: подпись под кодом (svg)
Всё. Дальше — смена назначения без перепечатки и статистика; напечатанная ссылка и short_url при этом не меняются никогда:
curl -X PATCH https://app.qrtap.kg/api/v1/codes/<id> \
-H "Authorization: Bearer $QRTAP_KEY" -H "Content-Type: application/json" \
-d '{"target_url":"https://example.com/new-landing"}'
curl -H "Authorization: Bearer $QRTAP_KEY" \
"https://app.qrtap.kg/api/v1/codes/<id>/stats?from=2026-07-01T00:00:00Z"
# 200 → { "total": 128, "by_day": [{"day":"2026-07-20","count":68}],
# "by_device": [{"device":"mobile","count":110}] }Тарифы API
Отдельной подписки на API нет: лимиты — это лимиты вашего обычного тарифа QRTap. Бесплатный уровень существует, чтобы интеграцию можно было попробовать до покупки; граница продажи — не наличие ключа, а объём и набор возможностей за ним.
| Free | Основной — 990 сом/мес | Полный — 1990 сом/мес | |
|---|---|---|---|
| Запросов в сутки | 100 | 5 000 | 50 000 |
| Ключей | 1 | 3 | 10 |
| Запросов в минуту | 60 | 120 | 300 |
| Bulk и вебхуки | нет | да | да |
Первый месяц тарифа «Основной» — 99 сом. Подробности и оплата — на странице тарифов.
Две вещи, которые лучше знать до того, как упрётесь. Суточная квота считается на рабочее пространство, а не на ключ: три ключа на «Основном» — это те же 5 000 запросов в сутки на всех, а не 15 000. Ключи нужны, чтобы разделять интеграции и отзывать их поштучно, а не чтобы умножать объём. И сутки считаются по UTC: квота обнуляется в полночь UTC, то есть в 6 утра по Бишкеку (в кабинете расход показан в бишкекском времени).
Что открывается на платном тарифе
Три возможности требуют платного тарифа. На бесплатном они отвечают 402 Payment Required с кодом plan_required — именно 402, а не 403: запрос корректен, не хватает тарифа, и после подключения тот же самый запрос сработает без единой правки в вашем коде.
POST /api/v1/codes/bulk— пакетное создание кодов./api/v1/webhooksцеликом — регистрация, список и удаление вебхуков сканов.PUT /api/v1/codes/{id}/smart-rules— умные правила редиректа (чтение черезGETдоступно всем).
# 402 → {
# "error": "plan_required",
# "detail": "Bulk creation is available on a paid plan. Your workspace is on FREE…",
# "plan": "FREE",
# "upgrade_url": "https://app.qrtap.kg/workspace/upgrade"
# }Лимиты в бою: 429, заголовки и ретраи
Ограничителей два, и они разные по смыслу. Минутный — тормоз против разогнавшегося цикла: считается на каждый ключ отдельно, в памяти процесса, который обслужил запрос. При нескольких инстансах веба фактический потолок — лимит × число инстансов, поэтому цифра из таблицы это гарантированный минимум. Суточный — то, за что берут деньги: считается на всё рабочее пространство, хранится в базе, переживает перезапуск и общий для всех инстансов.
# Слишком часто → 429
{ "error": "rate_limited", "scope": "minute", "limit": 60, "remaining": 0,
"reset_at": "2026-08-07T09:31:00.000Z", "retry_after": 24, "plan": "FREE",
"detail": "Too fast: at most 60 requests per minute on the FREE plan…",
"upgrade_url": "https://app.qrtap.kg/workspace/upgrade" }
# Кончилась суточная квота → тоже 429, но другой код
{ "error": "quota_exceeded", "scope": "day", "limit": 100, "remaining": 0,
"reset_at": "2026-08-08T00:00:00.000Z", "retry_after": 52140, "plan": "FREE",
"detail": "Daily quota spent: 100 requests per day on the FREE plan…" }
# Заголовки обоих отказов:
Retry-After: 24
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1786000260Различайте scope: minute — подождите retry_after секунд и продолжайте, это временно; day — до reset_at (ближайшая полночь UTC) ждать бессмысленно, нужен тариф выше. upgrade_url приходит только тогда, когда апгрейд действительно поможет.
Важно для клиентской библиотеки: успешные ответы заголовки X-RateLimit-* не несут — они появляются только в ответе 429. Не стройте счётчик остатка на заголовках каждого ответа: их там нет. Расход за текущие сутки владелец видит в кабинете, а интеграция должна просто уважать Retry-After.
Не расходуют ни минутный лимит, ни суточную квоту: маршруты /api/v1/webhooks* (партнёр обязан суметь отозвать сломанный эндпоинт, даже исчерпав квоту) и всё, что вне /api/v1. Отказ по минутному тормозу суточную квоту не тратит. Отказ по тарифу (402) не тратит её там, где проверка тарифа идёт до учёта — на /api/v1/codes/bulk и /api/v1/webhooks*. У записи умных правил проверка живёт глубже, поэтому такой отказ расходует один запрос.
Аутентификация и ошибки
Каждый запрос — с ключом организации в заголовке Authorization: Bearer <ключ>:
Authorization: Bearer qrt_live_XXXX
Ошибки — JSON вида {"error":"...","detail":...}: 401 unauthorized (нет или отозван ключ), 400 invalid_request / invalid_url / invalid_id / invalid_group / unknown_design_template / invalid_image, 402 plan_required (bulk, вебхуки или умные правила на бесплатном тарифе), 404 not_found (в том числе для чужой организации — ID чужого тенанта неотличим от несуществующего), 422 invalid_content / invalid_rules / prohibited_content (последнее — подпись под кодом со стимулированием, запрещённым ст. 17 Закона КР «О рекламе»), 429 rate_limited и 429 quota_exceeded, 500 qa_failed (редкий сбой decode-проверки, безопасно повторить). Полный перечень по каждому эндпоинту — в OpenAPI-спеке.
Что умеет API v1
Семнадцать маршрутов, сгруппированных по задачам. Точные схемы запросов и ответов — в спеке.
POST /api/v1/codes— создать код; идемпотентно поexternal_ref.POST /api/v1/codes/bulk— пакет до 500 кодов, результат по каждому элементу отдельно; одна плохая строка не роняет партию, весь вызов безопасно ретраить. Платный тариф.GET /api/v1/codes— список с фильтрамиgroup,external_ref, пагинацией (limit1…500,offset) и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 /api/v1/pages,POST /api/v1/pages,GET /api/v1/pages/{slug},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 /api/v1/codes/{id}/smart-rules,PUT /api/v1/codes/{id}/smart-rules— умные правила редиректа (устройство, язык, часы). Запись — платный тариф.
Пакет и группы
Партия кодов — один вызов; пакетное создание доступно на платном тарифе (на бесплатном — 402 plan_required). Элементы обрабатываются независимо, ответ содержит created, failed и data с построчным итогом (ok: true и сам код либо ok: false с external_ref и кодом ошибки). Сводная статистика снимается по всей группе разом.
curl -X POST https://app.qrtap.kg/api/v1/codes/bulk \
-H "Authorization: Bearer $QRTAP_KEY" -H "Content-Type: application/json" \
-d '[{"target_url":"https://example.com/p/1","external_ref":"SMK-P-001","group":"partner"},
{"target_url":"https://example.com/p/2","external_ref":"SMK-P-002","group":"partner"}]'
# 201 → { "created": 2, "failed": 0, "data": [ {"ok":true, …}, {"ok":true, …} ] }
curl -H "Authorization: Bearer $QRTAP_KEY" \
"https://app.qrtap.kg/api/v1/groups/partner/stats"
# 200 → { "group":"partner", "codes": 2, "total": 41, "by_day":[…], "by_device":[…] }Страницы-визитки
Страница живёт по адресу 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" \
-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" -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" -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"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"
# 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" -H "Content-Type: application/json" \
-d '{"action":"publish"}'
# Новые заявки на запись
curl "https://app.qrtap.kg/api/v1/bookings?status=new" \
-H "Authorization: Bearer $QRTAP_KEY"
# 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" -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" \
-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"Условия: device (ios | android | desktop | other), lang (коды вида ru, ky) и hours (tz — IANA-зона, from/to — ЧЧ:ММ, to исключительно, окно через полночь допустимо, days — 1=пн…7=вс). Правила видят только огрублённые признаки: сырые IP, User-Agent и Accept-Language не сохраняются никогда.
Вебхуки сканов
Зарегистрируйте HTTPS-адрес — и на каждый скан ваших кодов придёт POST. В ответе на регистрацию один раз показывается signing_secret (префикс whsec_); сохраните его для проверки подписи. Вебхуки — платный тариф (иначе 402 plan_required); при этом сами маршруты /api/v1/webhooks* не расходуют ни минутный лимит, ни суточную квоту.
curl -X POST https://app.qrtap.kg/api/v1/webhooks \
-H "Authorization: Bearer $QRTAP_KEY" -H "Content-Type: application/json" \
-d '{"url":"https://your-service.example/qrtap/scan"}'
# 201 → { "id": "…", "url": "…", "active": true, "signing_secret": "whsec_…" }
# Каждый скан прилетает так:
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 нет персональных данных — только идентификаторы кода, ваши референсы, время и класс устройства.
Спека, MCP и готовые сценарии
- app.qrtap.kg/openapi.json — полная спека OpenAPI 3.1: генерируйте клиентов, импортируйте в Postman или Insomnia, отдавайте ИИ-агентам.
- /llms.txt и /llms-full.txt — описание сервиса для языковых моделей: короткий индекс и развёрнутое описание всех возможностей.
- app.qrtap.kg/mcp — MCP-сервер (npm-пакет
qrtap-mcp): 20 инструментов для Claude Desktop, Claude Code и любого MCP-совместимого ассистента, готовый блок конфигурации. - app.qrtap.kg/integrations — готовые сценарии подключения: CRM, POS и чеки, Telegram-бот, таблицы, no-code-автоматизация, типография.
Использование API регулируют Условия использования API, а также оферта и политика конфиденциальности. Коротко: за содержимое ссылок, на которые ведут ваши коды, отвечаете вы; перепродажа доступа к API и обход лимитов запрещены.