Все операции в ЗалПлан доступны через JSON REST API. JWT-аутентификация, multi-tenant изоляция, идемпотентность, webhook-доставка, машиночитаемая спецификация OpenAPI 3.1.
Интерактивная документация. Можно попробовать запросы прямо из браузера.
Чистая ссылка-документация для беглого просмотра ресурсов.
Машиночитаемая спецификация. Генерируйте клиенты для любого языка.
Готовые клиентские библиотеки. npm install @zalplan/sdk / pip install zalplan.
# 1. Получить токен
curl -X POST https://zalplan.ru/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"..."}'
# 2. Запрос с токеном
TOKEN="..."
curl -H "Authorization: Bearer $TOKEN" https://zalplan.ru/api/v1/locations ЗалПлан говорит на iCalendar (RFC 5545) в обе стороны. Подписывайте Google / Apple / Outlook на наши фиды и импортируйте чужие календари — события становятся блоками доступности.
# 1. Создать подписанный фид
curl -X POST https://zalplan.ru/api/v1/calendar-feeds \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"Все бронирования","scope_type":"account"}'
# → {"id": "cf_...", "public_url": "https://zalplan.ru/api/v1/calendar/feed/<slug>/<token>.ics"}
# 2. Подпишите Google / Apple / Outlook на public_url
# Подтверждённые бронирования стримятся VEVENT-ами с UID=booking-<slug>@zalplan.ru
# 3. Прокатить токен (старый URL станет 404)
curl -X POST https://zalplan.ru/api/v1/calendar-feeds/<slug>/rotate \
-H "Authorization: Bearer $TOKEN" # 1. Подписаться на удалённый ICS (Google, Outlook, кастомный)
curl -X POST https://zalplan.ru/api/v1/external-calendars \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"Google зала","url":"https://calendar.google.com/.../basic.ics","space_id":"spc_...","sync_mode":"read_only_blocking"}'
# 2. Синхронизировать (по расписанию через Celery beat или вручную)
curl -X POST https://zalplan.ru/api/v1/external-calendars/<slug>/sync \
-H "Authorization: Bearer $TOKEN"
# → {"added": 3, "updated": 1, "removed": 0}
# 3. Импортированные события с blocks_availability=true теперь блокируют /v1/availability/check
# Конфликты возвращаются с kind="external" account / location / space / user. Каждый scope определяет, какие бронирования попадают в ICS.read_only_blocking (блокирует), read_only_informational (только показывает), manual_review (требует подтверждения).Принимайте оплату прямо в виджете. Поддерживаются ЮKassa, Тинькофф, CloudPayments и Stripe. По каждому подтверждённому платежу автоматически выпускается чек по 54-ФЗ через Атол ОФД.
# Виджет или ваше приложение спрашивают платформу,
# какие эквайринги настроены на сервере:
curl https://zalplan.ru/api/v1/public/payment-providers
# → {"providers": ["yookassa", "tinkoff"]} curl -X POST https://zalplan.ru/api/v1/public/checkout \
-H 'Content-Type: application/json' \
-d '{
"booking_slug": "bk_01H...",
"provider": "yookassa",
"return_url": "https://example-venue.ru/booking?zp_intent=PENDING"
}'
# → {
# "id": "pmt_01H...",
# "provider": "yookassa",
# "confirmation_url": "https://yoomoney.ru/checkout/payments/v2/...",
# "status": "pending"
# }
# Перенаправьте браузер на confirmation_url. После оплаты пользователь
# вернётся на return_url; ваше приложение может опросить
# /v1/public/checkout/{id} для актуального статуса. # Каждый провайдер шлёт server-to-server:
POST https://zalplan.ru/api/v1/webhooks/payments/{provider}
# ЮKassa: IP allow-list, тело { "event": "payment.succeeded", "object": {...} }
# Тинькофф: SHA-256 Token в теле, шифрует все верхнеуровневые scalar-поля + Password
# CloudPayments: HMAC-SHA256 в заголовке Content-HMAC (base64)
# Stripe: t=…,v1=… в заголовке Stripe-Signature (HMAC-SHA256 c защитой от replay 5 мин)
# ЗалПлан мэтчит provider_payment_id → PaymentIntent, обновляет статус,
# зеркалит на booking.payment_status / invoice.paid_cents, и фаерит
# исходящий webhook 'payment.received' и Celery-таску фискализации. # Полный или частичный возврат
curl -X POST https://zalplan.ru/api/v1/payment-intents/pmt_01H.../refund \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"amount_cents": 50000, "reason": "Клиент отменил"}'
# → PaymentIntent { status: "partial_refund", refunded_cents: 50000, ... } receipt_id. В тест-режиме (без креденшелов) пишется синтетический test_* id.pending → awaiting_3ds → confirmed / failed / cancelled; затем refunded / partial_refund.Маркетинговые скидки, спец-тарифы, подарочные карты с балансом. Применяются на этапе бронирования — в админке и через виджет.
# Через REST (или просто в админке: «Промокоды» → «Создать промокод»)
curl -X POST https://zalplan.ru/api/v1/promo-codes \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"code": "NEWYEAR2026",
"discount_type": "percent",
"discount_value": 1500,
"min_spend_cents": 500000,
"valid_until": "2026-12-31T23:59:00+03:00",
"max_redemptions": 100,
"first_time_only": true,
"stack_mode": "exclusive"
}'
# Типы скидок:
# percent — процент (значение в base points: 1500 = 15%)
# fixed — фиксированная сумма (cents/копейки)
# override_rate — спец. почасовой тариф (cents/час)
# free_addon — бесплатная услуга/доп (значение = price addon в cents)
#
# Режим стека (stack_mode):
# exclusive — нельзя комбинировать с другими промокодами
# stackable — суммируется со всеми «совместимыми»
# best_only — применяется только если даёт бóльшую скидку, чем сумма stackable # Открытый эндпоинт — виджет вызывает его при нажатии «Применить промокод»:
curl -X POST https://zalplan.ru/api/v1/public/promo-codes/validate \
-H "Content-Type: application/json" \
-d '{
"code": "NEWYEAR2026",
"space_id": "spc_…",
"booking_total_cents": 1200000,
"client_email": "anna@example.com"
}'
# Ответ:
{
"valid": true,
"discount_cents": 180000,
"applied_promos": [{"code":"NEWYEAR2026","amount_cents":180000,"discount_type":"percent"}],
"applied_gift_cards": [],
"errors": []
} # Виджет (и админка) передаёт promo_code в POST /bookings — backend сам
# выполняет валидацию, списывает discount с total_price_cents, добавляет
# отрицательную строку в price_breakdown, инкрементит redemption_count
# и пишет аудит-запись в promo_redemptions.
curl -X POST https://zalplan.ru/api/v1/bookings \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"space_id": "spc_…",
"start_time": "2026-12-31T19:00+03:00",
"end_time": "2027-01-01T04:00+03:00",
"client_email": "anna@example.com",
"promo_code": "NEWYEAR2026"
}' # Выпустить одну карту:
curl -X POST https://zalplan.ru/api/v1/gift-cards \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"initial_balance_cents": 1000000,
"issued_to_email": "gift-recipient@example.com",
"expires_at": "2027-12-31T23:59+03:00",
"sold_for_cents": 800000
}'
# Сгенерировать партию (с автоматическим CSV-экспортом в админке):
curl -X POST https://zalplan.ru/api/v1/gift-cards/bulk-generate \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "count": 50, "initial_balance_cents": 500000, "prefix": "NY26-" }'
# Карты применяются тем же полем promo_code в POST /bookings. Сначала
# списывается промо-скидка (если есть), затем gift card дебитуется
# с оставшейся суммы. Аудит — в /gift-cards/{slug}/redemptions. channel_restriction), помещением (space_restriction), минимальной суммой (min_spend_cents), окном дат (valid_from/valid_until), лимитом использований (max_redemptions) и режимом «только первый раз» (first_time_only).promo_redemptions с discount_cents и снапшотом кода — для отчётности и отмены.active → paused (временное отключение) → archived (мягкое удаление). Подарочные карты дополнительно переходят в depleted, когда баланс достигает 0.Площадки зарабатывают не только на аренде зала. ЗалПлан хранит каталог услуг (кейтеринг, декор, A/V, персонал), инвентарь оборудования с учётом запасов и список внешних поставщиков. Любая услуга или оборудование добавляется к бронированию как строка с количеством и ценой — общая сумма пересчитывается автоматически.
# Создать категорию
curl -X POST https://zalplan.ru/api/v1/service-categories \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "name": "Кейтеринг", "icon": "utensils" }'
# Создать услугу внутри категории
curl -X POST https://zalplan.ru/api/v1/service-items \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"category_id": "svccat_…",
"name": "Фуршет на 50 гостей",
"unit": "per_person",
"price_cents": 250000,
"vat_percent": 2000
}'
# Единицы измерения: per_hour | per_event | per_person | per_unit
# vat_percent — basis points (2000 = 20% НДС). Категории можно сортировать
# через POST /service-categories/reorder со списком slug-ов. curl -X POST https://zalplan.ru/api/v1/equipment-items \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"location_id": "loc_…",
"name": "Микрофон беспроводной Shure",
"total_stock": 6,
"rental_price_cents": 50000,
"rental_unit": "per_event",
"serial_numbers": ["SN-001","SN-002","SN-003"]
}'
# Состояния (maintenance_status):
# operational — работает, доступно для бронирования
# maintenance — на обслуживании
# broken — сломано (никогда не доступно)
# Проверка доступности на интервал:
curl -X POST https://zalplan.ru/api/v1/equipment-items/check-availability \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"equipment_id": "equip_…",
"start_time": "2026-06-01T10:00+03:00",
"end_time": "2026-06-01T18:00+03:00",
"requested_quantity": 4
}' # Добавить позицию: услугу ИЛИ оборудование (не оба сразу).
curl -X POST https://zalplan.ru/api/v1/bookings/bk_…/line-items \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"equipment_item_id": "equip_…",
"quantity": 4,
"unit_price_cents": 50000
}'
# Бэкенд проверяет, что для оборудования суммарное количество по всем
# активным бронированиям в этом окне не превысит total_stock. При нехватке —
# 409 conflict с деталями {already_reserved, total_stock, requested}.
#
# При каждом добавлении/удалении/изменении quantity backend
# пересчитывает booking.total_price_cents и обновляет price_breakdown:
# добавляет строки type="line_item" с amount_cents = subtotal_cents. curl -X POST https://zalplan.ru/api/v1/vendors \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Кейтеринг «Премьера»",
"contact_name": "Иван Иванов",
"email": "i@premiera.ru",
"phone": "+7 495 123 4567",
"service_areas": ["catering","decor"],
"contract_url": "https://drive.example.com/contract.pdf"
}'
# Любая услуга или оборудование может ссылаться на vendor_id —
# в админке и отчётах увидите, кто стоит за каждой позицией каталога. POST /v1/availability/check теперь принимает equipment: [{ equipment_id, quantity }] и возвращает equipment_conflicts: [] с деталями нехватки запаса.vat_percent хранится в basis points (2000 = 20%). При оформлении чека 54-ФЗ это значение прилетает в позицию чека.archived = true) — историческая отчётность не теряется.Генерируйте договоры, акты, оферты и NDA из markdown-шаблонов с подстановкой {{переменных}} бронирования, клиента и площадки. ЗалПлан сразу отдаёт PDF на DejaVu Cyrillic, а ссылка /sign позволяет клиенту подписать документ — мы фиксируем имя, email, время, IP и SHA-256 исходного PDF.
# Создать шаблон
curl -X POST https://zalplan.ru/api/v1/document-templates \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Договор оказания услуг",
"doc_type": "contract",
"requires_signature": true,
"content_markdown": "# Договор No {{booking.number}}\n\nИсполнитель: {{account.legal_name}} (ИНН {{account.inn}}).\n\nЗаказчик: {{client.legal_name}} (ИНН {{client.inn}}).\n\n## Сроки\n\nС {{booking.start_date}} {{booking.start_time}} по {{booking.end_date}} {{booking.end_time}}.\n\n## Стоимость\n\nИтого: {{booking.total_rub}} ₽."
}'
# Получить каталог доступных переменных:
curl -H "Authorization: Bearer $TOKEN" https://zalplan.ru/api/v1/document-templates/variables
# → {"data": [{"key": "account.name", "label": "..."}, ...]} curl -X POST https://zalplan.ru/api/v1/documents \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"template_id": "doctmpl_…",
"booking_id": "bk_…"
}'
# → {
# "id": "doc_…",
# "title": "Договор оказания услуг",
# "status": "draft",
# "pdf_url": "https://zalplan.ru/api/v1/uploads/<acc_id>/doc_doc_….pdf",
# "rendered_markdown": "...",
# "document_hash": "…sha256…",
# "version": 1
# }
# Mail-merge подставит:
# {{account.name}} {{account.legal_name}} {{account.inn}} {{account.address}}
# {{client.name}} {{client.email}} {{client.phone}} {{client.legal_name}} {{client.inn}}
# {{booking.slug}} {{booking.number}} {{booking.start_date}} {{booking.end_date}}
# {{booking.duration_hours}} {{booking.total_rub}} {{booking.total_kopecks}}
# {{location.name}} {{location.address}} {{space.name}} {{space.capacity}}
# {{today}} {{today_rus}} curl -X POST https://zalplan.ru/api/v1/documents/doc_…/send \
-H "Authorization: Bearer $TOKEN"
# → {
# "status": "sent",
# "signing_url": "https://zalplan.ru/sign?d=doc_…&t=<token>",
# "sign_token_expires_at": "2026-06-25T…"
# }
# Отправьте signing_url клиенту — он откроет страницу /sign, прочитает документ,
# введёт ФИО + email и нажмёт "Подписать". Бэкенд:
# 1. запишет событие "signed" с IP и user-agent в document_signature_events
# 2. перегенерирует PDF с финальной страницей-сертификатом (имя, время, IP,
# SHA-256 исходного документа)
# 3. вернёт signed_pdf_url и переведёт документ в статус "signed"
# 4. одноразовый sign_token будет инвалидирован curl -H "Authorization: Bearer $TOKEN" https://zalplan.ru/api/v1/documents/doc_…/events
# → {"data": [
# {"event": "sent", "created_at": "…", "actor_name": "Менеджер"},
# {"event": "viewed", "created_at": "…", "actor_ip": "…"},
# {"event": "signed", "created_at": "…", "actor_name": "Иван Иванов",
# "actor_email": "i@example.com", "actor_ip": "…",
# "payload": {"signed_pdf_url": "…", "document_hash": "…"}}
# ]} contract / act / offer / invoice / nda / custom. Поле doc_type наследуется из шаблона, но переопределяется при создании.content_markdown у шаблона инкрементирует version. Регенерация документа тоже бампает версию и переводит его обратно в draft.signed попадает в /documents/<slug>/events.#/##/###, **жирный**, *курсив*, маркеры - item, разделитель ---.Авто-рассылки email и SMS: триггеры по событиям бронирования, расписание или ручной запуск. Сегментация по тегам / общему чеку / давности брони / площадке, A/B варианты, трекинг открытий + кликов и one-click отписка.
curl -X POST https://zalplan.ru/api/v1/campaigns \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Спасибо за бронь",
"channel": "email",
"trigger_type": "event",
"trigger_event": "booking.confirmed",
"trigger_delay_minutes": 30,
"audience_filter": {
"min_total_spend_cents": 50000,
"last_booking_within_days": 90,
"tags": ["VIP"]
},
"subject_template": "{{client.name}}, спасибо за бронирование!",
"body_template": "Здравствуйте, {{client.name}}!\n\nЖдём вас {{booking.start_date}} в {{booking.start_time}}.\n\nПолучите 5% скидку: [Забронировать снова](https://zalplan.ru/book?promo=COMEBACK5)",
"status": "active"
}'
# → {"id": "camp_…", "status": "active", ...} trigger_type=event + trigger_event: booking.confirmed, booking.completed, booking.cancelled, client.created, quote.abandoned, payment.received. Срабатывает синхронно при событии — рассылка ставится в очередь и доставляется celery-воркером в течение минуты (или после trigger_delay_minutes).trigger_type=scheduled: запускается из celery beat каждые 5 минут, фильтрует аудиторию и создаёт отправления (не чаще 1 раза в сутки на одну кампанию).trigger_type=manual: запускается из админки или POST /v1/campaigns/{slug}/send.Все ключи объединяются по AND. Пустой фильтр — вся база клиентов.
tags: [str] — клиенты с любым из этих теговmin_total_spend_cents / max_total_spend_cents: int — суммарный чек по подтверждённым/оплаченным/завершённым бронямlast_booking_within_days: int — последняя бронь не старше указанного количества днейlocation_ids: [int] — клиенты, бронировавшие хотя бы одну из площадокcreated_within_days: int — клиенты, зарегистрированные не позже указанного количества дней назадcurl -X PATCH https://zalplan.ru/api/v1/campaigns/camp_… \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"variants": [
{"key": "A", "subject": "Скидка 10% на следующее бронирование",
"body": "Дорогой {{client.name}}, ..."},
{"key": "B", "subject": "Спасибо за выбор {{account.name}}",
"body": "Здравствуйте, {{client.name}}, ..."}
]
}'
# Распределение варианта детерминированное:
# variant = variants[ sha256(client_id + ":" + campaign_id) % len(variants) ]
# Один клиент всегда получит один и тот же вариант — отчёты по A/B стабильны. При отправке email-кампании сервер автоматически:
[ссылку](https://…) на трекинг-редирект https://zalplan.ru/api/v1/public/marketing/track/click/{token}?url=… — клик логируется в campaign_sends.clicked_at./api/v1/public/marketing/track/open/{token}.gif — открытие логируется в opened_at.suppression_entries и блокирует все последующие письма с этого аккаунта.suppression_entries — если адрес уже в стоп-листе, статус становится suppressed.# Сводная статистика
curl -H "Authorization: Bearer $TOKEN" https://zalplan.ru/api/v1/campaigns/camp_…/stats
# → {
# "totals": {"total": 312, "sent": 280, "opened": 152, "clicked": 47,
# "bounced": 8, "suppressed": 24, "pending": 0},
# "by_variant": {"A": {"sent": 142, "opened": 78, "clicked": 25},
# "B": {"sent": 138, "opened": 74, "clicked": 22}},
# "open_rate": 0.543, "ctr": 0.168
# }
# Полный журнал отправок
curl -H "Authorization: Bearer $TOKEN" \
"https://zalplan.ru/api/v1/campaign-sends?campaign_id=camp_…&status=clicked"
# Список адресов-отписок аккаунта
curl -H "Authorization: Bearer $TOKEN" https://zalplan.ru/api/v1/suppression-entries email работает через SMTP (тот же, что подтверждения броней). sms пока в режиме stub-логирования — провайдер (SMSC.ru / Twilio) подключается в backend/app/services/sms.py без изменений сервиса.tracking_token; повторное открытие пикселя или клик не дублируют событие.Сбор пост-ивентовых отзывов через подписанную ссылку, модерация в админке, агрегированный рейтинг и список последних отзывов в виджете площадки. Авто-приглашение уходит клиенту через 24 часа после booking.end_time — каждый час Celery-beat сметает завершённые брони и создаёт новые ReviewRequest.
schedule-review-requests ставит её каждый час по броням, у которых end_time прошёл больше 24 часов назад, статус completed/paid/in_progress/partially_paid и есть e-mail клиента.https://zalplan.ru/review?t={token}. SMTP-канал тот же, что у подтверждений броней и документов.pending. Админ переводит в published, hidden или spam в разделе «Отзывы». Только published попадают в публичные ответы и в агрегат.reply на той же записи, через PATCH /v1/reviews/{slug}/reply. Отображается под отзывом в виджете.# Список pending-отзывов одной площадки
curl -H "Authorization: Bearer $TOKEN" \
"https://zalplan.ru/api/v1/reviews?status=pending&location_id=loc_…"
# Опубликовать отзыв
curl -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"status":"published"}' \
https://zalplan.ru/api/v1/reviews/rev_…/status
# Ответить от имени площадки
curl -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"reply":"Спасибо за тёплые слова! Ждём вас снова."}' \
https://zalplan.ru/api/v1/reviews/rev_…/reply
# Сводный рейтинг площадки
curl -H "Authorization: Bearer $TOKEN" \
https://zalplan.ru/api/v1/reviews/-/summary/loc_…
# → {"location_id":"loc_…","average_rating":4.7,"count":23,"distribution":{"1":0,"2":1,"3":2,"4":5,"5":15}} # 1. Открыть форму
curl https://zalplan.ru/api/v1/public/reviews/submit/{token}
# → {"token":"…","booking":{…},"location":{…},"space":{…},"client_name":"Иван"}
# 2. Отправить отзыв (1-5 звёзд, текст и имя — необязательны)
curl -X POST https://zalplan.ru/api/v1/public/reviews/submit/{token} \
-H 'Content-Type: application/json' \
-d '{"rating":5,"text":"Отличная площадка!","reviewer_name":"Иван"}'
# → {"id":"rev_…","status":"pending","message":"Спасибо за отзыв! …"} # Сводка
curl https://zalplan.ru/api/v1/public/locations/loc_…/reviews/summary
# → {"location_id":"loc_…","average_rating":4.7,"count":23,"distribution":{…}}
# Последние 10 опубликованных
curl "https://zalplan.ru/api/v1/public/locations/loc_…/reviews?limit=10"
# → {"data":[{"id":"rev_…","rating":5,"text":"…","reviewer_name":"Гость","reply":null,"reply_at":null,"created_at":"…"}]} # Создать токен сразу после завершения брони
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"booking_id":"bk_…"}' \
https://zalplan.ru/api/v1/reviews/-/requests
# → {"id":"rreq_…","token":"…","review_link":"https://zalplan.ru/review?t=…","existing":false} average_rating в виджете обновится после первой публикации./venues/[slug] подмешивает AggregateRating в JSON-LD LocalBusiness, когда у площадки опубликован хотя бы один отзыв — см. раздел «Публичный каталог» ниже.SEO-витрина на https://zalplan.ru/venues/ — Airbnb-style карточки, индексируемые Google. Astro собирает страницы детального просмотра на этапе билда из публичного API; sitemap.xml генерируется динамически.
catalog-public# Список площадок с фильтрами (city, q, type, capacity_min/max, skip/limit)
curl "https://zalplan.ru/api/v1/public/venues?city=Москва&capacity_min=50&limit=12"
# Полная карточка одной площадки (фото, залы, отзывы, FAQ)
curl "https://zalplan.ru/api/v1/public/venues/loc_…"
# Города с количеством площадок — для фильтра
curl https://zalplan.ru/api/v1/public/venues/cities
# Слаги для sitemap.xml
curl https://zalplan.ru/api/v1/public/sitemap /venues/index.astro — список площадок с клиентскими фильтрами по городу / вместимости / типу / поиску./venues/[slug].astro — `getStaticPaths()` фетчит /v1/public/sitemap, затем по каждому слагу — /v1/public/venues/{slug}. Если бэкенд недоступен — билд проходит с пустым списком, на следующем ребилде заполнится./sitemap.xml.ts — Astro endpoint, возвращает application/xml с маркетинговыми страницами + всеми опубликованными площадками. Падение бэкенда не ломает билд — отдаём минимальный валидный urlset.LocalBusiness — адрес, координаты, телефон, e-mail, openingHours, image (до 6 фото), priceRange="₽₽".AggregateRating внутри LocalBusiness — добавляется только когда у площадки reviews_summary.count > 0.EventVenue — maximumAttendeeCapacity = сумма capacity_max всех залов; geo переиспользует координаты.FAQPage — собирается из /v1/locations/{slug}/faq, если есть хоть один вопрос.# Список / создание / правка / удаление вопросов
curl https://zalplan.ru/api/v1/locations/loc_…/faq
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"question":"Можно ли пронести свой алкоголь?","answer":"Да, пробковый сбор 500 ₽."}' \
https://zalplan.ru/api/v1/locations/loc_…/faq
curl -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"answer":"…"}' https://zalplan.ru/api/v1/locations/loc_…/faq/faq_…
# Перетягивание мышью в UI → POST /faq/-/reorder с массивом слагов
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"order":["faq_A","faq_C","faq_B"]}' \
https://zalplan.ru/api/v1/locations/loc_…/faq/-/reorder status="active" и public_booking_enabled=true. Помещения — те же критерии плюс public_visibility=true.robots.txt разрешает индексацию всего, кроме /widget/, /review, /sign и /api/; ссылается на https://zalplan.ru/sitemap.xml.Walk-in продажи через стойку: кофейни, бары, библиотеки-кафе. Кассир открывает смену с начальным остатком наличных, пробивает позиции из каталога услуг (SP-13), принимает оплату наличными / картой / смешанной, в конце дня закрывает смену с пересчётом денежного ящика и Z-отчётом.
POST /v1/cashier-shifts/open с location_id и opening_balance_cents. Один кассир — одна открытая смена на площадку. Дубликат ловится с кодом conflict.POST /v1/cashier-shifts/{slug}/close с closing_balance_cents (физический пересчёт денег). В ответ — финальный Z-отчёт с cash_variance_cents (расхождение между ожидаемым и фактическим остатком).GET /v1/cashier-shifts/{slug}/z-report — превью можно дёрнуть в любой момент; считает sales_count, line_count, refund_count, разбивку by_method (cash / card), скидки и возвраты.# Открыть смену
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"location_id":"loc_…","opening_balance_cents":500000}' \
https://zalplan.ru/api/v1/cashier-shifts/open
# Пробить чек с двумя позициями
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"shift_id":"shift_…","payment_method":"cash",
"lines":[
{"service_item_id":"svc_espresso","quantity":2},
{"service_item_id":"svc_croissant","quantity":1}
]}' \
https://zalplan.ru/api/v1/counter-sales
# Возврат продажи
curl -X POST -H "Authorization: Bearer $TOKEN" \
https://zalplan.ru/api/v1/counter-sales/sale_…/refund
# Закрыть смену
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"closing_balance_cents":555000}' \
https://zalplan.ru/api/v1/cashier-shifts/shift_…/close cash — наличные. Не идут через SP-11 PaymentIntent, фискализация позже (или вручную через ОФД).card — банковская карта. Опционально можно сослаться на существующий PaymentIntent через card_payment_intent_id (если оплата прошла через YooKassa / Tinkoff).mixed — часть наличными, часть картой. Сумма cash_amount_cents + card_amount_cents должна совпадать с итогом, иначе 400 validation_error.cash поле «получено наличными» показывает сдачу в реальном времени.CashierShift — SP-20 введёт отдельную StaffShift для рабочего графика, чтобы две концепции не путались.Выгрузка финансовых операций в российские учётные системы: 1С Бухгалтерия 8.3 через CommerceML 2.0 XML, МойСклад через REST API, Контур.Эльба через CSV, и универсальный Excel (XLSX) для оффлайн-обработки. Все экспорты создаются как асинхронные задания: POST /v1/exports возвращает ExportJob в статусе pending, Celery worker перебирает данные и через GET /v1/exports/{slug} вы получаете готовый file_url. Параметр async=false переводит на синхронное исполнение для коротких диапазонов.
# XLSX за май 2026, синхронно — файл готов сразу
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"format": "xlsx",
"scope": "all",
"date_from": "2026-05-01T00:00:00Z",
"date_to": "2026-05-31T23:59:59Z"
}' \
"https://zalplan.ru/api/v1/exports?async=false"
# → {"id":"expj_...","status":"completed","file_url":"https://zalplan.ru/api/v1/uploads/…/exp_expj_….xlsx",
# "row_count":42,"summary":{"invoices":18,"payments":20,"clients":4}}
# 1С CommerceML 2.0 XML, в очередь
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"format":"onec_xml","scope":"invoices","date_from":"2026-05-01T00:00:00Z","date_to":"2026-05-31T23:59:59Z"}' \
https://zalplan.ru/api/v1/exports
# → {"id":"expj_...","status":"pending",...}
# Опрос статуса
curl -H "Authorization: Bearer $TOKEN" https://zalplan.ru/api/v1/exports/expj_...
# → {"status":"completed","file_url":"..."} onec_xml) — XML с корневым <КоммерческаяИнформация ВерсияСхемы="2.05">, секциями <Контрагенты> и <Документы>. Каждый счёт — <Документ><ХозОперация>Заказ покупателя</ХозОперация>, каждый платёж — Оплата от покупателя. Контрагенты несут <ИНН> / <КПП> / <ОГРН>. Загружается через «Сервис → Универсальный обмен данными XML».moysklad) — не файл, а REST-синхронизация. Контрагенты пушатся в /entity/counterparty (дубли отсеиваются по ИНН), счета — в /entity/customerorder. В файл записывается JSON-сводка {ok,counterparties_created,invoices_pushed,errors}. Требует API-токен из «Профиль → Доступ → Авторизация по токену».elba_csv) — CSV с UTF-8 BOM и разделителем ;, заголовки Дата;Контрагент;ИНН;Документ;Сумма;Валюта;Назначение. Загружается через «Деньги → Загрузить операции».xlsx) — три листа («Счета», «Оплаты», «Контрагенты»), русскоязычные заголовки, авто-ширина колонок. Подходит для ручной правки и пересылки бухгалтеру.# Сохранить учётные данные МойСклад (Fernet-шифрование на сервере)
curl -X PUT -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"provider":"moysklad","enabled":true,
"config":{"api_token":"sk_xxxx","organization_id":"<uuid>"}}' \
https://zalplan.ru/api/v1/integrations/moysklad
# Получить статус интеграций (значения api_token замаскированы как ****1234)
curl -H "Authorization: Bearer $TOKEN" https://zalplan.ru/api/v1/integrations
# Запустить push в МойСклад (за последние 31 день)
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"scope":"all"}' \
https://zalplan.ru/api/v1/integrations/moysklad/sync
# → {"status":"completed","summary":{"counterparties_created":3,"invoices_pushed":18,"errors":[]}} api_token, секреты) шифруется Fernet-ключом, выведенным из jwt_signing_key через SHA-256, и хранится в БД как BYTEA.api_token: "****1234" (последние 4 символа). Полный токен не появляется ни в одном ответе API./app/data/uploads/acc{account_id}/exp_{slug}.{ext} и публикуются под https://zalplan.ru/api/v1/uploads/{account_id}/{filename} — ссылки трудно угадать (12-символьный crockford-base32 slug), но не подписаны: после загрузки храните файл локально или удаляйте экспорт.finance.manage; чтение — finance.view. Управление учётными данными интеграций — integrations.manage.