API per agenti di GetFacade
Progettazione della facciata su MCP e HTTP. Ogni progetto è elaborato per il paese in cui sorge l'edificio: materiali applicabili lì, prodotti che i produttori vi vendono davvero e la stratigrafia tecnica dietro la superficie. Un render lo mostra sulla foto della casa, il preventivo lo quantifica voce per voce e l'album PDF lo documenta per l'impresa che lo realizza. Ogni percorso qui sotto è un endpoint GetFacade già esistente, lo stesso che chiamano le app iOS, Android e web. Una chiave agent restringe soltanto chi può chiamarlo, misura la spesa e si ferma al proprio tetto.
- URL di base
- https://api.getfacade.ai/api/v1
- Autenticazione
- Bearer <agent key>
- Pacchetto
- @getfacade/mcp
- Ambiente di esecuzione
- Node.js 20+
- Trasporto
- stdio (MCP), HTTPS (REST)
- Specifica
- OpenAPI 3.1, v1.0.0
Avvio rapido
Emetti una chiave
app.getfacade.aiAccountImpostazioniAPICrea chiave
Il valore compare una sola volta e non è recuperabile, solo sostituibile. Il tetto di spesa si imposta all'emissione e vale su ogni chiamata a pagamento.
Emetti una chiave agenteRegistra il server MCP
Una voce nella configurazione del client, poi riavvia il client. Claude Desktop la tiene in claude_desktop_config.json; ogni altro client MCP accetta gli stessi tre campi.
{ "mcpServers": { "getfacade": { "command": "npx", "args": ["-y", "@getfacade/mcp"], "env": { "GETFACADE_API_KEY": "your-key" } } } }Variabili d'ambiente Variabile Obbligatoria Valore predefinito GETFACADE_API_KEYSì —GETFACADE_API_BASE_URLNo https://api.getfacade.ai/api/v1Oppure chiama direttamente l'API HTTP
La stessa chiave funziona come token Bearer. Richieste e risposte sono documenti JSON:API, dove id è un campo di primo livello e non sta mai dentro 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"}}}'
Strumenti
Ventuno strumenti. Il server MCP non conserva stato né regole proprie: ogni strumento è una o più chiamate agli endpoint indicati accanto, e ogni messaggio che l'agente riporta è scritto dall'API.
create_buildingcreate_building(name, goals?, construction_region?) -> { building_id, name }Crea un edificio. Il nome è unico all'interno dell'account e lungo al massimo 50 caratteri; un duplicato viene rifiutato con 422.
Chiama
POST /projectsupload_photoupload_photo(building_id, file_path, wait_for_validation? = true) -> { view_id, validation: { status, reason? } }Registra una vista, carica i byte su un URL prefirmato, li conferma e interroga finché la foto non è accettata o rifiutata. Larghezza, altezza e md5 si calcolano in locale; le proporzioni le deduce il server.
Chiama
POST /projects/{project}/angles → PUT (presigned) → POST /angles/{angle}/confirm → GET /angles/{angle}/validationstart_designasincronostart_design(building_id, view_id, prompt?, style_ids?, colors?, brand_selections?, render_effort?, seed?) -> { design_id, job_id, status, seed }Progetta la facciata su una vista scelta e la mostra sulla foto: materiali applicabili nel paese dell'edificio, prodotti davvero in vendita lì e la stratigrafia dietro la superficie. Crea un progetto, mette il lavoro in coda e restituisce l'id del lavoro. Il seed è facoltativo: se omesso, il server lo genera e lo restituisce.
Chiama
POST /projects/{project}/concepts → POST /concepts/{concept}/angles/{conceptAngle}/rendersrefine_designasincronorefine_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 }Rivede a parole un progetto terminato. L'istruzione si applica al progetto finito, quindi resta tutto ciò che non viene nominato. Rivedere una vista principale crea un nuovo progetto, così il precedente non viene mai sovrascritto.
Chiama
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? }Legge lo stato di un render, di un preventivo o di un album.
Chiama
GET /renders/{render} · GET /estimates/{estimate}list_jobslist_jobs(kind?, limit? = 20) -> [{ job_id, kind, status, building_id, created_at }]Lavori recenti dell'account, prima quelli non conclusi.
Chiama
GET /historylist_designslist_designs(building_id) -> [{ design_id, note, has_main_render, main_render_id, main_render_url, renders }]Design di un edificio con i loro render. Da qui arrivano gli identificativi dei render per preventivo e album.
Chiama
GET /projects/{project}/conceptsorder_estimateasincronoorder_estimate(design_id, render_ids, currency?, measurement_system?, special_requirements?) -> { job_id, status }Quantifica il progetto voce per voce, in materiali e manodopera, ai prezzi che quei materiali hanno nel paese dell'edificio. Valuta e sistema di misura seguono per impostazione predefinita quel paese.
Chiama
POST /projects/{project}/concepts/{concept}/estimatesorder_albumasincronoorder_album(design_id, render_ids, language?, include_blueprints?, include_estimate?, requirements?) -> { job_id, status }Documenta il progetto per l'impresa che lo realizza: i materiali, la stratigrafia della facciata, le note di sicurezza e le norme che le sostengono. Richiede un render principale completato.
Chiama
POST /concepts/{concept}/album/generateupscale_renderasincronoupscale_render(render_id) -> { job_id, status }Ingrandisce un render completato. Costa crediti e viene eseguito in modo asincrono.
Chiama
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 }] }Il preventivo stesso: totali, le ipotesi alla base e ogni riga con quantità, unità e prezzo. get_job riporta lo stato di un preventivo, mai il suo contenuto.
Chiama
GET /estimates/{estimate}add_estimate_lineadd_estimate_line(estimate_id, section, name, quantity, unit_price, unit?, category?) -> { line_id }Aggiunge una riga al preventivo. Le unità provengono dal suo sistema di misura.
Chiama
POST /estimates/{estimate}/itemsupdate_estimate_lineupdate_estimate_line(estimate_id, line_id, name?, quantity?, unit_price?, unit?, category?, section?) -> { line_id }Modifica una riga del preventivo. Cambiano solo i campi passati; i totali li ricalcola il server.
Chiama
PATCH /estimates/{estimate}/items/{item}delete_estimate_linedelete_estimate_line(estimate_id, line_id) -> { deleted }Rimuove una riga dal preventivo.
Chiama
DELETE /estimates/{estimate}/items/{item}delete_renderdelete_render(render_id) -> { deleted }Elimina un render. Eliminando il render principale, il suo progetto torna in bozza.
Chiama
DELETE /renders/{render}delete_designdelete_design(building_id, design_id) -> { deleted }Elimina un progetto con i render che contiene.
Chiama
DELETE /projects/{project}/concepts/{concept}delete_buildingdelete_building(building_id) -> { deleted }Elimina un edificio con tutto il suo contenuto. I crediti già spesi non vengono rimborsati.
Chiama
DELETE /projects/{project}list_token_packageslist_token_packages() -> [{ package, tokens, price, currency }]I pacchetti acquistabili da questo account, con prezzo e numero di crediti.
Chiama
GET /tokens/packagesbuy_tokensasincronobuy_tokens(package) -> { status, transaction_id, tokens, checkout_url?, detail }Acquista un pacchetto per il portafoglio di questa chiave. Richiede una chiave emessa con l'acquisto abilitato e non va mai oltre ciò che la chiave può ancora spendere.
Chiama
POST /tokens/purchaseget_balanceget_balance() -> { balance, scope: "api", spend_cap, spent, remaining, is_admissible }Portafoglio dell'agente, tetto di questa chiave e se la prossima chiamata a pagamento sarà ammessa.
Chiama
GET /tokens/balancereport_problemreport_problem(message, category?, context?: { tool, endpoint, status_code, job_id, expected, actual }) -> { reference, message }Segnala un difetto di questa API: un campo descritto qui che non arriva mai, un rifiuto dalla cui formulazione non segue alcun passo, un risultato che non corrisponde alla richiesta. Gratuito e accettato anche a saldo zero; torna un riferimento, non una risposta.
Chiama
POST /feedback
Autenticazione e chiavi
- La chiave viaggia come token Bearer nell'intestazione
Authorization. Il server MCP la legge daGETFACADE_API_KEYe non invia altro. - Il valore compare una volta, all'emissione, e viene conservato solo il suo hash. Ruotare significa emettere una nuova chiave e revocare la vecchia.
- Ogni chiave porta un tetto di spesa, applicato lato server prima che la chiamata raggiunga un controller. Il tetto raggiunto ferma quella chiave, non l'account.
- Le chiavi non gestiscono chiavi: è un'azione umana, e quegli endpoint rispondono 403 a una chiave agente.
- La revoca ha effetto immediato. Le chiamate con una chiave revocata rispondono 401.
Lavoro asincrono e polling
start_design,order_estimateeorder_albumrestituiscono un identificativo di lavoro e finiscono lì. Un render richiede minuti: interrogaGET /renders/{render}oGET /estimates/{estimate}finché lo stato non è terminale.- La fine della validazione della foto è annunciata da un websocket che l'agente non ha. Interroga
GET /angles/{angle}/validatione leggivalidation.is_in_progress; non dedurre da solo la terminalità dalla stringa di stato. - Un render completato e un album completato stanno a URL pubblici permanenti: senza firma e senza scadenza. Il link si può consegnare direttamente a una persona, come risposta a «mostrami il risultato». Non essendo firmato non chiede il permesso a nessuno: continua a funzionare per chiunque lo riceva e non può essere revocato.
GET /renders/{render}/downloadè un'altra cosa: un URL firmato che scade in pochi minuti e porta un nome file. Serve a salvare il file, non a condividerlo.
Limiti di frequenza per chiave
Una chiave agente ha contatori propri, separati dalle sessioni umane dello stesso account, così un agente in loop non consuma la quota di chi sta davanti allo schermo. Il rifiuto costa poco: viene deciso nel middleware, prima di qualsiasi lavoro sul database.
| Ambito | Al minuto | All'ora |
|---|---|---|
| Letture e scritture ordinarie | 120 | 2000 |
| Polling di stato e validazione | 120 | 2000 |
| Ordini di render, preventivo e album | 10 | 200 |
Idempotenza
Una chiamata a pagamento crea un lavoro, e l'addebito segue il lavoro. È il nome della chiamata a permettere che un rinvio restituisca lo stesso lavoro invece di crearne un secondo.
Idempotency-Keyè obbligatoria su ogni chiamata a pagamento fatta con una chiave API: avviare o rifinire un design, ingrandire un render, ordinare un preventivo o un album, rigenerare un preventivo. Senza di essa la chiamata risponde 422IDEMPOTENCY_KEY_REQUIREDe non viene messo in coda nulla.- Un valore qualsiasi da 8 a 191 caratteri, uno per ordine; di solito si usa un UUID. Un nuovo ordine prende un valore nuovo: due chiamate identiche con due valori sono due design.
- Ripetere una chiamata con lo stesso valore e lo stesso corpo restituisce stato e corpo originali, con
Idempotent-Replay: truenella risposta. Non viene messo in coda nulla e non si paga due volte. - Lo stesso valore con un corpo diverso risponde 422
IDEMPOTENCY_KEY_REUSED. Un rinvio che arriva mentre la prima chiamata è ancora in corso risponde 409IDEMPOTENCY_IN_PROGRESS: attendi e invia di nuovo la stessa chiamata. - Qualsiasi 4xx libera il valore, che può essere inviato di nuovo una volta risolta la causa. I valori restano in memoria 24 ore, per account.
@getfacade/mcpgenera il valore e ripete la chiamata da solo, quindi nella chiamata allo strumento non va passato nulla.
Errori
Gli errori arrivano come documenti JSON:API. Le risposte di validazione di Laravel non hanno forma JSON:API e portano il testo in message.
| Stato | Codice | Significato | Ripetibile |
|---|---|---|---|
401 | — | La chiave manca, è revocata o è scaduta. | No |
402 | AGENT_CREDITS_EXHAUSTED | L'account non ha più crediti di ambito api. | No |
402 | AGENT_KEY_CAP_REACHED | Questa chiave ha esaurito il suo tetto. Emetti un'altra chiave o alza il tetto. | No |
403 | — | Questo endpoint non è disponibile per le chiavi API. L'API per agenti copre edifici, foto, design, render, preventivi, album e il portafoglio API. Le impostazioni di account, accesso e pagamento le modifica una persona che ha effettuato l'accesso nell'app. | No |
403 | AGENT_PURCHASE_NOT_ALLOWED | Questa chiave è stata emessa senza il permesso di acquistare crediti. | No |
403 | AGENT_PURCHASE_EXCEEDS_CAP | L'acquisto porterebbe la chiave oltre il suo tetto di spesa. | No |
409 | IDEMPOTENCY_IN_PROGRESS | La prima chiamata con questo Idempotency-Key non ha ancora risposto. Attendi e invia di nuovo la stessa chiamata. | Sì |
422 | — | La richiesta è stata compresa e rifiutata: nome edificio duplicato, foto respinta, album ordinato prima del completamento del render principale. | No |
422 | IDEMPOTENCY_KEY_REQUIRED | Una chiamata a pagamento con chiave API e senza header Idempotency-Key. Non è stato messo in coda nulla; rinviala con l'header. | No |
422 | IDEMPOTENCY_KEY_REUSED | Questo Idempotency-Key è stato usato per un'altra richiesta. Usa un valore nuovo per un nuovo ordine. | No |
429 | — | Il contatore proprio di questa chiave è esaurito. Rallenta, non ritentare in un ciclo stretto. | Sì |
Il testo leggibile lo scrive l'API, nella lingua del chiamante. Mostra errors[].detail così com'è invece di comporre un messaggio tuo.
Fatturazione e ammissione
- Le chiamate a pagamento attingono ai crediti dell'ambito
apie l'ammissione guarda solo quel saldo: ogni chiave paga in crediti. - Un Pro Plan attivo riporta il portafoglio
apia 1.000 crediti una volta per periodo di fatturazione. Oltre, i crediti si acquistano. - Una chiave ricarica il proprio portafoglio solo se è stata emessa con l'acquisto abilitato, e solo fino a quanto può ancora spendere: un acquisto non alza mai il tetto di spesa.
- L'ambito
apiè un portafoglio a sé. I crediti dell'app, piano gratuito incluso, non vengono mai spesi da una chiave. - La spesa è contata per chiave, quindi il consumo di ciascun assistente è visibile separatamente.
- Il controllo preliminare è
GET /tokens/balance, campodata.attributes.agent.is_admissible. Il blocco compare solo per le chiavi agente e il flag rispecchia esattamente il middleware di ammissione. Leggilo invece di confrontare saldo e tetto. - L'ammissione è decisa prima che il lavoro entri in coda, quindi una chiamata rifiutata non spende nulla.
Token di colore e di marca
start_design accetta due elenchi indipendenti di al massimo dieci voci. L'ordine porta il ruolo 60/30/10: la prima voce è il colore dominante delle pareti.
colors
| Token | Significato |
|---|---|
palette:1 | Uno schema curato da GetFacade, per identificativo. |
#8A8F7D | Un colore libero, sei cifre esadecimali. |
paint:412 | Un campione di produttore, nella forma a due segmenti mantenuta per compatibilità. |
brand_selections
| Token | Significato |
|---|---|
siding:brand:12 | Qualsiasi prodotto di quel produttore in quella categoria. |
siding:line:40@double-4-dutchlap | Una linea, su una geometria. |
siding:product:88@double-4-dutchlap | Un prodotto, completamente specificato. |
paint:brand:3 | Qualsiasi colore di quella marca di pitture. |
paint:product:412 | Un singolo campione di pittura. |
La grammatica è category:level:id[@value][.value]. La parte dopo @ porta slug di valori di geometria, unici nella loro categoria, così l'asse a cui appartengono si ricava con una ricerca invece di essere scritto nel token. Un token sconosciuto viene rifiutato con 422 e mai ignorato in silenzio.
Sessione completa
Un edificio, una foto, un design e poi i due documenti. L'istruzione che lo produce:
Crea un edificio chiamato Maple Street 14, carica ./front.jpg come sua vista e avvia un design con pareti grigio caldo e cornici bianche. Ordina il preventivo e l'album per il risultato.
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)