Агентный API GetFacade

Проектирование фасада через MCP и HTTP. Каждый дизайн прорабатывается под страну, где стоит дом: материалы, применимые там, продукты производителей, которые там действительно продаются, и технический пирог отделки за поверхностью. Рендер показывает это на фотографии дома, смета считает построчно, а PDF-альбом документирует для бригады, которая будет строить. Каждый путь ниже это существующий эндпоинт GetFacade, тот же самый, что вызывают приложения iOS, Android и веб. Агентный ключ лишь сужает круг вызывающих, считает расход и останавливается на своём потолке.

Базовый URL
https://api.getfacade.ai/api/v1
Аутентификация
Bearer <agent key>
Пакет
@getfacade/mcp
Среда выполнения
Node.js 20+
Транспорт
stdio (MCP), HTTPS (REST)
Спецификация
OpenAPI 3.1, v1.0.0

Быстрый старт

  1. Выпустите ключ

    app.getfacade.aiАккаунтНастройкиAPIСоздать ключ

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

    Выпустить агентный ключ
  2. Подключите MCP-сервер

    Одна запись в конфигурации клиента, затем перезапуск клиента. Claude Desktop хранит её в claude_desktop_config.json; любой другой MCP-клиент принимает те же три поля.

    claude_desktop_config.json
    {
      "mcpServers": {
        "getfacade": {
          "command": "npx",
          "args": ["-y", "@getfacade/mcp"],
          "env": { "GETFACADE_API_KEY": "your-key" }
        }
      }
    }
    Переменные окружения
    ПеременнаяОбязательнаЗначение по умолчанию
    GETFACADE_API_KEYДа
    GETFACADE_API_BASE_URLНетhttps://api.getfacade.ai/api/v1
  3. Или вызывайте HTTP-API напрямую

    Тот же ключ работает как токен Bearer. Запросы и ответы — документы JSON:API, где id это поле верхнего уровня и никогда не лежит внутри attributes.

    shell
    curl -X POST https://api.getfacade.ai/api/v1/projects \
      -H "Authorization: Bearer $GETFACADE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"data":{"type":"project","attributes":{"name":"Maple Street 14"}}}'

Инструменты

