# Настройки аккаунта

## Своё хранилище файлов, брендинг и логотип

Подключение своего хранилища в API не входит: там ключи доступа к S3 или
Яндекс.Диску. Читать состояние и проверять доступность можно.

---

### 92. Своё хранилище

*Метод в API с 2026-09-08.*

| Действие | HTTP |
|---|---|
| Состояние | `GET /workspace/{workspace_id}/storage` |
| Занятое место | `GET /workspace/{workspace_id}/storage/usage` |
| Скрытые файлы | `GET /workspace/{workspace_id}/storage/hidden-files` |
| Проверка доступности | `POST /workspace/{workspace_id}/storage/verify` |

Файлы сверх дисковой квоты не удаляются, а скрываются: их возвращают, освободив
место или оплатив хранение — это и отдают «скрытые файлы». Возвращают их
оплатой, поэтому список требует право **`payment`**.

Своё хранилище входит не во все тарифы. На запрещённом состояние и занятое место
отвечают `200` с `available: false` и без настроек: файлы лежат на стороне
платформы, настройкам взяться неоткуда.

Проверка отвечает `200` с полем `storage_ok`: недоступное хранилище — результат
проверки, а не сбой вызова. Запускает её только владелец аккаунта — проверка
меняет состояние подключения, а не просто читает его. Тариф без своего хранилища
— `403` с `blocked_by_tariff`, хранилище не подключено — `400` с кодом
`storage_not_connected`.

---

### 93. Брендинг и логотип

*Метод в API с 2026-09-08.*

Требуют право **`account_quiz_settings`**. Логотип и копирайт — настройки
**аккаунта**: действуют сразу на все его опросы, отчёты и PDF.

| Действие | HTTP |
|---|---|
| Логотип и копирайт | `POST /workspace/{workspace_id}/branding` с полями `logo_show`, `show_copyright` |
| Загрузить логотип | `POST /workspace/{workspace_id}/branding/logo`, файл в поле `logo` (jpeg, jpg, png, webp, до 5 МБ) |
| Удалить логотип | `DELETE /workspace/{workspace_id}/branding/logo` |

После загрузки адрес файла приходит полем `logo_url`.

Скрыть копирайт можно не на всех тарифах: на запрещённом запрос отклоняется с
`403` и `blocked_by_tariff`, а значение остаётся включённым — так же поступает
конструктор.

---

## Адрес, по которому открываются опросы

Требует право участника **`domain`**. Адресов четыре: служебный, поддомен
сервиса, дополнительный поддомен и свой домен. Поддомены и свой домен разрешены
разными тарифными опциями, поэтому в ответе на чтение приходит `allowed_by_tariff`.

---

### 103. Текущий адрес аккаунта

*Метод в API с 2026-09-08.*

**HTTP:** `GET /workspace/{workspace_id}/domain`

```json
{
    "status": true, "workspace_id": 760,
    "domain_type": "subdomain", "domain": null,
    "subdomain": "nashi-oprosy.webask.io", "other_subdomain": null,
    "service_url": "webask.io", "certificate_state": null,
    "allowed_by_tariff": {"domain": true, "subdomain": true}
}
```

`domain_type` — какой из адресов используется сейчас. `certificate_state` —
на чём стоит выпуск сертификата для своего домена: без `success` домен не
откроется по https. Записи адреса у аккаунта может не быть — тогда приходит
`domain: null`, опросы открываются по служебному адресу.

---

### 104. Поддомен

*Метод в API с 2026-09-08.*

**HTTP:** `POST /workspace/{workspace_id}/domain/subdomain`

| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| `subdomain` | string\|null | Да | Имя поддомена. Пустая строка или `null` снимает его. |
| `subdomain_kind` | string | Нет | `default` (по умолчанию) или `other_subdomain`. |

**Поле обязательно, даже когда поддомен снимают.** Отсутствие поля — `422`:
молчаливое стирание увело бы все опросы аккаунта на служебный адрес, о чём никто
не просил.

Если снять поддомен, которым аккаунт пользовался, адрес автоматически
возвращается на служебный (`domain_type: "default"`) — иначе он указывал бы в
пустоту.

Отказы приходят кодом: `subdomain_invalid` (недопустимые символы),
`subdomain_long`, `subdomain_forbidden` (имя занято служебными адресами сервиса),
`subdomain_taken` (занято другим аккаунтом). Тариф без поддоменов — `403` с
`blocked_by_tariff: true` и кодом `tariff_subdomain`.

