API-ul GetFacade pentru agenți
Proiectare de fațadă prin MCP și HTTP. Fiecare proiect este elaborat pentru țara în care se află clădirea: materiale aplicabile acolo, produse pe care producătorii chiar le vând acolo și stratificația tehnică din spatele suprafeței. Un render îl arată pe fotografia casei, devizul îl evaluează rând cu rând, iar albumul PDF îl documentează pentru echipa care va construi. Fiecare cale de mai jos este un endpoint GetFacade existent, același pe care îl apelează aplicațiile iOS, Android și web. O cheie de agent doar restrânge cine îl poate apela, măsoară cheltuiala și se oprește la plafonul ei.
- URL de bază
- https://api.getfacade.ai/api/v1
- Autentificare
- Bearer <agent key>
- Pachet
- @getfacade/mcp
- Mediu de execuție
- Node.js 20+
- Transport
- stdio (MCP), HTTPS (REST)
- Specificație
- OpenAPI 3.1, v1.0.0
Pornire rapidă
Emite o cheie
app.getfacade.aiContSetăriAPICreează cheia
Valoarea se afișează o singură dată și nu poate fi recuperată, doar înlocuită. Plafonul de cheltuieli se stabilește la emitere și se aplică la fiecare apel plătit.
Emite o cheie de agentÎnregistrează serverul MCP
O intrare în configurația clientului, apoi repornirea clientului. Claude Desktop o păstrează în claude_desktop_config.json; orice alt client MCP acceptă aceleași trei câmpuri.
{ "mcpServers": { "getfacade": { "command": "npx", "args": ["-y", "@getfacade/mcp"], "env": { "GETFACADE_API_KEY": "your-key" } } } }Variabile de mediu Variabilă Obligatorie Valoare implicită GETFACADE_API_KEYDa —GETFACADE_API_BASE_URLNu https://api.getfacade.ai/api/v1Sau apelează direct API-ul HTTP
Aceeași cheie funcționează ca jeton Bearer. Cererile și răspunsurile sunt documente JSON:API, în care id este un câmp de prim nivel și nu se află niciodată în 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"}}}'
Instrumente
Douăzeci și unu de instrumente. Serverul MCP nu păstrează stare și nu are reguli proprii: fiecare instrument înseamnă unul sau mai multe apeluri către endpointurile indicate alături, iar fiecare mesaj pe care agentul îl repetă este scris de API.
create_buildingcreate_building(name, goals?, construction_region?) -> { building_id, name }Creează o clădire. Numele este unic în cadrul contului și are cel mult 50 de caractere; un duplicat este respins cu 422.
Apelează
POST /projectsupload_photoupload_photo(building_id, file_path, wait_for_validation? = true) -> { view_id, validation: { status, reason? } }Înregistrează o vedere, încarcă octeții la un URL presemnat, îi confirmă și interoghează până când fotografia este acceptată sau respinsă. Lățimea, înălțimea și md5 se calculează local; raportul de aspect îl deduce serverul.
Apelează
POST /projects/{project}/angles → PUT (presigned) → POST /angles/{angle}/confirm → GET /angles/{angle}/validationstart_designasincronstart_design(building_id, view_id, prompt?, style_ids?, colors?, brand_selections?, render_effort?, seed?) -> { design_id, job_id, status, seed }Proiectează fațada pe un unghi ales și o arată pe fotografie: materiale aplicabile în țara clădirii, produse vândute acolo în realitate și stratificația din spatele suprafeței. Creează un proiect, pune lucrarea la coadă și returnează id-ul sarcinii. Seed-ul este opțional: dacă lipsește, serverul îl generează și îl returnează.
Apelează
POST /projects/{project}/concepts → POST /concepts/{concept}/angles/{conceptAngle}/rendersrefine_designasincronrefine_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 }Revizuiește în cuvinte un proiect finalizat. Instrucțiunea se aplică proiectului finalizat, deci tot ce nu menționează rămâne. Revizuirea unghiului principal creează un proiect nou, așa că cel anterior nu este niciodată suprascris.
Apelează
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? }Citește starea unei randări, a unui deviz sau a unui album.
Apelează
GET /renders/{render} · GET /estimates/{estimate}list_jobslist_jobs(kind?, limit? = 20) -> [{ job_id, kind, status, building_id, created_at }]Sarcinile recente din cont, cele neterminate primele.
Apelează
GET /historylist_designslist_designs(building_id) -> [{ design_id, note, has_main_render, main_render_id, main_render_url, renders }]Designurile unei clădiri împreună cu randările lor. De aici provin identificatorii de randare pentru deviz și album.
Apelează
GET /projects/{project}/conceptsorder_estimateasincronorder_estimate(design_id, render_ids, currency?, measurement_system?, special_requirements?) -> { job_id, status }Evaluează proiectul rând cu rând, în materiale și manoperă, la prețurile materialelor indicate în țara clădirii. Moneda și sistemul de măsură urmează implicit acea țară.
Apelează
POST /projects/{project}/concepts/{concept}/estimatesorder_albumasincronorder_album(design_id, render_ids, language?, include_blueprints?, include_estimate?, requirements?) -> { job_id, status }Documentează proiectul pentru echipa care va construi: materialele, stratificația fațadei, notele de siguranță și normele care le susțin. Necesită un render principal finalizat.
Apelează
POST /concepts/{concept}/album/generateupscale_renderasincronupscale_render(render_id) -> { job_id, status }Mărește un randare finalizată. Costă credite și rulează asincron.
Apelează
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 }] }Devizul în sine: totalurile, ipotezele din spatele lor și fiecare linie cu cantitate, unitate și preț. get_job raportează starea unui deviz, niciodată conținutul lui.
Apelează
GET /estimates/{estimate}add_estimate_lineadd_estimate_line(estimate_id, section, name, quantity, unit_price, unit?, category?) -> { line_id }Adaugă o linie la deviz. Unitățile vin din propriul lui sistem de măsură.
Apelează
POST /estimates/{estimate}/itemsupdate_estimate_lineupdate_estimate_line(estimate_id, line_id, name?, quantity?, unit_price?, unit?, category?, section?) -> { line_id }Modifică o linie a devizului. Se schimbă doar câmpurile trimise; totalurile le recalculează serverul.
Apelează
PATCH /estimates/{estimate}/items/{item}delete_estimate_linedelete_estimate_line(estimate_id, line_id) -> { deleted }Șterge o linie din deviz.
Apelează
DELETE /estimates/{estimate}/items/{item}delete_renderdelete_render(render_id) -> { deleted }Șterge o randare. Ștergerea randării principale readuce designul în ciornă.
Apelează
DELETE /renders/{render}delete_designdelete_design(building_id, design_id) -> { deleted }Șterge un design împreună cu randările lui.
Apelează
DELETE /projects/{project}/concepts/{concept}delete_buildingdelete_building(building_id) -> { deleted }Șterge o clădire cu tot ce conține. Creditele deja cheltuite nu se restituie.
Apelează
DELETE /projects/{project}list_token_packageslist_token_packages() -> [{ package, tokens, price, currency }]Pachetele pe care acest cont le poate cumpăra, cu preț și număr de credite.
Apelează
GET /tokens/packagesbuy_tokensasincronbuy_tokens(package) -> { status, transaction_id, tokens, checkout_url?, detail }Cumpără un pachet pentru portofelul acestei chei. Cere o cheie emisă cu achiziția activată și nu depășește niciodată ce mai poate cheltui cheia.
Apelează
POST /tokens/purchaseget_balanceget_balance() -> { balance, scope: "api", spend_cap, spent, remaining, is_admissible }Portofelul agentului, plafonul acestei chei și dacă următorul apel plătit va fi admis.
Apelează
GET /tokens/balancereport_problemreport_problem(message, category?, context?: { tool, endpoint, status_code, job_id, expected, actual }) -> { reference, message }Raportează un defect al acestui API: un câmp descris aici care nu ajunge niciodată, un refuz din a cărui formulare nu reiese pasul următor, un rezultat care nu corespunde cererii. Gratuit și acceptat cu portofelul gol; înapoi vine o referință, nu un răspuns.
Apelează
POST /feedback
Autentificare și chei
- Cheia călătorește ca jeton Bearer în antetul
Authorization. Serverul MCP o citește dinGETFACADE_API_KEYși nu trimite nimic altceva. - Valoarea se afișează o dată, la emitere, iar din ea se păstrează doar hash-ul. Rotația înseamnă emiterea unei chei noi și revocarea celei vechi.
- Fiecare cheie poartă un plafon de cheltuieli, aplicat pe server înainte ca apelul să ajungă la un controler. Atingerea plafonului oprește acea cheie, nu contul.
- Cheile nu administrează chei: este o acțiune umană, iar acele endpointuri răspund 403 unei chei de agent.
- Revocarea are efect imediat. Apelurile cu o cheie revocată răspund 401.
Lucru asincron și interogare
start_design,order_estimateșiorder_albumreturnează un identificator de sarcină și se încheie. O randare durează minute: interogheazăGET /renders/{render}sauGET /estimates/{estimate}până când starea este finală.- Încheierea validării fotografiei este anunțată printr-un websocket pe care agentul nu îl are. Interoghează
GET /angles/{angle}/validationși citeștevalidation.is_in_progress; nu deduce singur finalitatea din șirul de stare. - Un render finalizat și un album finalizat stau la adrese publice permanente: fără semnătură și fără expirare. Linkul poate fi dat direct unei persoane, ca răspuns la „arată-mi rezultatul”. Nefiind semnat, nu cere voie nimănui: funcționează pentru oricine îl primește și nu poate fi retras.
GET /renders/{render}/downloadeste altceva: un URL semnat care expiră în câteva minute și poartă un nume de fișier. Servește la salvarea fișierului, nu la partajarea lui.
Limite de frecvență pe cheie
O cheie de agent are propriile cote, separate de sesiunile umane ale aceluiași cont, astfel încât un agent blocat într-o buclă să nu consume cota persoanei din fața ecranului. Refuzul este ieftin: se decide în middleware, înainte de orice lucru cu baza de date.
| Domeniu | Pe minut | Pe oră |
|---|---|---|
| Citiri și scrieri obișnuite | 120 | 2000 |
| Interogarea stărilor și a validării | 120 | 2000 |
| Comenzi de randare, deviz și album | 10 | 200 |
Idempotență
Un apel plătit creează o lucrare, iar plata urmează lucrarea. Denumirea apelului este ceea ce permite ca o repetare să returneze aceeași lucrare în loc să creeze a doua.
Idempotency-Keyeste obligatoriu la orice apel plătit făcut cu o cheie API: pornirea sau rafinarea unui design, mărirea unui render, comandarea unui deviz sau a unui album, regenerarea unui deviz. Fără el, apelul răspunde 422IDEMPOTENCY_KEY_REQUIREDși nu se pune nimic la coadă.- Orice valoare de 8 până la 191 de caractere, una pentru fiecare comandă; de obicei un UUID. O comandă nouă primește o valoare nouă: două apeluri identice sub două valori sunt două designuri.
- Repetarea unui apel cu aceeași valoare și același corp returnează statusul și corpul original, cu
Idempotent-Replay: trueîn răspuns. Nu se pune nimic la coadă și nu se taxează de două ori. - Aceeași valoare cu un corp diferit răspunde 422
IDEMPOTENCY_KEY_REUSED. O repetare care sosește cât timp primul apel încă rulează răspunde 409IDEMPOTENCY_IN_PROGRESS: așteaptă și trimite din nou același apel. - Orice 4xx eliberează valoarea, așa că aceeași poate fi trimisă din nou după ce cauza este rezolvată. Valorile sunt ținute minte 24 de ore, pe cont.
@getfacade/mcpgenerează valoarea și reia apelul sub ea de unul singur, deci apelul de instrument nu trebuie să transmită nimic.
Erori
Eșecurile sosesc ca documente de eroare JSON:API. Răspunsurile de validare Laravel nu au formă JSON:API și își poartă textul în message.
| Stare | Cod | Semnificație | Se poate relua |
|---|---|---|---|
401 | — | Cheia lipsește, a fost revocată sau a expirat. | Nu |
402 | AGENT_CREDITS_EXHAUSTED | Contul nu mai are credite în domeniul api. | Nu |
402 | AGENT_KEY_CAP_REACHED | Această cheie și-a epuizat plafonul. Emite altă cheie sau ridică plafonul. | Nu |
403 | — | Acest endpoint nu este disponibil pentru cheile API. API-ul pentru agenți acoperă clădiri, fotografii, designuri, randări, devize, albume și portofelul API. Setările de cont, de autentificare și de plată sunt modificate de o persoană conectată în aplicație. | Nu |
403 | AGENT_PURCHASE_NOT_ALLOWED | Această cheie a fost emisă fără dreptul de a cumpăra credite. | Nu |
403 | AGENT_PURCHASE_EXCEEDS_CAP | Achiziția ar duce cheia peste plafonul ei de cheltuială. | Nu |
409 | IDEMPOTENCY_IN_PROGRESS | Primul apel cu acest Idempotency-Key nu a răspuns încă. Așteaptă și trimite din nou același apel. | Da |
422 | — | Cererea a fost înțeleasă și respinsă: nume de clădire duplicat, fotografie respinsă, album comandat înainte de finalizarea randării principale. | Nu |
422 | IDEMPOTENCY_KEY_REQUIRED | Un apel plătit cu cheie API, fără antetul Idempotency-Key. Nu s-a pus nimic la coadă; trimite-l din nou cu antetul. | Nu |
422 | IDEMPOTENCY_KEY_REUSED | Acest Idempotency-Key a fost folosit pentru altă cerere. Folosește o valoare nouă pentru o comandă nouă. | Nu |
429 | — | Cota proprie a acestei chei s-a epuizat. Așteaptă, nu relua într-o buclă strânsă. | Da |
Textul lizibil este scris de API, în limba apelantului. Afișează errors[].detail ca atare, în loc să compui propriul mesaj.
Facturare și admitere
- Apelurile plătite consumă credite din domeniul
api, iar admiterea se uită doar la acest sold: fiecare cheie plătește în credite. - Un Pro Plan activ completează portofelul
apipână la 1.000 de credite o dată pe perioadă de facturare. Peste asta, creditele se cumpără. - O cheie își alimentează portofelul doar dacă a fost emisă cu achiziția activată și cel mult cu cât mai poate cheltui, așa că o achiziție nu ridică niciodată plafonul de cheltuială.
- Domeniul
apieste un portofel separat. Creditele aplicației, inclusiv nivelul gratuit, nu sunt cheltuite niciodată de o cheie. - Cheltuiala se contorizează pe cheie, așa că se vede separat consumul fiecărui asistent.
- Verificarea prealabilă este
GET /tokens/balance, câmpuldata.attributes.agent.is_admissible. Blocul apare doar la cheile de agent, iar indicatorul reflectă exact middleware-ul de admitere. Citește-l în loc să compari singur soldul cu plafonul. - Admiterea se decide înainte ca lucrul să intre la coadă, așa că un apel respins nu costă nimic.
Jetoane de culoare și de marcă
start_design acceptă două liste independente, de cel mult zece intrări fiecare. Ordinea poartă rolul 60/30/10: prima intrare este culoarea dominantă a pereților.
colors
| Jeton | Semnificație |
|---|---|
palette:1 | O schemă GetFacade pregătită, după identificator. |
#8A8F7D | O culoare liberă, șase cifre hexazecimale. |
paint:412 | Un eșantion de producător, în forma cu două segmente păstrată pentru compatibilitate. |
brand_selections
| Jeton | Semnificație |
|---|---|
siding:brand:12 | Orice produs al acelui producător din acea categorie. |
siding:line:40@double-4-dutchlap | O gamă, pe o geometrie. |
siding:product:88@double-4-dutchlap | Un produs, complet precizat. |
paint:brand:3 | Orice culoare a acelei mărci de vopsea. |
paint:product:412 | Un singur eșantion de vopsea. |
Gramatica este category:level:id[@value][.value]. Partea de după @ poartă slug-uri de valori de geometrie, unice în cadrul categoriei lor, astfel încât axa căreia îi aparțin se caută, nu se scrie în jeton. Un jeton necunoscut este respins cu 422 și nu este ignorat niciodată în tăcere.
Sesiune completă
O clădire, o fotografie, un design și apoi cele două documente. Instrucțiunea care le produce:
Creează o clădire numită Maple Street 14, încarcă ./front.jpg ca vedere a ei și pornește un design cu pereți gri cald și ancadramente albe. Comandă devizul și albumul pentru rezultat.
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)