Двадцать один инструмент. MCP-сервер не хранит состояния и не имеет своих правил: каждый инструмент — это один или несколько вызовов перечисленных рядом эндпоинтов, а любое сообщение, которое повторяет агент, написано API.

  • create_building
    create_building(name, goals?, construction_region?)
      -> { building_id, name }

    Создаёт объект. Имя уникально в пределах аккаунта и не длиннее 50 символов; дубликат отклоняется с кодом 422.

    ВызываетPOST /projects

  • upload_photo
    upload_photo(building_id, file_path, wait_for_validation? = true)
      -> { view_id, validation: { status, reason? } }

    Регистрирует ракурс, загружает байты по подписанному URL, подтверждает их и опрашивает статус, пока фотография не будет принята или отклонена. Ширина, высота и md5 считаются локально; соотношение сторон выводит сервер.

    ВызываетPOST /projects/{project}/angles → PUT (presigned) → POST /angles/{angle}/confirm → GET /angles/{angle}/validation

  • start_designасинхронный
    start_design(building_id, view_id, prompt?, style_ids?, colors?,
                 brand_selections?, render_effort?, seed?)
      -> { design_id, job_id, status, seed }

    Проектирует фасад на выбранном ракурсе и показывает его на фотографии: материалы, применимые в стране объекта, продукты, которые там действительно продаются, и пирог отделки за поверхностью. Создаёт дизайн, ставит работу в очередь и возвращает идентификатор задания. Seed необязателен: если его не передать, сервер сгенерирует его сам и вернёт.

    ВызываетPOST /projects/{project}/concepts → POST /concepts/{concept}/angles/{conceptAngle}/renders

  • refine_designасинхронный
    refine_design(render_id | design_id + building_id, instruction,
                  style_ids?, colors?, brand_selections?, render_effort?, seed?)
      -> { design_id, job_id, status, parent_render_id, seed }

    Правит словами готовый дизайн. Инструкция применяется к готовому дизайну, поэтому всё, о чём в ней не сказано, сохраняется. Правка главного ракурса создаёт новый дизайн, так что прежний никогда не затирается.

    ВызываетGET /renders/{render} → POST /concepts/{concept}/angles/{conceptAngle}/renders (mode: refine)

  • get_job
    get_job(job_id, kind: "render" | "album" | "estimate" = "render")
      -> { status, expected_seconds?, result_url?, error?, error_code? }

    Читает состояние одного рендера, сметы или альбома.

    ВызываетGET /renders/{render} · GET /estimates/{estimate}

  • list_jobs
    list_jobs(kind?, limit? = 20)
      -> [{ job_id, kind, status, building_id, created_at }]

    Недавние задачи по всему аккаунту, незавершённые наверху.

    ВызываетGET /history

  • list_designs
    list_designs(building_id)
      -> [{ design_id, note, has_main_render, main_render_id,
           main_render_url, renders }]

    Дизайны объекта вместе с их рендерами. Отсюда берутся идентификаторы рендеров для сметы и альбома.

    ВызываетGET /projects/{project}/concepts

  • order_estimateасинхронный
    order_estimate(design_id, render_ids, currency?,
                   measurement_system?, special_requirements?)
      -> { job_id, status }

    Считает дизайн построчно, по материалам и работам, по ценам указанных материалов в стране объекта. Валюта и система мер по умолчанию берутся из этой страны.

    ВызываетPOST /projects/{project}/concepts/{concept}/estimates

  • order_albumасинхронный
    order_album(design_id, render_ids, language?, include_blueprints?,
                include_estimate?, requirements?)
      -> { job_id, status }

    Документирует дизайн для бригады, которая будет строить: материалы, пирог отделки, замечания по безопасности и нормы, на которых они стоят. Требует завершённого главного рендера.

    ВызываетPOST /concepts/{concept}/album/generate

  • upscale_renderасинхронный
    upscale_render(render_id)
      -> { job_id, status }

    Увеличивает завершённый рендер. Стоит токенов и выполняется асинхронно.

    ВызываетPOST /renders/{render}/upscale

  • get_estimate
    get_estimate(estimate_id)
      -> { status, currency, facade_area, materials_total, labor_total,
           grand_total, notes, lines: [{ line_id, section, name, quantity,
           unit, unit_price, line_total }] }

    Сама смета: итоги, допущения под ними и каждая строка с количеством, единицей и ценой. get_job сообщает статус сметы, но не её содержимое.

    ВызываетGET /estimates/{estimate}

  • add_estimate_line
    add_estimate_line(estimate_id, section, name, quantity,
                      unit_price, unit?, category?)
      -> { line_id }

    Добавляет строку в смету. Единицы измерения берутся из системы мер самой сметы.

    ВызываетPOST /estimates/{estimate}/items

  • update_estimate_line
    update_estimate_line(estimate_id, line_id, name?, quantity?,
                         unit_price?, unit?, category?, section?)
      -> { line_id }

    Меняет строку сметы. Затрагиваются только переданные поля, итоги пересчитывает сервер.

    ВызываетPATCH /estimates/{estimate}/items/{item}

  • delete_estimate_line
    delete_estimate_line(estimate_id, line_id)
      -> { deleted }

    Удаляет строку сметы.

    ВызываетDELETE /estimates/{estimate}/items/{item}

  • delete_render
    delete_render(render_id)
      -> { deleted }

    Удаляет рендер. Удаление главного рендера возвращает его дизайн в черновик.

    ВызываетDELETE /renders/{render}

  • delete_design
    delete_design(building_id, design_id)
      -> { deleted }

    Удаляет дизайн вместе с его рендерами.

    ВызываетDELETE /projects/{project}/concepts/{concept}

  • delete_building
    delete_building(building_id)
      -> { deleted }

    Удаляет объект со всем содержимым. Уже потраченные токены не возвращаются.

    ВызываетDELETE /projects/{project}

  • list_token_packages
    list_token_packages()
      -> [{ package, tokens, price, currency }]

    Пакеты, доступные аккаунту, с ценой и числом токенов.

    ВызываетGET /tokens/packages

  • buy_tokensасинхронный
    buy_tokens(package)
      -> { status, transaction_id, tokens, checkout_url?, detail }

    Покупает пакет в кошелёк этого ключа. Нужен ключ, выпущенный с разрешением на покупку, и покупка не выходит за то, что ключу ещё можно потратить.

    ВызываетPOST /tokens/purchase

  • get_balance
    get_balance()
      -> { balance, scope: "api", spend_cap, spent, remaining, is_admissible }

    Агентный кошелёк, потолок этого ключа и признак того, будет ли принят следующий платный вызов.

    ВызываетGET /tokens/balance

  • report_problem
    report_problem(message, category?,
                   context?: { tool, endpoint, status_code, job_id,
                               expected, actual })
      -> { reference, message }

    Сообщает о неисправности этого API: поле, описанное здесь и не приходящее в ответе, отказ, из формулировки которого не следует следующий шаг, результат, не соответствующий запросу. Бесплатно и принимается при пустом кошельке; в ответ приходит номер обращения, а не ответ.

    ВызываетPOST /feedback