---

### 105. Выбор адреса

*Метод в API с 2026-09-08.*

**HTTP:** `POST /workspace/{workspace_id}/domain/type`

| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| `domain_type` | string | Да | `default`, `subdomain`, `domain`, `other_subdomain`. |

Пустое имя поддомена при выборе достраивается значением по умолчанию — иначе
выбранный адрес указывал бы в никуда. Тариф без своего домена или без
поддоменов — `403` с `blocked_by_tariff: true` и кодом `tariff_type`.

---

## Тариф, приём оплаты и партнёрская программа

Оплатить тариф или вывести партнёрские средства через API нельзя: это перевод
денег, его делает человек сам. Здесь картина тарифа, настройка приёма оплаты в
опросе и сводка по программе.

---

### 106. Что входит в тариф аккаунта

*Метод в API с 2026-09-08.*

**HTTP:** `GET /workspace/{workspace_id}/tariff`

```json
{
    "status": true, "workspace_id": 760,
    "tariff": {"code": "premium", "is_free": false, "is_trial": false,
               "is_paid": true, "expires_at": "2026-10-09T00:00:00+03:00", "days_left": 30},
    "limits": {
        "quizzes": {"used": 12, "limit": 999999, "left": 999987, "can_create_more": true},
        "answers": {"left": 4200, "resets_on_day": 9, "incomplete_counted": false},
        "members": {"used": 3, "can_add_more": true},
        "themes": {"limit": 50},
        "disk": {"used_bytes": 1048576, "limit_bytes": 10737418240, "free_bytes": 10736369664,
                 "file_size_limit_bytes": 52428800},
        "letters": {"recipients_limit": 5000}
    },
    "features": {"mcp_access": true, "custom_domain": true, "quiz_payment": true,
                 "integrations": {"webhook": true, "telegram": true, "google_sheets": true}}
}
```

Признаки читаются поштучно и по отдельности не роняют остальные: опция может
отсутствовать в справочнике, и для картины тарифа это означает «возможности
нет», а не «ответа нет». Числовой лимит в таком случае приходит как `null` —
«неизвестно».

`features.quiz_payment` вынесен отдельно от `integrations`: приём оплаты — не
интеграция, у него своя тарифная опция.

---

### 107. Список тарифов с ценами

*Метод в API с 2026-09-08.*

**HTTP:** `GET /workspace/{workspace_id}/tariff/list`

---

### 108. Приём оплаты в опросе

*Метод в API с 2026-09-08.*

Требует тарифную опцию приёма оплаты и право участника **`settings`**.

| Действие | HTTP |
|---|---|
| Что настроено | `GET /quiz/{id}/payment` |
| Настроить | `POST /quiz/{id}/payment` |

| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| `yookassa_account_id` | integer | Да | Счёт продавца этого же аккаунта. |
| `payment_type` | string | Да | `fixed` — фиксированная сумма, `scoring` — по набранным баллам. |
| `fixed_amount` | numeric | При `fixed` — да | Иначе оплата была бы на ноль. |
| `contact_widget_id` | string | Нет | Вопрос, из которого берут контакт для чека. |

Счета продавца заводят в кабинете: там платёжные ключи. **Секретный токен счёта
наружу не отдаётся** — он равносилен доступу к приёму платежей; в ответе только
`shop_id`, название и настройки чека.

`payment_ready` показывает, есть ли у аккаунта хоть один счёт: отдельного
переключателя у оплаты нет, она работает при наличии счёта и разрешающего
тарифа, а показ вопроса задаётся его виджетом.

Счёт другого аккаунта — `400` с кодом `account_not_found`: чужой увёл бы платежи
респондентов на другого продавца.

---

### 109. Партнёрская программа

*Метод в API с 2026-09-08.*

**HTTP:** `GET /partner`

| Параметр | Тип | Описание |
|---|---|---|
| `date_from`, `date_to` | date | Период приглашений и начислений. |
| `source_id` | integer | Отбор по метке источника. |
| `limit` | integer | 1–200, по умолчанию 20. |
| `offset` | integer | Сдвиг по списку приглашённых. |

В ответе `registrations.total` — сколько приглашённых всего, `registrations.returned`
— сколько пришло на этой странице; остальные забираются через `offset`.

