1. Kif jaħdem
Ma tospita xejn u ma tinstalla xejn. Il-fluss kollu għandu erba’ passi:
- Il-konfiguratur jaħdem fuq is-sit tiegħek f’iframe (
<script>wieħed). Il-klijent jiddisinja fi 3D kċina, gwardarobba, ħajt tas-salott, għamara jew kamra tal-banju. - Jibgħat talba (isem, email, telefown, rekwiżiti, ritratti). Fittle jaħżinha taħt iċ-ċavetta tiegħek.
- Tirċeviha bi tliet modi: bl-email (b’link 3D), b’webhook (POST iffirmat lis-server tiegħek – minnufih) u permezz tal-API (kull ħin wara, bir-ritratti).
- Tqassamha lill-mastrudaxxi tiegħek – kull talba għandha link ta’ previżjoni 3D li tinfetaħ mingħajr kont.
X’għandek bżonn: kont ta’ mastrudaxxa fuq app.getfittle.com b’ħlas għal kull talba (il-portali u l-ktajjen ma jħallsux pakkett iżda kull talba li tasal) – wara l-bidla, fil-kont tidher il-kard Integrazzjoni bil-webhook u ċ-ċavetta API. Iktbilna u naqilbuk.
403 integration_off. Id-dħul tal-konfiguratur waħdu (pass 1) jaħdem ukoll b’pakkett normali.Pass 1 – Dħul tal-konfiguratur (5 minuti)
L-aktar mod sempliċi: script wieħed fuq il-paġna fejn għandu jkun il-konfiguratur. Iċ-ċavetta FITT-… issibha fil-kont; hija marbuta mad-dominji tiegħek (issettjahom fil-kont), għalhekk ma tistax tintuża ħażin minn sit ieħor.
<div id="configurator-mt"></div>
<script src="https://app.getfittle.com/embed.js"
data-key="FITT-XXXX-XXXX-XXXX"
data-product="kitchen"
data-target="#configurator"></script>
data-product: kitchen · wardrobe · living · furniture · bathroom. Il-lingwa tal-konfiguratur hija l-lingwa awtomatika tal-kont tiegħek; il-klijent jista’ jibdilha fl-intestatura.
Portal b’utenti li daħlu: minflok data-target sejjaħ HNL.mount() – timla minn qabel l-isem, l-email u t-telefown fil-formola tat-talba u tkun taf meta ntbagħtet talba:
<script src="https://app.getfittle.com/embed.js" data-key="FITT-XXXX-XXXX-XXXX"></script>
<script>
HNL.mount('#configurator', {
product: 'wardrobe', // kitchen | wardrobe | living | furniture | bathroom
fill: true, // fill the parent's height (default 820 px)
prefill: { // logged-in user of your portal (editable in the form)
name: 'Jane Doe', email: 'jane@example.com', phone: '+421 900 000 000'
}
});
HNL.on('ready', function (e) { console.log('configurator running:', e.product); });
HNL.on('quote', function (e) {
// request sent – e.id is the request id (DM…), e.design the design JSON
location.href = '/thank-you?request=' + encodeURIComponent(e.id);
});
</script>
Avvenimenti HNL.on: ready {product}, quote {id, product, design}, design (tweġiba għal HNL.getDesign(cb)), height {height}. HNL.prefill({…}) jista’ jissejjaħ ukoll aktar tard.
quote jasal fil-browser tal-klijent – tajjeb għal paġna ta’ ringrazzjament jew ridirezzjoni. Għall-ipproċessar fuq is-server uża l-webhook (pass 2), li huwa ffirmat.Pass 2 – Webhook (20 minuta)
Fil-kont → Integrazzjoni issettja l-URL tal-webhook (https) u sigriet (kwalunkwe sekwenza twila, eż. 32 karattru każwali). Ma’ kull talba nibagħtu:
POST https://portal.example.com/furniconf/webhook
Content-Type: application/json
X-Furniconf-Event: quote.created
X-Furniconf-Delivery: WMTZ7Q2K9A1B
X-Furniconf-Timestamp: 1789300000
X-Furniconf-Signature: sha256=3f1a…9c
{
"id": "WMTZ7Q2K9A1B",
"event": "quote.created",
"createdAt": "2026-09-16T09:12:33.000Z",
"shopKey": "FITT-XXXX-XXXX-XXXX",
"data": {
"design": {
"id": "DMU3VCTB63BB7",
"product": "kitchen",
"status": "new",
"createdAt": "2026-09-16T09:12:33.000Z",
"customer": { "name": "Jane Doe", "email": "jane@example.com", "phone": "+421 900 000 000",
"note": "Handles in black, please.", "photoCount": 2 },
"design": { "type": "kitchen", "layout": "…", "dims": { … }, "runs": [ … ], "room": { … }, "state": { … } }
}
}
}
Regoli ta’ konsenja:
- Wieġeb 2xx fi żmien 8 sekondi. Ipproċessa wara t-tweġiba (kju, worker) – inkella hemm riskju ta’ timeout.
- F’każ ta’ żball jew timeout nerġgħu nippruvaw wara 10 s, 1 min u 5 min, imbagħad naqtgħu qalbna – it-talba dejjem tista’ tniżżilha permezz tal-API. Ridirezzjonijiet (3xx) ma nsegwuhomx.
- L-istess
X-Furniconf-Delivery= l-istess konsenja. Aħżnu u injora d-duplikati (idempotenza). - Ir-ritratti tal-klijent mhumiex fil-webhook (biss
photoCount) – niżżilhom permezz tal-API (pass 3).
Verifika tal-firma
Il-firma hija HMAC-SHA256 bis-sigriet tiegħek fuq is-sekwenza timestamp + "." + korp mhux ipproċessat. Qabbel f’ħin kostanti:
// Node.js / Express – the signature is computed over the RAW body, so read it before any JSON parser
const express = require('express');
const crypto = require('crypto');
const app = express();
const SECRET = process.env.FURNICONF_WEBHOOK_SECRET;
app.post('/furniconf/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.header('X-Furniconf-Timestamp') || '';
const got = req.header('X-Furniconf-Signature') || '';
const want = 'sha256=' + crypto.createHmac('sha256', SECRET).update(ts + '.' + req.body).digest('hex');
if (got.length !== want.length || !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want))) {
return res.status(401).end(); // not from Fittle
}
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.status(401).end(); // replay protection (5 min)
const evt = JSON.parse(req.body);
res.status(200).end(); // answer first (within 8 s) …
if (evt.event === 'quote.created') {
const d = evt.data.design;
// … then process: idempotency by delivery id, save, assign to a craftsman
saveRequest({ deliveryId: evt.id, requestId: d.id, product: d.product, customer: d.customer, design: d.design });
}
});
app.listen(3000);
L-istess f’PHP:
<?php // PHP – same check with hash_hmac + hash_equals
$secret = getenv('FURNICONF_WEBHOOK_SECRET');
$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_FURNICONF_TIMESTAMP'] ?? '';
$got = $_SERVER['HTTP_X_FURNICONF_SIGNATURE'] ?? '';
$want = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, $secret);
if (!hash_equals($want, $got) || abs(time() - (int)$ts) > 300) { http_response_code(401); exit; }
http_response_code(200);
$evt = json_decode($raw, true);
if ($evt['event'] === 'quote.created') {
$d = $evt['data']['design'];
// $evt['id'] = delivery id (idempotency), $d['id'] = request id, $d['customer'], $d['design']
}
Test: il-buttuna Ibgħat webhook ta’ prova fil-kont tibgħat avveniment webhook.test bl-istess headers; ir-riżultat (status HTTP, tentattivi) jidher fil-kont tiegħek u fil-log tagħna.
express.raw() qabel express.json(), f’PHP php://input, f’Django request.body, f’Laravel $request->getContent().Pass 3 – API (15-il minuta)
Fil-kont → Integrazzjoni → Iġġenera ċavetta API. Iċ-ċavetta fak_… tintwera darba biss – aħżinha fis-sigrieti tas-server, qatt fil-frontend. Kull sejħa ġġorr Authorization: Bearer fak_…, il-korpi huma JSON, limitu ta’ 600 sejħa kull 10 minuti.
Talbiet ġodda (l-aktar reċenti l-ewwel; ritratti biss bħala numru):
curl -s "https://app.getfittle.com/api/portal/designs?status=new&limit=50" \
-H "Authorization: Bearer fak_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
{ "designs": [
{ "id": "DMU3VCTB63BB7", "product": "kitchen", "status": "new", "createdAt": "2026-09-16T09:12:33.000Z",
"customer": { "name": "Jane Doe", "email": "jane@example.com", "phone": "+421 900 000 000", "note": "…", "photoCount": 2 },
"design": { … } }
] }
Talba waħda bil-lista tar-ritratti u tniżżil ta’ ritratt:
curl -s "https://app.getfittle.com/api/portal/designs/DMU3VCTB63BB7?photos=1" \
-H "Authorization: Bearer fak_…"
{ "design": { "id": "DMU3VCTB63BB7", …,
"customer": { …, "photoCount": 2,
"photos": [ { "n": 1, "name": "kitchen-now.jpg", "type": "image/jpeg", "size": 812345,
"url": "/api/portal/designs/DMU3VCTB63BB7/photos/1" }, … ] } } }
# photo (binary) – the API key is required here as well
curl -s "https://app.getfittle.com/api/portal/designs/DMU3VCTB63BB7/photos/1" \
-H "Authorization: Bearer fak_…" -o kitchen-now.jpg
Bidla fl-istatus – biex int u aħna naraw fejn tinsab it-talba:
curl -s -X POST "https://app.getfittle.com/api/portal/designs/DMU3VCTB63BB7/status" \
-H "Authorization: Bearer fak_…" -H "Content-Type: application/json" \
-d '{ "status": "contacted" }'
{ "ok": true, "design": { "id": "DMU3VCTB63BB7", "status": "contacted", … } }
Link ta’ previżjoni 3D għall-mastrudaxxa (mingħajr kont, iffirmata, valida għan-numru ta’ jiem mogħti):
curl -s -X POST "https://app.getfittle.com/api/portal/designs/DMU3VCTB63BB7/view-link" \
-H "Authorization: Bearer fak_…" -H "Content-Type: application/json" \
-d '{ "days": 30 }'
{ "ok": true,
"url": "https://app.getfittle.com/c/kitchen?k=FITT-XXXX-XXXX-XXXX&load=DMU3VCTB63BB7&vt=eyJ0…&view=1" }
Mingħajr webhook: ġbir perjodiku (eż. kull 5 minuti):
# polling instead of a webhook: everything since the last check curl -s "https://app.getfittle.com/api/portal/designs?since=2026-09-16T00:00:00Z" -H "Authorization: Bearer fak_…"
Statusi tat-talba: new → seen → contacted → quoted → won / lost. Toroq oħra: GET /api/portal/me (il-profil u s-settings tiegħek), POST /api/portal/webhook/test.
It-tweġibiet ta’ żball għandhom il-forma { "error": "…", "reason": "…", "reqId": "…" } – semmi r-reqId meta tirrapporta problema.
Pass 4 – Previżjoni 3D għall-mastrudaxxa u fil-portal
Kull talba għandha link minn view-link: il-mastrudaxxa jiftaħha fil-browser (anki fuq il-mowbajl) u jara eżatt dak li ddisinja l-klijent – rotazzjoni, qisien, ftuħ tal-bibien, mixja, mingħajr login. Ibgħatha bl-email jew SMS, jew uriha fid-dettall tat-talba fil-portal tiegħek:
<!-- the request inside your portal: a 3D preview in an iframe (URL from view-link) -->
<iframe src="https://app.getfittle.com/c/kitchen?k=FITT-…&load=DMU3VCTB63BB7&vt=eyJ0…&view=1"
style="width:100%;height:720px;border:0;border-radius:12px" allow="xr-spatial-tracking"></iframe>
Il-link hija ffirmata u limitata fiż-żmien (days, awtomatikament 30). Wara li tiskadi ġġenera waħda ġdida – it-talba tibqa’ maħżuna.
Pass 5 – X’hemm fil-JSON tad-disinn
design għandu żewġ saffi: deskrizzjoni li tinqara għan-nies u state għall-magna:
{
"type": "kitchen", // kitchen | wardrobe | living | furniture | bathroom
"layout": "Rohová (L)", // readable values – in the shop's default language
"dims": { "baseHeight": 72, "wallHeight": 72, "totalHeight": 218, … },
"runs": [ { "id": "A", "length": 365, "columns": [ { "width": 80, "base": "Drez", "wall": "Otvorená polica", … } ] } ],
"decors": { "base": "Dekor 3017", "baseCode": "D3017", … },
"appliances": { "fridge": 1, "oven": 1, "hob": 1, "sink": 1, … },
"room": { "width": 460, "depth": 360, "height": 270, "openings": [ … ], "cart": { … } },
"state": { … } // raw configurator state (codes) – for loading back into 3D
}
- Il-valuri li jinqraw (forma, qisien, moduli u l-kontenut tagħhom, dekorazzjonijiet b’kodiċijiet, apparat, kamra b’fetħiet) huma dejjem fil-lingwa awtomatika tal-kont tiegħek – tkun xi tkun il-lingwa li uża l-klijent.
statehuwa l-istat mhux ipproċessat tal-konfiguratur (kodiċijiet, mhux testi). Jippermetti li d-disinn jerġa’ jitgħabba fi 3D kull ħin; tibdlux.room.cart(biss fil-modalità tal-prezzijiet): karrettun b’kodiċijiet tal-prodotti, kwantitajiet u ammonti jekk il-klijent poġġa fil-kamra l-oġġetti 3D jew il-madum tiegħek.- Il-format huwa stabbli – oqsma ġodda jiżdiedu biss, dawk eżistenti qatt ma jinbidlilhom isimhom.
Ħlas u limiti
- Tħallas għal kull talba li tasal il-prezz miftiehem mingħajr VAT; il-fattura ta’ kull xahar tasal bl-email (PDF), ir-rendikont tat-talbiet jinsab fil-kont.
- API: 600 sejħa / 10 minuti għal kull ċavetta; webhook: 8 s biex twieġeb, 4 tentattivi.
- It-talba tal-klijent hija limitata fid-daqs (disinn sa 350 kB, nota 4,000 karattru, ritratti sa 2 MB) – akbar minn hekk il-browser jirrifjuta qabel ma jibgħat.
Lista ta’ kontroll qabel it-tnedija
- Id-dominji tas-sit tiegħek jinsabu fil-kont (lock taċ-ċavetta) u l-konfiguratur jitgħabba fuq il-paġna live.
- Webhook: firma vverifikata fuq il-korp mhux ipproċessat, 2xx fi żmien 8 s, duplikati skont
X-Furniconf-Deliveryinjorati, test għadda. - Iċ-ċavetta API tinsab fis-sigrieti tas-server; f’każ ta’ tnixxija ġġenera waħda ġdida fil-kont (il-qadima tieqaf minnufih).
- Tibgħat lura l-istatusi tat-talbiet (
status) – jidhru wkoll fil-kont. - Il-mastrudaxxa jirċievi l-
view-link, mhux JSON. - Il-ħlas għal kull talba huwa ssettjat (inkella l-kard Integrazzjoni tkun nieqsa).
Kodiċijiet ta’ żball (reason)
| Kodiċi | Tifsira u soluzzjoni |
|---|---|
api_key (401) | Iċ-ċavetta fak_… mhijiex valida jew ġiet iġġenerata waħda ġdida. Iċċekkja l-header Authorization. |
integration_off (403) | Il-kont m’għandux ħlas għal kull talba – webhook/API mhumiex attivi. Iktbilna. |
suspended (403) | Il-kont huwa sospiż. |
rate_limit (429) | Wisq sejħiet (600 / 10 min). Naqqas ir-ritmu, uża since. |
license, quota, bad_email… (/api/quote) | Il-konfiguratur irrifjuta t-talba: liċenzja/dominju invalidu (license), limitu ta’ kull xahar maqbuż (quota), email/telefown/tul invalidu (bad_email, bad_phone, too_long), disinn akbar minn 350 kB (too_big). |
Appoġġ
Chat direttament fil-kont (kard Appoġġ) jew email; għal problema teknika ehmeż ir-reqId mit-tweġiba u l-X-Furniconf-Delivery mill-webhook. Referenza sħiħa: PORTAL.md.