Аутентификация и ключи

  • Ключ передаётся как токен Bearer в заголовке Authorization. MCP-сервер читает его из GETFACADE_API_KEY и не отправляет ничего другого.
  • Значение показывается один раз, при выпуске, и хранится только его хеш. Ротация это выпуск нового ключа и отзыв старого.
  • У каждого ключа есть потолок трат, который проверяется на сервере до того, как вызов дойдёт до контроллера. Достигнутый потолок останавливает ключ, а не аккаунт.
  • Ключи не управляют ключами: работа с ключами это действие человека, и агентному ключу она отвечает 403.
  • Отзыв действует немедленно. Вызовы с отозванным ключом получают 401.

Асинхронная работа и опрос статуса

  • start_design, order_estimate и order_album возвращают идентификатор задачи и завершаются. Рендер занимает минуты: опрашивайте GET /renders/{render} или GET /estimates/{estimate}, пока состояние не станет терминальным.
  • О завершении валидации фотографии сообщает вебсокет, которого у агента нет. Опрашивайте GET /angles/{angle}/validation и читайте validation.is_in_progress; не вычисляйте терминальность по строке статуса самостоятельно.
  • Готовый рендер и готовый альбом лежат по постоянным публичным адресам: без подписи и без срока. Такую ссылку можно отдать человеку напрямую, это и есть ответ на «покажи результат». Поскольку она не подписана, она никого не спрашивает: работает у любого, кто её получил, и отозвать её нельзя.
  • GET /renders/{render}/download это другое: подписанный URL, который истекает за минуты и несёт имя файла. Он нужен, чтобы сохранить файл, а не чтобы поделиться им.

Лимиты частоты на ключ

У агентного ключа свои корзины лимитов, отдельные от человеческих сессий того же аккаунта, поэтому зациклившийся агент не съест лимит того, кто сидит за экраном. Отказ обходится дёшево: он выносится в middleware, до любой работы с базой.

ОбластьВ минутуВ час
Чтение и обычная запись1202000
Опрос статусов и валидации1202000
Заказ рендеров, смет и альбомов10200

Идемпотентность

Платный вызов создаёт задачу, и списание идёт за задачей. Имя вызова — это то, что позволяет повтору вернуть ту же задачу вместо создания второй.

  • Idempotency-Key обязателен для каждого платного вызова с API-ключом: создание и доработка дизайна, увеличение рендера, заказ сметы или альбома, перегенерация сметы. Без него вызов отвечает 422 IDEMPOTENCY_KEY_REQUIRED, и в очередь ничего не ставится.
  • Любое значение длиной от 8 до 191 символа, по одному на заказ; обычно берут UUID. Новый заказ — новое значение: два одинаковых вызова под двумя значениями это два дизайна.
  • Повтор вызова с тем же значением и тем же телом возвращает исходный статус и тело, с заголовком Idempotent-Replay: true. В очередь ничего не ставится и второй раз ничего не списывается.
  • То же значение с другим телом отвечает 422 IDEMPOTENCY_KEY_REUSED. Повтор, пришедший пока первый вызов ещё выполняется, отвечает 409 IDEMPOTENCY_IN_PROGRESS: подождите и отправьте тот же вызов снова.
  • Любой ответ 4xx освобождает значение, так что его можно отправить снова, когда причина устранена. Значения помнятся 24 часа, в пределах аккаунта.
  • @getfacade/mcp сам создаёт значение и сам повторяет вызов под ним, так что в вызове инструмента передавать нечего.

Ошибки

Отказы приходят документами ошибок JSON:API. Ответы валидации Laravel не имеют формы JSON:API и несут текст в поле message.