Возвращает баланс (доступный и ещё не подтверждённый к выплате), приглашённых с
числом их оплат, начисления за период и список меток. Всё считается по владельцу
программы: программа принадлежит человеку, а не аккаунту.

Вывод средств через API не делается.

---

### 110. Метки источников

*Метод в API с 2026-09-08.*

**HTTP:** `POST /partner/sources`

| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| `action` | string | Да | `create`, `rename`, `delete`. |
| `source_id` | integer | Кроме `create` | |
| `name` | string | При `rename` — да | 3–25 символов. При `create` необязательно: подставится имя по порядку. |

Имя по порядку при `create` берётся первым свободным: после удаления метки счёт
по количеству выдавал бы уже занятое имя.

Метка ищется среди своих — чужую не переименовать и не удалить (`400`,
`source_not_found`).

---

## Запись на встречи

Требует право участника **`settings`**: расписание — это настройка того, как
респондент записывается.

---

### 117. Расписания аккаунта

*Метод в API с 2026-09-08.*

| Действие | HTTP |
|---|---|
| Список | `GET /workspace/{workspace_id}/schedulers` |
| Создать | `POST /workspace/{workspace_id}/schedulers` |
| Изменить | `POST /workspace/{workspace_id}/schedulers/{schedulerId}` |
| Копия | `POST /workspace/{workspace_id}/schedulers/{schedulerId}/duplicate` |
| Удалить | `DELETE /workspace/{workspace_id}/schedulers/{schedulerId}` |

Основные поля: `name`, `color`, `schedule_mode` (`weekly` — недельная сетка,
`slots` — конкретные даты), `timezone`, `slot_duration` (5–1440 минут) и
`weekly` — список дней с интервалами. При создании они обязательны, при правке
нет: непереданное остаётся прежним.

Остальное: `description`, `is_active`, `slot_increment`, `buffer_before`,
`buffer_after`, `min_notice_minutes`, `max_advance` (`rolling` со `days`, `range`
с `from` и `to`, либо `infinite`), `daily_limit`, `seats_per_slot`,
`confirmation_mode` (`auto` или `manual`), `notify_respondent`,
`allow_respondent_cancel`, `notify_admin`, `admin_emails`, `location_type`
(`online`, `phone`, `address`, `custom`), `location_value`, `manual_slots`,
`overrides` — исключения из сетки на диапазон дат.

Расписание другого аккаунта не найдётся — `400` с кодом `scheduler_not_found`.

---

### 118. Журнал брони

*Метод в API с 2026-09-08.*

**HTTP:** `GET /workspace/{workspace_id}/bookings`

| Параметр | Тип | Обязательное | Описание |
|---|---|---|---|
| `from` | date | Да | Начало периода. |
| `to` | date | Да | Конец, позже начала. |

Период обязателен: журнал за всё время — это выборка по всем встречам аккаунта,
она нужна редко и стоит дорого.

---

### 119. Правка брони

*Метод в API с 2026-09-08.*

**HTTP:** `POST /workspace/{workspace_id}/bookings/{bookingId}`

| Поле | Тип | Описание |
|---|---|---|
| `status` | string | `pending`, `confirmed`, `cancelled`, `no_show`. |
| `comment` | string | До 5000 символов. |
| `start_utc`, `end_utc` | date | Перенос встречи. |

**Время задаётся парой.** Одна граница или пустое значение — `422`: перенос идёт
только при обеих, и такой запрос выглядел бы успешным, а встреча уехала бы на
момент вызова или осталась на месте.

Смена статуса и перенос отправляют респонденту уведомление, если оно включено в
расписании.

---

### 120. Блокировки времени

*Метод в API с 2026-09-08.*

Способ закрыть занятый интервал, не ломая всю сетку: отпуск, праздник, разовая
встреча вне опроса.

| Действие | HTTP |
|---|---|
| Закрыть время | `POST /workspace/{workspace_id}/booking-blocks` |
| Снять | `DELETE /workspace/{workspace_id}/booking-blocks/{blockId}` |

Поля: `scheduler_id`, `start_utc`, `end_utc` (позже начала), необязательный
`comment`. Время приводится к UTC на нашей стороне — присылать можно и со
смещением. В журнале видно, кто закрыл время: блокировки ставят и руками, и по
ключу.

---