API para agentes da GetFacade
Projeto de fachada sobre MCP e HTTP. Cada projeto é elaborado para o país onde o edifício se encontra: materiais aplicáveis ali, produtos que os fabricantes ali vendem realmente e a composição técnica por trás da superfície. Um render mostra-o na foto da casa, o orçamento valoriza-o linha a linha e o álbum PDF documenta-o para a equipa que o executa. Cada caminho abaixo é um endpoint GetFacade já existente, o mesmo que as apps iOS, Android e web chamam. Uma chave de agente apenas restringe quem pode chamá-lo, mede o que gasta e para no seu teto.
- URL base
- https://api.getfacade.ai/api/v1
- Autenticação
- Bearer <agent key>
- Pacote
- @getfacade/mcp
- Ambiente de execução
- Node.js 20+
- Transporte
- stdio (MCP), HTTPS (REST)
- Especificação
- OpenAPI 3.1, v1.0.0
Início rápido
Emitir uma chave
app.getfacade.aiContaConfiguraçõesAPICriar chave
O valor é mostrado uma única vez e não pode ser recuperado, apenas substituído. O limite de gasto define-se na emissão e é aplicado em cada chamada paga.
Emitir uma chave de agenteRegistar o servidor MCP
Uma entrada na configuração do cliente e depois reiniciar o cliente. O Claude Desktop guarda-a em claude_desktop_config.json; qualquer outro cliente MCP aceita os mesmos três campos.
{ "mcpServers": { "getfacade": { "command": "npx", "args": ["-y", "@getfacade/mcp"], "env": { "GETFACADE_API_KEY": "your-key" } } } }Variáveis de ambiente Variável Obrigatória Valor por omissão GETFACADE_API_KEYSim —GETFACADE_API_BASE_URLNão https://api.getfacade.ai/api/v1Ou chamar a API HTTP diretamente
A mesma chave funciona como token Bearer. Pedidos e respostas são documentos JSON:API, onde id é um campo de primeiro nível e nunca fica dentro de attributes.
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"}}}'
Ferramentas
Vinte e uma ferramentas. O servidor MCP não guarda estado nem regras próprias: cada ferramenta é uma ou mais chamadas aos endpoints indicados ao lado, e cada mensagem que o agente repete é escrita pela API.
create_buildingcreate_building(name, goals?, construction_region?) -> { building_id, name }Cria um edifício. O nome é único dentro da conta e tem no máximo 50 caracteres; um duplicado é recusado com 422.
Chama
POST /projectsupload_photoupload_photo(building_id, file_path, wait_for_validation? = true) -> { view_id, validation: { status, reason? } }Regista uma vista, carrega os bytes para um URL pré-assinado, confirma-os e consulta até a foto ser aceite ou recusada. Largura, altura e md5 são calculados localmente; a proporção é deduzida pelo servidor.
Chama
POST /projects/{project}/angles → PUT (presigned) → POST /angles/{angle}/confirm → GET /angles/{angle}/validationstart_designassíncronastart_design(building_id, view_id, prompt?, style_ids?, colors?, brand_selections?, render_effort?, seed?) -> { design_id, job_id, status, seed }Projeta a fachada numa vista escolhida e mostra-a na foto: materiais aplicáveis no país do edifício, produtos ali realmente vendidos e a composição por trás da superfície. Cria um projeto, coloca o trabalho em fila e devolve o id do trabalho. A seed é opcional: se for omitida, o servidor gera-a e devolve-a.
Chama
POST /projects/{project}/concepts → POST /concepts/{concept}/angles/{conceptAngle}/rendersrefine_designassíncronarefine_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 }Revê por palavras um projeto terminado. A instrução aplica-se ao projeto terminado, pelo que fica tudo o que ela não menciona. Rever uma vista principal cria um projeto novo, de modo que o anterior nunca é substituído.
Chama
GET /renders/{render} → POST /concepts/{concept}/angles/{conceptAngle}/renders (mode: refine)get_jobget_job(job_id, kind: "render" | "album" | "estimate" = "render") -> { status, expected_seconds?, result_url?, error?, error_code? }Lê o estado de um render, de um orçamento ou de um álbum.
Chama
GET /renders/{render} · GET /estimates/{estimate}list_jobslist_jobs(kind?, limit? = 20) -> [{ job_id, kind, status, building_id, created_at }]Tarefas recentes da conta, as inacabadas primeiro.
Chama
GET /historylist_designslist_designs(building_id) -> [{ design_id, note, has_main_render, main_render_id, main_render_url, renders }]Designs de um edifício com os seus renders. É daqui que vêm os identificadores de render para o orçamento e o álbum.
Chama
GET /projects/{project}/conceptsorder_estimateassíncronaorder_estimate(design_id, render_ids, currency?, measurement_system?, special_requirements?) -> { job_id, status }Valoriza o projeto linha a linha, em materiais e mão de obra, aos preços que esses materiais têm no país do edifício. A moeda e o sistema de medida seguem por omissão esse país.
Chama
POST /projects/{project}/concepts/{concept}/estimatesorder_albumassíncronaorder_album(design_id, render_ids, language?, include_blueprints?, include_estimate?, requirements?) -> { job_id, status }Documenta o projeto para a equipa que o executa: os materiais, a composição da fachada, as notas de segurança e as normas que as sustentam. Exige um render principal concluído.
Chama
POST /concepts/{concept}/album/generateupscale_renderassíncronaupscale_render(render_id) -> { job_id, status }Amplia um render concluído. Custa créditos e é executado de forma assíncrona.
Chama
POST /renders/{render}/upscaleget_estimateget_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 }] }O orçamento em si: totais, os pressupostos por trás deles e cada linha com quantidade, unidade e preço. get_job informa o estado de um orçamento, nunca o seu conteúdo.
Chama
GET /estimates/{estimate}add_estimate_lineadd_estimate_line(estimate_id, section, name, quantity, unit_price, unit?, category?) -> { line_id }Adiciona uma linha ao orçamento. As unidades vêm do seu próprio sistema de medida.
Chama
POST /estimates/{estimate}/itemsupdate_estimate_lineupdate_estimate_line(estimate_id, line_id, name?, quantity?, unit_price?, unit?, category?, section?) -> { line_id }Altera uma linha do orçamento. Só os campos enviados mudam; o servidor recalcula os totais.
Chama
PATCH /estimates/{estimate}/items/{item}delete_estimate_linedelete_estimate_line(estimate_id, line_id) -> { deleted }Remove uma linha do orçamento.
Chama
DELETE /estimates/{estimate}/items/{item}delete_renderdelete_render(render_id) -> { deleted }Apaga um render. Apagar o render principal devolve o design ao estado de rascunho.
Chama
DELETE /renders/{render}delete_designdelete_design(building_id, design_id) -> { deleted }Apaga um design com os renders que contém.
Chama
DELETE /projects/{project}/concepts/{concept}delete_buildingdelete_building(building_id) -> { deleted }Apaga um edifício com tudo o que contém. Os créditos já gastos não são devolvidos.
Chama
DELETE /projects/{project}list_token_packageslist_token_packages() -> [{ package, tokens, price, currency }]Os pacotes que esta conta pode comprar, com preço e número de créditos.
Chama
GET /tokens/packagesbuy_tokensassíncronabuy_tokens(package) -> { status, transaction_id, tokens, checkout_url?, detail }Compra um pacote para a carteira desta chave. Exige uma chave emitida com a compra ativada e nunca ultrapassa o que a chave ainda pode gastar.
Chama
POST /tokens/purchaseget_balanceget_balance() -> { balance, scope: "api", spend_cap, spent, remaining, is_admissible }Carteira do agente, limite desta chave e se a próxima chamada paga será admitida.
Chama
GET /tokens/balancereport_problemreport_problem(message, category?, context?: { tool, endpoint, status_code, job_id, expected, actual }) -> { reference, message }Relata um defeito desta API: um campo descrito aqui que nunca chega, uma recusa cuja redação não indica o passo seguinte, um resultado que não corresponde ao pedido. Gratuito e aceite com saldo vazio; volta uma referência, não uma resposta.
Chama
POST /feedback
Autenticação e chaves
- A chave viaja como token Bearer no cabeçalho
Authorization. O servidor MCP lê-a deGETFACADE_API_KEYe não envia mais nada. - O valor é mostrado uma vez, na emissão, e apenas o seu hash é guardado. Rodar significa emitir uma chave nova e revogar a antiga.
- Cada chave tem um limite de gasto, aplicado no servidor antes de a chamada chegar a um controlador. Atingir o limite para essa chave, não a conta.
- As chaves não gerem chaves: isso é uma ação humana, e esses endpoints respondem 403 a uma chave de agente.
- A revogação tem efeito imediato. Chamadas com uma chave revogada respondem 401.
Trabalho assíncrono e consulta de estado
start_design,order_estimateeorder_albumdevolvem um identificador de tarefa e terminam. Um render demora minutos: consulteGET /renders/{render}ouGET /estimates/{estimate}até o estado ser terminal.- O fim da validação da foto é anunciado por um websocket que o agente não tem. Consulte
GET /angles/{angle}/validatione leiavalidation.is_in_progress; não deduza a terminalidade a partir da cadeia de estado. - Um render concluído e um álbum concluído ficam em URL públicos permanentes: sem assinatura e sem prazo. A ligação pode ser entregue diretamente a uma pessoa, como resposta a «mostra-me o resultado». Por não estar assinada, não pede autorização a ninguém: continua a funcionar para quem quer que a receba e não pode ser revogada.
GET /renders/{render}/downloadé outra coisa: um URL assinado que expira em minutos e traz um nome de ficheiro. Serve para guardar o ficheiro, não para o partilhar.
Limites de frequência por chave
Uma chave de agente tem contadores próprios, separados das sessões humanas da mesma conta, para que um agente em ciclo não consuma a quota de quem está ao ecrã. A recusa é barata: é decidida no middleware, antes de qualquer trabalho na base de dados.
| Âmbito | Por minuto | Por hora |
|---|---|---|
| Leituras e escritas comuns | 120 | 2000 |
| Consulta de estados e validação | 120 | 2000 |
| Encomendas de render, orçamento e álbum | 10 | 200 |
Idempotência
Uma chamada paga cria uma tarefa, e a cobrança segue a tarefa. Dar nome à chamada é o que permite que um reenvio devolva a mesma tarefa em vez de criar uma segunda.
Idempotency-Keyé obrigatório em qualquer chamada paga feita com uma chave de API: iniciar ou aperfeiçoar um design, ampliar um render, encomendar um orçamento ou um álbum, regenerar um orçamento. Sem ele a chamada responde 422IDEMPOTENCY_KEY_REQUIREDe nada entra na fila.- Qualquer valor de 8 a 191 caracteres, um por encomenda; o habitual é um UUID. Uma encomenda nova leva um valor novo: duas chamadas iguais com dois valores são dois designs.
- Repetir uma chamada com o mesmo valor e o mesmo corpo devolve o estado e o corpo originais, com
Idempotent-Replay: truena resposta. Nada entra na fila e nada é cobrado duas vezes. - O mesmo valor com um corpo diferente responde 422
IDEMPOTENCY_KEY_REUSED. Um reenvio que chega enquanto a primeira chamada ainda decorre responde 409IDEMPOTENCY_IN_PROGRESS: espere e envie a mesma chamada de novo. - Qualquer 4xx liberta o valor, portanto pode ser enviado de novo assim que a causa for corrigida. Os valores ficam em memória 24 horas, por conta.
@getfacade/mcpgera o valor e repete a chamada com ele sozinho, por isso não há nada a passar na chamada da ferramenta.
Erros
As falhas chegam como documentos de erro JSON:API. As respostas de validação do Laravel não têm forma JSON:API e trazem o texto em message.
| Estado | Código | Significado | Repetível |
|---|---|---|---|
401 | — | A chave falta, foi revogada ou expirou. | Não |
402 | AGENT_CREDITS_EXHAUSTED | A conta não tem mais créditos de âmbito api. | Não |
402 | AGENT_KEY_CAP_REACHED | Esta chave esgotou o seu limite. Emita outra chave ou aumente o limite. | Não |
403 | — | Este endpoint não está disponível para chaves de API. A API para agentes abrange edifícios, fotos, designs, renderizações, orçamentos, álbuns e a carteira de API. As definições de conta, de início de sessão e de pagamento são alteradas por uma pessoa com sessão iniciada na aplicação. | Não |
403 | AGENT_PURCHASE_NOT_ALLOWED | Esta chave foi emitida sem permissão para comprar créditos. | Não |
403 | AGENT_PURCHASE_EXCEEDS_CAP | A compra levaria a chave além do seu teto de gasto. | Não |
409 | IDEMPOTENCY_IN_PROGRESS | A primeira chamada com este Idempotency-Key ainda não respondeu. Espere e envie a mesma chamada de novo. | Sim |
422 | — | O pedido foi compreendido e recusado: nome de edifício duplicado, foto rejeitada, álbum encomendado antes de o render principal terminar. | Não |
422 | IDEMPOTENCY_KEY_REQUIRED | Uma chamada paga com chave de API e sem cabeçalho Idempotency-Key. Nada entrou na fila; envie-a de novo com o cabeçalho. | Não |
422 | IDEMPOTENCY_KEY_REUSED | Este Idempotency-Key foi usado noutro pedido. Use um valor novo para uma encomenda nova. | Não |
429 | — | O contador próprio desta chave está esgotado. Abrande, não repita em ciclo apertado. | Sim |
O texto legível é escrito pela API, na língua de quem chama. Mostre errors[].detail tal como está, em vez de compor a sua própria mensagem.
Faturação e admissão
- As chamadas pagas consomem créditos do escopo
api, e a admissão olha apenas para esse saldo: cada chave paga em créditos. - Um Pro Plan ativo completa a carteira
apiaté 1000 créditos uma vez por período de faturação. Além disso, os créditos são comprados. - Uma chave recarrega a própria carteira apenas se foi emitida com a compra ativada, e só até o que ainda pode gastar, por isso uma compra nunca levanta o teto de gasto.
- O âmbito
apié uma carteira à parte. Os créditos da aplicação, incluindo o nível gratuito, nunca são gastos por uma chave. - O gasto é contado por chave, pelo que o consumo de cada assistente é visível em separado.
- A verificação prévia é
GET /tokens/balance, campodata.attributes.agent.is_admissible. O bloco só aparece em chaves de agente e o indicador espelha exatamente o middleware de admissão. Leia-o em vez de comparar o saldo com o limite. - A admissão é decidida antes de qualquer trabalho entrar em fila, por isso uma chamada recusada não gasta nada.
Tokens de cor e de marca
start_design aceita duas listas independentes com dez entradas no máximo. A ordem carrega o papel 60/30/10: a primeira entrada é a cor dominante das paredes.
colors
| Token | Significado |
|---|---|
palette:1 | Um esquema selecionado da GetFacade, por identificador. |
#8A8F7D | Uma cor livre, seis dígitos hexadecimais. |
paint:412 | Uma amostra de fabricante, na forma de dois segmentos mantida por compatibilidade. |
brand_selections
| Token | Significado |
|---|---|
siding:brand:12 | Qualquer produto desse fabricante nessa categoria. |
siding:line:40@double-4-dutchlap | Uma linha, numa geometria. |
siding:product:88@double-4-dutchlap | Um produto, totalmente especificado. |
paint:brand:3 | Qualquer cor dessa marca de tintas. |
paint:product:412 | Uma amostra de tinta. |
A gramática é category:level:id[@value][.value]. A parte depois de @ leva slugs de valores de geometria, únicos dentro da sua categoria, pelo que o eixo a que pertencem é procurado em vez de escrito no token. Um token desconhecido é recusado com 422 e nunca ignorado em silêncio.
Sessão de ponta a ponta
Um edifício, uma foto, um design e depois os dois documentos. A instrução que o produz:
Cria um edifício chamado Maple Street 14, carrega ./front.jpg como a sua vista e inicia um design com paredes cinzento quente e molduras brancas. Encomenda o orçamento e o álbum para o resultado.
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)