Справочник ручек
Все ответы — JSON. Удачный ответ несёт "ok": true, отказ — "ok": false и объект error (см. errors.md).
Авторизация на всех ручках, кроме GET /v1/health и GET /r/{token}:
Authorization: Bearer pk_…
GET /v1/health
Без ключа. Ставьте в мониторинг.
{ "ok": true, "version": 1, "model": "deepseek/deepseek-v4-pro-0813" }
model — какой моделью сервис сейчас работает. Меняется без предупреждения: это наш выбор по цене за сошедшуюся просьбу, а не часть договора.
GET /v1/usage
Остаток монет учётной записи, на которую выдан ключ.
{ "ok": true, "usage": {
"plan": "seat", "status": "active",
"coins_left": 940, "coins_used": 60, "coins_included": 1000,
"extra_coins": 0,
"period_end": "2026-10-01T00:00:00.000Z",
"cabinet": "https://teril.example.ru/cabinet" } }
status — trial, active, past_due или canceled. Монеты добирает мебельная компания в своём кабинете; вы можете только увидеть остаток.
POST /v1/conversations
Завести разговор — или получить уже заведённый.
{
"external_id": "lead-7781",
"order_id": "кухня-иванов",
"webhook_url": "https://ваш-сервис/hooks/teril",
"meta": { "источник": "сайт", "менеджер": 12 }
}
| Поле | Обяз. | Что это |
|---|---|---|
external_id | нет | Ваш id лида. По нему разговор находится повторно |
order_id | нет | Имя заказа. Не задано — lead-<external_id> |
webhook_url | нет | Куда слать исход. Не задан — общий адрес партнёра |
meta | нет | Любой ваш JSON. Мы его храним и не читаем |
201 — завели новый. 200 — вернули тот же по external_id.
{ "ok": true, "conversation": {
"id": "3f2b…", "external_id": "lead-7781", "order_id": "кухня-иванов",
"turn": 0, "running_turn": null, "has_project": false,
"expires_at": "2026-10-18T09:11:00.000Z" } }
running_turn — id идущего хода или null. has_project — собрано ли уже что-нибудь.
GET /v1/conversations/{id}
Тот же объект conversation. Разговор чужого партнёра не находится вовсе (404).
DELETE /v1/conversations/{id}
204, без тела. Удаляются и переписка, и картинки, и история ходов. Необратимо. Делайте, когда лид закрыт: сам разговор и так умрёт через 30 дней после последней просьбы.
POST /v1/conversations/{id}/turns
Просьба клиента. Отвечает 202.
{
"text": "Кухня 3 метра вдоль окна, мойка под окном",
"images": [{ "mime": "image/png", "b64": "iVBORw0KGgo…", "name": "план.png" }]
}
| Поле | Обяз. | Что это |
|---|---|---|
text | да | Слова клиента как есть, по-русски |
images | нет | До двух картинок, каждая ≤ 5 МБ. mime: image/png, image/jpeg, image/webp, image/gif. name можно слать — он принимается и никуда не идёт |
Заголовок Idempotency-Key (рекомендуется): повтор с тем же ключом вернёт тот же ход и не купит второго вопроса модели. Без него повтор — это второй ход и вторые деньги.
{ "ok": true, "turn": { "id": "b41c…", "state": "queued" } }
409 conflict_busy — по этому разговору уже идёт ход. Дождитесь исхода или остановите его.
GET /v1/turns/{id}
Состояние и исход хода. Запасной путь к тому, что приходит вебхуком.
{ "ok": true, "turn": {
"id": "b41c…",
"state": "done",
"status": "built",
"result": { "…": "см. turns.md" } } }
Поля status, result и error появляются по мере того, как им есть что сказать: у идущего хода их нет вовсе, у неудачного вместо result приходит error. Не полагайтесь на то, что поле present-but-null — его просто нет.
state: queued → running → done | stopped | failed.
Опрашивать имеет смысл раз в 5–10 секунд и не дольше десяти минут.
POST /v1/turns/{id}/stop
Оборвать идущий ход: клиент передумал, дописал, ушёл.
{ "ok": true, "turn_id": "b41c…", "state": "running" }
409 — ход уже кончился либо идёт на другом узле сервиса и остановить его нечем. Второй случай честно говорит об этом текстом: мы не притворяемся, что остановили.
Остановленный ход не списывает монет.
GET /v1/conversations/{id}/project
Документ заказа — то, из чего собирается мебель. Его читает проектировщик и наши же инструменты; вам он нужен, если вы храните состояние у себя или показываете состав словами.
{ "ok": true, "project": {
"id": "кухня-иванов", "kind": "kitchen", "ceiling": 2700,
"room": { "walls": [ { "id": "задняя", "turn": "right", "length": 3000 } ] },
"runs": [ { "id": "задняя-низ-1", "wall": "задняя", "tier": "base",
"countertop": true, "plinth": true,
"nodes": [ { "id": "мойка", "at": "module", "code": "sink-straight", "w": 800 } ] } ],
"items": [], "obstacles": [] } }
404, пока в разговоре ничего не собрано.
Документ большой и подробный; разбирать его целиком не нужно. Практически полезны runs[].nodes[] — состав по порядку, у каждого узла id, code (модуль каталога) и w (ширина в миллиметрах).
GET /v1/conversations/{id}/bom
Расход материалов. Подробно — bom.md.
{ "ok": true, "order_id": "кухня-иванов", "turn": 3, "bom": {
"sheets": [ { "material": "ldsp16", "name": "ЛДСП 16 мм", "parts": 78, "area_mm2": 15253450 } ],
"linear": [],
"edges": [ { "edge": "pvc1carcass", "name": "Кромка корпуса", "length_mm": 70164 } ],
"pieces": [ { "article": "LEG-ADJ-100", "name": "Опора регулируемая 100", "qty": 22 } ] } }
GET /v1/conversations/{id}/render
Объёмный вид заказа.
Вид принадлежит РАЗГОВОРУ, а не ходу. Он доступен после ЛЮБОГО хода, пока в разговоре что-то собрано, и просить его можно когда угодно — хоть перед первым ходом дня. Статус последнего хода на это не влияет вовсе: question и rejected ничего не разобрали, и картинка после них ровно та же, что была. Не собрано ещё ничего — 404 not_found.
| Параметр | Значения | По умолчанию |
|---|---|---|
format | svg, html | svg |
view | front, iso | front |
carcass | выкрас из палитры | clear |
facade | выкрас из палитры | clear |
link | 1 | нет |
Без link — тело: image/svg+xml или text/html.
С link=1 — подписанная ссылка вместо тела:
{ "ok": true, "url": "https://teril.example.ru/r/eyJ…", "expires_at": "2026-09-19T…" }
carcass и facade красят вид СПЛОШНЫМИ ПАНЕЛЯМИ — порознь корпус и фасад, потому что порознь их и называют («белый верх, дуб низ»). Выкрасы:
clear, white, milk, sand, oak, walnut, wenge, grey, graphite, black, olive, blue.
clear — не цвет, а его отсутствие: вид остаётся чертежом, залитым бумагой и обведённым краской, и это же по умолчанию. Неизвестное имя — 400 invalid_request со списком: опечатка в цвете иначе уехала бы клиенту картинкой не того тона, и узнали бы вы об этом от него.
Цвет здесь ВРЁТ НАРОЧНО и ничего не решает: декор выбирают по выкрасу у поставщика, а не по экрану телефона, — поэтому имена родовые, а не артикулы. Геометрия при этом та же самая, из которой выйдут детали.
На странице (format=html) палитра идёт строкой кнопок под видом: клиент перебирает цвета сам, не ходя к вам за новой ссылкой. Выбранное в запросе — то, что он увидит первым; там, где скриптов нет вовсе (предпросмотр в мессенджере), останется оно же. Подписанная ссылка несёт выкрасы в себе: параметром их клиенту не дать — подпись покрывает только тело.
view=iso показывает и маркеры техники (место под холодильник, трубу, котёл); front — только мебель. Клиенту обычно нужен front.
format=png отвечает 501 png_unsupported: ради картинки, которая получается из SVG, пришлось бы держать браузер в рантайме. Нужен растр — сконвертируйте SVG у себя.
GET /r/{token}
Страница вида по подписанной ссылке. Без ключа — её открывает ваш клиент.
Страница самодостаточна: чертёж внутри неё, наружу она не ходит ни за чем. Живёт сутки; просроченная и подделанная отвечают одинаково — 404.
Содержимое адресуется парой «разговор + номер хода», поэтому ссылка неизменна: после следующего хода запросите новую.
POST /v1/conversations/{id}/handoff
Передать заказ на рабочее место мебельной компании.
{ "seat": "Цех на Ленина", "order_id": "кухня-иванов-2" }
seat — id места или его название из кабинета компании. order_id — имя, под которым заказ ляжет на место; не задано — имя заказа разговора.
{ "ok": true, "handoff": {
"seat_id": 5, "seat_title": "Цех на Ленина",
"order_id": "кухня-иванов-2", "turn": 3 } }
403 — такого места у этой учётной записи нет либо оно отозвано. 400 — в разговоре ещё ничего не собрано.
Что эта ручка делает и чего не делает — handoff.md.
Эта страница разметкой: /docs/api/reference.md · вся документация одним файлом: /llms-full.txt