СтатусКодЗначениеПовтор
401Ключ отсутствует, отозван или истёк.Нет
402AGENT_CREDITS_EXHAUSTEDНа аккаунте не осталось кредитов скоупа api.Нет
402AGENT_KEY_CAP_REACHEDКлюч израсходовал свой потолок. Выпустите другой ключ или поднимите потолок.Нет
403Этот эндпоинт недоступен по API-ключу. Агентный API охватывает объекты, фотографии, дизайны, рендеры, сметы, альбомы и кошелёк API. Настройки аккаунта, входа и оплаты меняет человек, вошедший в приложение.Нет
403AGENT_PURCHASE_NOT_ALLOWEDКлюч выпущен без права покупать токены.Нет
403AGENT_PURCHASE_EXCEEDS_CAPПокупка вывела бы ключ за его потолок расхода.Нет
409IDEMPOTENCY_IN_PROGRESSПервый вызов с этим Idempotency-Key ещё не ответил. Подождите и отправьте тот же вызов снова.Да
422Запрос понят и отклонён: повторяющееся имя объекта, отклонённая фотография, альбом, заказанный до завершения главного рендера.Нет
422IDEMPOTENCY_KEY_REQUIREDПлатный вызов с API-ключом без заголовка Idempotency-Key. В очередь ничего не поставлено, отправьте вызов снова с заголовком.Нет
422IDEMPOTENCY_KEY_REUSEDЭтот Idempotency-Key уже использован для другого запроса. Для нового заказа возьмите новое значение.Нет
429Исчерпана собственная корзина лимитов этого ключа. Сделайте паузу, не повторяйте в плотном цикле.Да

Человекочитаемый текст пишет API, на языке вызывающего. Показывайте errors[].detail как есть, не составляя собственное сообщение.

Оплата и допуск

  • Платные вызовы идут из кредитов скоупа api, и допуск смотрит только на этот баланс: каждый ключ платит кредитами.
  • Активный Pro Plan раз в биллинг-период доводит кошелёк api до 1000 кредитов. Дальше кредиты покупаются.
  • Ключ пополняет свой кошелёк, только если выпущен с разрешением на покупку, и не больше того, что ему ещё можно потратить, поэтому покупка не поднимает потолок расхода.
  • Скоуп api это отдельный кошелёк. Кредиты приложения, включая бесплатный тариф, ключом не расходуются никогда.
  • Расход считается по каждому ключу, поэтому потребление каждого ассистента видно отдельно.
  • Предполётная проверка это GET /tokens/balance, поле data.attributes.agent.is_admissible. Блок присутствует только у агентских ключей, а флаг в точности повторяет middleware допуска. Читайте его вместо сравнения баланса с потолком.
  • Допуск решается до постановки работы в очередь, поэтому отклонённый вызов ничего не расходует.

Цветовые и брендовые токены

start_design принимает два независимых списка, не более десяти записей в каждом. Порядок несёт роль 60/30/10: первая запись это доминирующий цвет стен.

colors

ТокенЗначение
palette:1Готовая схема GetFacade, по идентификатору.
#8A8F7DПроизвольный цвет, шесть шестнадцатеричных знаков.
paint:412Чип производителя в двухсегментной форме, оставленной для совместимости.

brand_selections

ТокенЗначение
siding:brand:12Любой товар этого производителя в этой категории.
siding:line:40@double-4-dutchlapОдна линейка на одной геометрии.
siding:product:88@double-4-dutchlapОдин товар, полностью заданный.
paint:brand:3Любой цвет этого бренда красок.
paint:product:412Один чип краски.

Грамматика: category:level:id[@value][.value]. Часть после @ несёт слаги значений геометрии, которые уникальны внутри своей категории, поэтому ось выясняется поиском, а не пишется в токене. Неизвестный токен отклоняется с кодом 422 и никогда не игнорируется молча.

Сквозная сессия

Один объект, одна фотография, один дизайн и затем два документа. Запрос, который это порождает:

Создай объект «Кленовая 14», загрузи ./front.jpg как его ракурс и запусти дизайн с тёплыми серыми стенами и белой отделкой. Закажи смету и альбом по результату.
Сессия MCP
create_building(name: "Maple Street 14")
  -> { building_id: "0f8c…" }

upload_photo(building_id: "0f8c…", file_path: "./front.jpg")
  -> { view_id: "41ab…", validation: { status: "approved" } }

start_design(building_id: "0f8c…", view_id: "41ab…",
             colors: ["#8A8F7D", "#F2F0EB"],
             brand_selections: ["siding:line:40@double-4-dutchlap"])
  -> { design_id: "7d21…", job_id: "b933…", status: "queued" }

get_job(job_id: "b933…")
  -> { status: "completed", result_url: "https://…" }

refine_design(render_id: "b933…",
              instruction: "put a canopy over the front door")
  -> { design_id: "9e44…", job_id: "c07f…", status: "queued" }

order_estimate(design_id: "9e44…", render_ids: ["c07f…"])
order_album(design_id: "9e44…", render_ids: ["c07f…"], include_estimate: true)

Материалы