nesting true-shape — poligoane neregulate pe plăci fixe, cu zone de excludere (laser / plasmă / jet de apă)
POST
/v1/validate/2d
validează o cerere fără s-o rezolve — gratuit, fără cheie, fără cotă (2d / 1d / wood / nest)
POST
/v1/validate/1d
validează o cerere fără s-o rezolve — gratuit, fără cheie, fără cotă (2d / 1d / wood / nest)
POST
/v1/validate/wood
validează o cerere fără s-o rezolve — gratuit, fără cheie, fără cotă (2d / 1d / wood / nest)
POST
/v1/validate/nest
validează o cerere fără s-o rezolve — gratuit, fără cheie, fără cotă (2d / 1d / wood / nest)
GET
/v1/jobs/{id}
interoghează o lucrare asincronă a motorului max — returnează status-ul ei și, odată finalizată, planul (fără cotă; doar propriile lucrări)
POST
/v1/import/nest
citește conturul pieselor dintr-un fișier SVG sau DXF — necesită o cheie, nu consumă cotă
GET
/v1/usage
consumul și cota CONTULUI în luna curentă
GET
/v1/health
verificare de disponibilitate — fără cheie, fără limitare de rată, fără bază de date
POST /v1/validate/{2d,1d,wood,nest} — response
valid
boolean
Always true on a 200 — a malformed body is a 400 with the exact bad field path instead. The body is the SAME one the matching optimize endpoint takes.
mode
string
"2d" | "1d" | "wood" | "nest" — the endpoint you called.
contractVersion
string
Shape version. Currently "1".
engineEnabled
boolean
NEST ONLY — whether the nesting engine is built into this deployment. Absent for the rectangular modes.
parts
object
{ rows, total } — part rows sent, and the total quantity after qty expansion. Check it against the part cap before you spend a call.
stock
object
{ rows, total } — the same for stock.
warnings
string[]
Approximate, SOLVE-FREE feasibility notes — e.g. a part that fits no stock (2D and nest: bounding box; wood: no matching section long enough). Empty ⇒ every part fits something. Ignores trim, kerf and material: a shape check, not a solve.
Unități de măsură
API-ul este agnostic față de unitatea de măsură. Alege o singură unitate — milimetri, inch, orice — folosește-o pentru fiecare număr pe care îl trimiți, iar fiecare număr pe care îl primești înapoi este exprimat în aceeași unitate. Nimic nu este convertit pe server și niciun nume de câmp nu impune o unitate.
Acest lucru acoperă dimensiunile pieselor și ale materialului de bază, kerf, tolerance, trim și minOffcut la intrare, precum și fiecare coordonată, poziție, lungime rămasă, rest și lungime de tăiere la ieșire. Amestecarea unităților în cadrul aceleiași cereri produce un plan care trece validarea, dar este greșit din punct de vedere fizic, iar serverul nu poate detecta asta.
Sistemul de coordonate
Originea este colțul din stânga sus al plăcii: x crește spre dreapta, pe lățimea plăcii, iar y crește în jos, pe înălțimea plăcii. Valorile x și y ale unei piese indică colțul ei din stânga sus, iar w și h sunt dimensiunile așa cum a fost plasată — deja interschimbate atunci când rotated este true — astfel încât dreptunghiul x, y, w, h este amprenta pe placă, fără niciun calcul suplimentar. Dreptunghiurile resturilor folosesc același sistem.
Tăierea de margine deplasează plasările: trim.left împinge fiecare piesă spre dreapta, iar trim.top împinge fiecare piesă în jos, deoarece piesele sunt aranjate în interiorul zonei utilizabile și apoi decalate înapoi pe placa întreagă. trim.right și trim.bottom micșorează zona utilizabilă fără să mute originea. Valorile w și h ale unei plăci sunt întotdeauna dimensiunile complete ale materialului, inclusiv tăierea de margine — și tocmai de aceea tăierea de margine se numără ca deșeu în yieldPct.
implicit 1 — expandat pe server; se numără în plafonul de 2,000 piese
name
etichetă opțională, returnată la fiecare plasare
rotatable
implicit true — poate fi rotită piesa la 90°
grainGroup
membrii unui grup sunt păstrați pe aceeași placă (potrivirea fibrei)
priority
de tăiat obligatoriu: câștigă spațiu pe placă atunci când stocul este plafonat (împreună cu respectStock)
edgeBanding
cantuire pe fiecare latură: denumește o referință de tip pe oricare dintre top / right / bottom / left (un șir liber, codul tău) — răspunsul însumează metrii pe referință. Doar metadate, nu mută niciodată o piesă. Doar 2D
material
etichetă de material (un șir liber, codul tău): piesele și materialul de bază cu același material sunt aranjate doar împreună. Spre deosebire de edgeBanding, schimbă aranjamentul. Absent = un singur bazin nespecificat. Funcționează în fiecare mod
Câmpurile materialului de bază
w și h sunt obligatorii. qty este implicit 1 și devine plafon strict doar împreună cu respectStock. price este per placă și determină totalPrice și modul cost. priority (boolean) folosește acest material de bază cu prioritate; material (un șir liber) îl restrânge la piesele cu același material.
Opțiuni
kerf
lățimea lamei (implicit 0)
tolerance
acceptă tăieri care depășesc cu până la această valoare
trim
tăiere de margine pe fiecare latură: left, right, top, bottom
ordonează variantele după cel mai mic preț total al materialului; cu mai multe dimensiuni de material cu preț, le combină (2D) sau alege lungimea cea mai ieftină (1D) pentru a reduce costul, chiar dacă astfel se folosește mai mult material
respectStock
tratează qty de pe fiecare rând de material ca plafon strict
minOffcut
raportează doar resturile a căror latură scurtă atinge cel puțin această valoare
maxCutStages
limită de etape pentru ferăstrăul de panouri — un număr de faze, nu adâncimea brută a arborelui
minimizeRotations
preferă aranjamentele care rotesc mai puține piese
effort
'fast' | 'balanced' (implicit 'balanced'). Adâncimea căutării: 'balanced' rulează best-of-ul complet cu mai multe strategii; 'fast' sare peste singura căutare costisitoare de combinații de arii per placă — considerabil mai rapid la lucrările mari, cu prețul câtorva puncte de utilizare, rămâne ghilotină-valid și niciodată mai dens decât 'balanced'. La lucrările mici, rezultatul este de obicei identic. Doar motorul heuristic
include face două lucruri. RESTRÂNGERE — "cutPlan" și "offcuts" sunt active implicit; un array prezent păstrează doar tokenurile de restrângere pe care le listează (un array gol le elimină pe amândouă). EXPORT ADIȚIONAL — "svg", "csv" și "dxf" adaugă fiecare acel export în răspuns ca ȘIR: svg un desen de layout 2D de sine stătător (doar 2D — o cerere 1d/wood returnează în schimb un avertisment), dxf un desen R12/AC1009 pe straturile STOCK/PARTS/LABELS, csv o listă de tăiere. Tokenurile de export nu afectează restrângerea, așa că include:["svg"] adaugă svg și — nedenumind niciun token de restrângere — elimină cutPlan/offcuts; folosește ["cutPlan","offcuts","svg"] pentru a păstra totul și a adăuga svg.
Opaque JSON of your own — an ERP article number, an order-line id, a customer ref. Echoed VERBATIM on every placed piece of that part. The optimizer never reads it, so it can never change a layout.
stock[].meta
object
The same on a stock row: echoed on every sheet / rod / nested sheet cut from it, so the plan reconciles with your system without a lookup table.
Opțiunea effort echilibrează timpul de calcul cu utilizarea. Iată acest compromis, măsurat pe o lucrare solicitantă — fiecare cifră provine din packerul real.
La majoritatea lucrărilor (mai mici) cele două sunt identice; diferența apare doar la lucrările mari ca aceasta. balanced este valoarea implicită și nu este niciodată mai dens decât poate atinge fast.
Și oricum este rapid: chiar și cele mai mari lucrări de producție — 2.000 de piese și mai mult — se rezolvă în câteva secunde pe motorul implicit, confortabil în bugetul de timp al API-ului.
cutLines vs sawPasses — cutLines unifică tăierile coliniare (o singură reglare a opritorului); sawPasses numără fiecare trecere. Două măsuri oneste ale aceluiași plan, nu o pretenție de a coincide cu numărul raportat de vreun concurent.
guillotineValid / cutPlan — Când un aranjament nu poate fi tăiat de la o margine la alta, guillotineValid este false și cutPlan este null. Aceasta este o informație reală — nu se poate realiza pe un ferăstrău pentru panouri — nu o eroare.
unplaced + warnings — O lucrare imposibil de satisfăcut returnează 200, cu piesele listate în unplaced și o notă în warnings. Un plan cu care poți lucra este mai util decât un cod de stare.
edgeBanding — Când o piesă poartă edgeBanding, răspunsul adaugă un bloc edgeBanding: metrii liniari pe care îi consumă fiecare referință de tip, per piesă și ca total pe comandă. Este geometrie exactă, fără adaos pentru pierderi — atelierul îl adaugă singur — și presupune intrare în milimetri (÷1000 pentru metri). Cheia lipsește complet pentru o lucrare fără cantuire.
materials / unmatchedMaterials — Când o piesă sau un material de bază poartă material, răspunsul adaugă materials (un rezumat per material — material, sheetCount/rodCount, yieldPct, placed, total, totalPrice) și unmatchedMaterials (cererea al cărei material nu are material de bază potrivit). La lemn, materialul călătorește în schimb pe fiecare secțiune de secțiune transversală. Ambele chei lipsesc pentru o lucrare fără material, care rămâne identică la nivel de octet.
Câmpurile răspunsului
Numele și tipurile câmpurilor fac parte din contract, așa că tabelele de mai jos rămân în engleză în toate limbile — un nume de câmp tradus ar documenta un API care nu există.
POST /v1/optimize/2d — top level
engine
string
Which engine ran: "heuristic", "balanced" or "max".
engineVersion
string
Algorithm identity — package version + a content hash of the algorithm source. Moves automatically on any packer change, and independently per engine.
contractVersion
string
Shape version, matching the /v1/ in the path. Currently "1".
deterministic
boolean
Always true. Present so a client can assert the guarantee it relies on.
sheets
array
One entry per sheet used, in cutting order.
metrics
object
Aggregate numbers for the whole job.
cutPlan
array | null
The sawing plan. null when guillotineValid is false; absent when excluded via include.
unplaced
array
Parts that did not fit, aggregated by name + size. Empty when everything fitted.
warnings
string[]
Free-text notes about the plan. Do not parse — branch on unplaced, guillotineValid and metrics.
guillotineValid
boolean
True when the layout is producible with edge-to-edge cuts, i.e. on a panel saw.
edgeBanding
object
Linear metres of edge banding, grouped by type reference. ABSENT unless a part requested banding via parts[].edgeBanding. 2D only.
materials
array
Per-material rollup. ABSENT unless a part or stock row carried material — a material-free job stays byte-identical. (OPEN-256)
unmatchedMaterials
array
Demand whose material has no matching stock at all. ABSENT when it does not happen. A missing-material report, not a did-not-fit one.
svg
string
Inline SVG of the 2D layout (self-contained, no external refs). Present ONLY when include contains "svg". 2D only. (OPEN-223)
csv
string
Inline CSV cut list. Present ONLY when include contains "csv".
dxf
string
Inline DXF (R12/AC1009) on layers STOCK/PARTS/LABELS. Present ONLY when include contains "dxf".
timing.solveMs
number
Milliseconds inside the packer, 2 decimals. Excludes parsing, auth and queueing.
sheets[]
w, h
number
FULL stock dimensions, trim included — not the usable area.
price
number | null
Price of the stock row this sheet came from, or null if none was given.
parts
array
Placements on this sheet.
offcuts
array
Usable leftover rectangles, filtered by options.minOffcut. Absent (not empty) when excluded via include.
meta
object
Present only when the stock row carried meta — echoed verbatim from stock[].meta. (OPEN-224)
sheets[].parts[]
name
string
The requested name, or the generated default "Part <row>".
x, y
number
Top-left corner of the part, from the top-left corner of the sheet.
w, h
number
Dimensions AS PLACED — already swapped when rotated is true. No client-side swap needed.
rotated
boolean
True if the part was turned 90° from the requested w×h. Informational only.
meta
object
Present only when the part carried meta — echoed verbatim from parts[].meta on every placed piece. (OPEN-224)
sheets[].offcuts[]
x, y, w, h
number
A leftover rectangle, in the same coordinate system as the placements.
metrics (2D)
sheetCount
integer
Sheets used. Equals sheets.length.
yieldPct
number
Placed part area ÷ total FULL sheet area × 100, 2 decimals. Trim and kerf count as waste.
placed
integer
Pieces placed, after qty expansion.
total
integer
Pieces requested, after qty expansion.
cutLines
integer
Collinear cuts merged: same axis, same coordinate, same stage counted once — one fence setting.
sawPasses
integer
Every cut, one per strip crossed — how many separate passes the saw makes.
cutLength
number
Total distance sawn, 3 decimals. Same under either counting convention. In your unit.
totalPrice
number
Sum of the used sheets’ prices, 2 decimals. 0 when no stock row carried a price.
unplaced[] (2D)
name
string
The part name.
w, h
number
As requested.
qty
integer
How many of this part could not be placed.
edgeBanding (2D — present only when a part is banded)
totalMeters
number
Order-wide total across every banded edge, 3 decimals. ⚠️ Assumes mm input: top/bottom edges run the part width, left/right the height, ×qty, ÷1000. In another unit it is your raw edge length ÷ 1000.
byType[]
{ reference, meters }
Order total split by the caller-supplied type reference, sorted by reference.
byPart[]
{ name, meters, byType }
One entry per requested part row that has any banded edge; its byType splits that part’s metres by reference.
cutPlan[]
sheet
integer
0-based index into sheets (2D) or rods (1D). Named sheet in both.
step
integer
1-based order within THIS sheet — it restarts at 1 on every sheet.
axis
"h" | "v"
"h": blade travels along x, separating top from bottom. "v": along y, separating left from right.
pos
number
The blade’s LOW-COORDINATE edge — the y value for "h", the x value for "v" — not its centre line. The kerf occupies pos to pos + kerf.
length
number
Distance the blade travels on this cut: the extent of the region crossed. Always 0 in 1D.
stage
integer
1-based machine pass. Increments only when the axis flips relative to the parent cut.
2D · edgeBanding
Când o piesă poartă edgeBanding, răspunsul adaugă un bloc edgeBanding: metrii liniari pe care îi consumă fiecare referință de tip, per piesă și ca total pe comandă. Este geometrie exactă, fără adaos pentru pierderi — atelierul îl adaugă singur — și presupune intrare în milimetri (÷1000 pentru metri). Cheia lipsește complet pentru o lucrare fără cantuire.
Când o piesă sau un material de bază poartă material, răspunsul adaugă materials (un rezumat per material — material, sheetCount/rodCount, yieldPct, placed, total, totalPrice) și unmatchedMaterials (cererea al cărei material nu are material de bază potrivit). La lemn, materialul călătorește în schimb pe fiecare secțiune de secțiune transversală. Ambele chei lipsesc pentru o lucrare fără material, care rămâne identică la nivel de octet.
The tag exactly as you sent it. Free text, matched exactly.
sheetCount | rodCount
integer
Stock consumed for this material — sheetCount on 2D and nest, rodCount on 1D.
yieldPct | density
number
This material’s own fill — yieldPct on the rectangular modes, density on nest (a polygon fill, not comparable to yieldPct).
placed, total
integer
Pieces placed and requested for this material, after qty expansion.
totalPrice
number
Sum of the prices of the stock used for this material.
unmatchedMaterials[] — demand with no matching stock
material
string
The tag that has no stock of its own anywhere in the request.
parts
array
The demand rows in that material, in the mode’s unplaced shape: name, w, h, qty on 2D; name, length, qty on 1D; name, qty on nest.
cutPlan — ce înseamnă fizic un pas
Un step este o singură mișcare a lamei, iar lista este exact în ordinea în care poți tăia efectiv: o tăiere părinte înaintea tăierilor din interiorul bucății pe care a produs-o, pentru că nu poți reteza transversal o fâșie înainte de a o fi desprins. axis "h" înseamnă că lama se deplasează pe direcția x și separă partea de sus de cea de jos; axis "v" înseamnă că se deplasează pe direcția y și separă stânga de dreapta. pos este muchia lamei cu coordonata MAI MICĂ — valoarea y pentru "h", valoarea x pentru "v" — nu axa ei mediană: banda de kerf ocupă intervalul de la pos la pos + kerf, deci lama mușcă în direcția în care crește coordonata, adică în jos pentru "h" și spre dreapta pentru "v". Materialul aflat pe partea cu coordonata mai mică a liniei — deasupra ei pentru "h", la stânga ei pentru "v" — este bucata pe care o eliberează acea tăiere. length arată cât parcurge lama la acea singură tăiere: întinderea zonei pe care o traversează, nu lățimea întregii plăci.
stage este o trecere a mașinii. Începe de la 1 și crește doar atunci când axa se schimbă față de tăierea părinte, așa că despicarea unei plăci în șase fâșii este o singură etapă, iar retezarea lor transversală este următoarea. Acesta este sensul în care se vorbește despre „tăiere în trei etape” la ferăstrăul pentru panouri, nu adâncimea arborelui de tăiere, și este exact ceea ce limitează maxCutStages. sheet este un index în sheets care pornește de la 0, iar step reîncepe de la 1 pe fiecare placă, în loc să continue pe toată lucrarea.
cutPlan este null — nu lipsește, nu este gol — de fiecare dată când guillotineValid este false: un aranjament care nu poate fi tăiat de la o margine la alta nu are nicio secvență de tăiere de returnat. Lipsește complet din payload dacă l-ai exclus din include.
Piesele primesc length (plus qty, name, priority); materialul de bază primește length, qty și price. Atât piesele, cât și materialul de bază acceptă în plus o etichetă opțională material (materialul de bază și priority) — material restrânge o piesă la material de bază cu același material, iar răspunsul adaugă atunci materials și unmatchedMaterials, ca la 2D. Opțiunile sunt kerf, tolerance, trim.start / trim.end, minimizeCost, respectStock și minOffcut. Răspunsul returnează rods în loc de sheets, fiecare cu piesele sale (parts), lungimea rămasă și resturile (offcuts).
rods înlocuiește sheets și nu există guillotineValid, pentru că o tăiere liniară poate fi realizată întotdeauna. Valoarea pos a fiecărei piese este distanța de la capătul ei apropiat până la capătul barei pe care îl retează trim.start, așa că prima piesă începe exact la trim.start, iar fiecare pos următor adaugă câte un kerf. remaining este restul UTILIZABIL: kerf-ul tăierii care îl desprinde de ultima piesă este deja scăzut, deci este lungimea recuperabilă, nu golul brut. Este raportat pentru fiecare bară, chiar și atunci când se află sub minOffcut — minOffcut filtrează doar lista offcuts, care conține cel mult o intrare. În planul de tăiere, sheet este indexul barei, axis este întotdeauna "v", stage este întotdeauna 1 și length este întotdeauna 0: o retezare de bară nu are o distanță de parcurs de raportat, motiv pentru care metrics din 1D conține cuts, dar nu cutLength.
Lemnul are o identitate pe care o bară simplă nu o are: o piesă 50×150 nu poate ieși din material 50×100, oricâtă lungime ar rămâne. De aceea piesele și materialul poartă sw și sh, cele două laturi ale secțiunii, în orice ordine — 50×100 și 100×50 sunt aceeași grindă întoarsă și formează o singură secțiune. Lucrarea se împarte pe secțiuni, fiecare secțiune este potrivită cu materialul ei și rezolvată separat, iar un singur apel returnează totul. Piesele și materialul de bază acceptă în plus o etichetă opțională material (materialul de bază și priority): cu ea, un stejar 50×100 și un pin 50×100 devin două secțiuni separate, iar fiecare secțiune poartă materialul ei. Opțiunile sunt aceleași ca la 1D.
sections înlocuiește rods la nivelul de sus: fiecare intrare este o secțiune cu propriile rods (identice ca formă cu cele din 1D) și propriile metrics, așa că cifrele pe material există fără să le recalculezi. unmatched nu are echivalent în 1D — este cererea pentru a cărei secțiune nu ai furnizat deloc material, o problemă diferită de unplaced (piese care aveau material și nu au încăput) și cu altă rezolvare, de aceea cele două nu se amestecă niciodată. metrics.total numără fiecare piesă cerută, inclusiv cele din unmatched. În planul de tăiere fiecare pas își indică și section, iar sheet este indexul barei ÎN INTERIORUL acelei secțiuni, nu un contor pe toată lucrarea.
POST /v1/optimize/wood — top level (what differs from 1D)
sections
array
Replaces rods at the top level: one entry per cross-section, each matched to its own stock and solved on its own.
unmatched
array
Demand whose cross-section has NO stock at all. Deliberately separate from unplaced (had stock, did not fit) — the fix differs, so the two are never mixed.
unplaced
array
Did-not-fit demand, aggregated across the sections that DID have stock. Same shape as 1D.
metrics
object
Job-wide totals across every section (below).
cutPlan
array | null
As 1D, except every step also carries section, and sheet is the rod index WITHIN that section, not a job-wide counter.
csv, dxf
string
Inline export, present only when include names the token. svg is 2D-only — a wood request asking for it gets a warning instead.
sections[]
section
string
Normalised cross-section key, e.g. "50x100" — short side first, so 50×100 and 100×50 are one section.
sw, sh
number
The two cross-section sides: sw the SHORT one, sh the long one, whatever order they arrived in.
stockName
string | null
The name of the stock row this section was matched to, or null when that row carried none.
material
string
Present only when the section carries a material — an oak 50×100 and a pine 50×100 are two sections. (OPEN-256)
rods
array
Identical in shape to the 1D rods[] above, meta and offcuts included.
metrics
object
This section’s own totals: rodCount, yieldPct, placed, total, cuts, totalPrice — so the per-section figures need no recomputation.
unplaced
array
This section’s parts that had stock and still did not fit.
metrics (wood)
sectionCount
integer
Cross-sections solved. Equals sections.length.
rodCount
integer
Bars used across every section.
yieldPct
number
Placed length ÷ total FULL bar length × 100 over the whole job, 2 decimals.
placed
integer
Pieces placed, after qty expansion.
total
integer
Pieces REQUESTED, after qty expansion — unmatched ones included.
cuts
integer
Crosscuts across every used bar.
totalPrice
number
Sum of the used bars’ prices, 2 decimals.
toleranceAcceptedCount
integer
Pieces that fitted only because options.tolerance allowed an overshoot.
unmatched[]
section
string
The cross-section key nothing in stock matched.
sw, sh
number
That cross-section’s two sides.
material
string
Present when the cross-section DOES exist in stock but only in a different material. (OPEN-256)
parts
array
The demand rows in that section, in the 1D unplaced shape: name, length, qty.
qty
integer
Total pieces in this section that had no stock at all.
Cele trei moduri de mai sus împachetează dreptunghiuri. POST /v1/optimize/nest împachetează POLIGOANE ARBITRARE: o piesă este un contur (polygon, cu holes interioare opționale), nu o lățime×înălțime, așa că piesele se întrepătrund în buzunarele concave ale celorlalte, iar golul de aer pe care îl irosește un bounding box este recuperat — pe o lucrare reprezentativă, 6 sheets acolo unde aceleași piese după bounding box au nevoie de 9. Este o clasă diferită de algoritm (un motor geometric de coliziune, nu packerul ghilotină), pentru tăiere cu laser, plasmă și jet de apă. Vin la pachet două lucruri pe care API-ul dreptunghiular nu le poate exprima: zone de excludere per placă (stock[].exclusions — un defect, amprenta unei cleme, o zonă pretipărită; o zonă cu quality 0 este o regiune interzisă oricărei piese) și holes true-shape. Partiționarea pe material și transmiterea meta funcționează ca peste tot. Spațierea se setează de două ori: options.minSeparation între piese și options.edgeClearance la marginea plăcii (implicit este egală cu minSeparation). Cu mai multe dimensiuni de material de bază pentru un material, ambele motoare le compară și păstrează cel mai bun plan — după aria plăcii, sau după preț cu minimizeCost — și pot amesteca dimensiunile (o ultimă placă pe jumătate goală trece pe o dimensiune mai mică), așa că rezultatul nu depinde de ordinea în care le listezi. Motorul implicit, lbf, răspunde instantaneu; engine "max" rulează aceeași lucrare asincron și caută un aranjament cu mai puține plăci (vezi Lucrări asincrone). Exemplul de mai jos este un apel real capturat — opt piese pe o singură placă, cu un colț deteriorat exclus.
POST /v1/optimize/nest — request (top level)
parts
array
One or more NestPart (see below). Required.
stock
array
One or more NestStock sheet types (see below). Required.
options
object
Solve options (see below). Optional.
engine
string
"lbf" (default — single-pass, synchronous, instant) or "max" (asynchronous: 202 + poll GET /v1/jobs/{id}; runs lbf first, then a time-budgeted search that fills sheets one at a time looking for FEWER sheets, and never returns more than lbf, nor a plan that ranks worse; several sheet sizes per material and respectStock are modelled, while a polygon sheet or exclusion zones are served by lbf inside the job, with a warning). "sparrow" is a deprecated alias of "max".
include
string[]
Additive inline export of the ACHIEVED nest: "svg" a self-contained styled drawing (one titled band per sheet, parts as filled polygons with holes, exclusion zones hatched), "dxf" an R12/AC1009 document on layers STOCK/PARTS/HOLES/ZONES/LABELS. Unit-agnostic, exactly like the request coordinates. The geometry in sheets[] is returned either way.
parts[] (NestPart)
polygon
number[][]
The part outline: an ordered ring of ≥3 [x, y] vertices. Given closed (first == last) or open; winding order is not required — it is oriented internally. Required UNLESS the row carries source.
source
object
OPEN-262 — read the outline out of an SVG or DXF FILE instead of listing coordinates (fields below). EXACTLY ONE of polygon / source: both, or neither, is a 400.
holes
number[][][]
Optional interior holes — a part with a cut-out. Each hole is a ring like polygon. NOT accepted alongside source: the file already carries its own holes, and two sources of truth for one geometry is not a thing we resolve silently.
qty
integer
Copies to place. Default 1.
allowedRotations
number[] | "continuous"
Allowed rotations in DEGREES (e.g. [0,90,180,270]). Omit or "continuous" for free rotation.
minQuality
integer
The part may only be placed where sheet quality ≥ this. Default 1 → it avoids every quality-0 exclusion zone. Raise it to keep the part off inferior-but-not-forbidden zones too.
priority
boolean
Must-cut. With options.respectStock (a capped supply, where not every part may fit) these parts are placed first, so a plain part never takes the sheet space a must-cut part needs, and every plan comparison ranks the plan that keeps more of them ahead. Both engines. Without respectStock every part that fits is placed anyway, so it changes nothing. A must-cut part that fits no sheet is still listed in unplaced.
material
string
OPEN-256 — a part of material X nests only on material-X sheets; the job is partitioned by material.
name
string
Optional label, echoed on every placement. Defaults to "Part <1-based row index>".
meta
object
OPEN-224 — opaque JSON (your ERP ids), echoed verbatim on every placed copy. Never affects the layout.
stock[] (NestStock)
w, h
number
Rectangular sheet size. Give w & h OR polygon, not both.
Default 1. A HARD cap only when options.respectStock is true.
price
number
Per-sheet price. Summed into totalPrice and stockUsage[].totalPrice, and the ranking key of options.minimizeCost.
cost
integer
Integer relative per-sheet cost, used by options.minimizeCost for a row that has no price (e.g. when price is not money).
exclusions
object[]
OPEN-233 — per-sheet excluded / inferior zones. Each: { polygon: number[][], quality?: integer }. quality 0 (the default) = a hard no-go region for any part; a higher quality only excludes parts whose minQuality demands at least that. This is what the rectangular API cannot express.
material
string
OPEN-256 — this sheet serves only material-matching parts.
meta
object
OPEN-224 — echoed on every sheet cut from this stock row.
options (NestOptions)
minSeparation
number
Minimum clearance between parts and between a part and any hazard (sheet edge / exclusion zone). Use for kerf / beam / torch width. Default 0. The sheet-edge part can be set separately with edgeClearance.
edgeClearance
number
Minimum distance between any part and the SHEET EDGE, independent of minSeparation (e.g. 5 between parts, 0 or 10 at the edge). Omit it and the edge keeps minSeparation, exactly as before. Both engines. Rectangular sheets only: with a polygon sheet it is a 400, as is a value that leaves no room on a sheet. Exclusion zones keep minSeparation, like parts.
seed
integer
Determinism: a fixed seed → a reproducible layout. Omit and the server pins a fixed default so the response stays reproducible.
minimizeCost
boolean
How "lbf" picks the sheet size when a material has two or more stock rows. It always tries the rows in your order, each row on its own and a cheapest-first order, and keeps the best — so the answer does not depend on the order you list them in. Ranking: most must-cut (priority) parts placed → most parts placed → (with minimizeCost) lowest total price → least total sheet area → fewest sheets. minimizeCost ranks by price (or cost) only when EVERY row of that material has one; otherwise it falls back to sheet area with a warning. Then every sheet, the least-filled first, is re-nested onto the cheaper or smaller rows and replaced when the whole plan ranks better — so sizes can be MIXED within a material (e.g. two large sheets and a small last one). "max" uses the same ranking to decide whether its search result replaces the lbf plan.
respectStock
boolean
Treat each stock qty as a hard cap — on both engines; the mixed-size re-nest never uses a row more often than its qty.
simplifyTolerance
number
Polygon simplification tolerance (max area deviation as a fraction). Speeds up dense DXF outlines with hundreds of vertices. 0 disables.
compact
boolean
Default true, both engines (OPEN-298). After the plan is final, the least-filled sheet of each material is re-nested with the same parts on the same sheet, also with rotation SUBSETS of what each part allows ({0,180}, {0,90,180,270}, one orientation for every copy; intersected with a discrete allowedRotations, never a new angle), and the variant with the smallest used area (usedWidth × usedHeight) is kept. Sheet count, sizes, price and placed parts never change. Within a work budget; false returns the uncompacted layout.
compactFor
"box" | "horizontal" | "vertical"
OPEN-299 — which used measure the compaction minimises; match it to how you charge a partial sheet. "box" (default): the smallest usedArea. "horizontal": the lowest usedHeight (a strip across the full sheet width). "vertical": the smallest usedWidth (a strip over the full sheet height). Ties fall back to the area; the strip modes also try every copy turned 90° or 270° where allowedRotations permits. Sheet count, sizes, price and placed parts never change; never worse than the uncompacted layout on the chosen measure.
timeBudgetMs
integer
Search budget for engine "max", in ms: default 60000, clamped to 10000–180000 (a clamp is reported in warnings). Ignored by "lbf" (single-pass). The job finishes about 1–2 s after the budget plus any queueing; for a job of a few dozen parts 15000 is usually enough. Poll GET /v1/jobs/{id} every 2–3 s — a poll costs no quota.
Fiecare intrare din sheets este o placă folosită; o piesă plasată poartă transformarea rigidă (rotation în grade, apoi translația x/y), NU un polygon reemis — rotește conturul de intrare cu rotation în jurul originii sale și adaugă (x, y) pentru a reconstrui plasarea exact. rotation poate fi negativă; reconstrucția este exactă indiferent de semn. ⚠️ density este aria POLIGONULUI plasat raportată la aria plăcii folosite — umplerea onestă, cu buzunarele concave numărate ca goale — și NU este comparabilă cu yieldPct al unui packer dreptunghiular (care numără fiecare bounding box ca fiind plin, deci arată mai mare pentru un rezultat mai slab); metrica direct comparabilă între cele două este sheetCount pe aceleași piese. Layoutul este determinist: setează options.seed pentru a-l reproduce. exclusions este returnat pe fiecare placă pentru randare. Fiecare placă poartă stock — indexul rândului de material de bază din cerere din care a fost tăiată — iar stockUsage totalizează plăcile, prețul și densitatea pe rând de material de bază, astfel încât o ofertă să poată fi calculată pe dimensiune de placă. Fiecare placă mai poartă și cutLength și pierces (metrics le totalizează): perimetrul însumat al conturilor și al găurilor pieselor sale plasate, și câte o străpungere pentru fiecare contur închis — geometric, fără tăiere pe linie comună sau intrări de tăiere. Fiecare placă mai poartă și usedWidth și usedHeight — cel mai îndepărtat punct la care ajung piesele ei plasate din originea plăcii, bounding box-ul utilizat pentru facturarea unei părți de placă — și usedArea (stockUsage îl totalizează pe rând); ambele motoare compactează implicit placa cea mai puțin umplută spre acel colț, cu subseturi de rotații din cele permise fiecărei piese (options.compact: false îl dezactivează; options.compactFor alege măsura de minimizat — "box" (implicit) aria utilizată, "horizontal" înălțimea utilizată pentru facturarea unei fâșii pe toată lățimea, "vertical" lățimea utilizată pentru o fâșie pe toată înălțimea; numărul de plăci și prețul nu se schimbă niciodată).
POST /v1/optimize/nest — top level
engine
string
Which nesting engine served the request: "lbf", or "max" in the result of a finished async max job.
engineVersion
string
The nesting engine's algorithm identity (jagua-rs revision + build hash). Versions independently of the rectangular engines.
contractVersion
string
Nest contract version, currently "1". Versions independently of the /v1/ rectangular contract — it is a different path and engine family.
deterministic
boolean
true for "lbf" — guaranteed by the pinned seed (false only if the comparison between several stock rows hit its time limit on the server, with a warning). For "max": false when the time-limited search shaped the result, true when the lbf pass alone decided it.
sheets
array
One entry per used sheet.
metrics
object
Job totals (see below).
stockUsage
array
Sheets used per request stock ROW (see below). Always present.
unplaced
array
Demand that could not be placed — a plan-plus-warning, not an error.
warnings
string[]
e.g. unplaced parts, a material with no matching stock, or — for "max" — how the result was reached (for instance that the lbf layout was kept because no layout with fewer sheets was found in the time budget).
materials
array
OPEN-256 per-material rollup — present only when parts/stock carry material.
unmatchedMaterials
array
Parts whose material has no matching stock — present only when it happens.
imported
object
OPEN-262 — what the request’s source files contributed (fields above). ABSENT for a coordinates-only job, which is what keeps such a request byte-identical to before the feature existed.
svg, dxf
string
The achieved nest as an inline drawing — present ONLY when include contains that token. svg is self-contained (no external refs); dxf is R12/AC1009.
timing
object
{ solveMs: number } — the solve time; environment-dependent.
sheets[] (nest)
w, h
number
Present for rectangular sheets — always the real sheet size, also with edgeClearance.
polygon
number[][]
Present for arbitrary-outline sheets instead of w/h.
stock
integer
Index into the request stock[] this sheet was cut from — the key to tie the sheet back to your stock row.
price
number | null
The stock row's price, or null.
density
number
This sheet's fill = placed polygon area / sheet area.
cutLength
number
Σ perimeter of the placed parts' outer outlines AND holes on this sheet, 3 decimals, in your unit — the contour a laser / plasma / waterjet head follows. Geometric: no common-line cutting, lead-in/out or micro-joints (your CAM adds those).
pierces
integer
Pierce points on this sheet: one per closed contour (each placed part's outline + one per hole).
usedWidth, usedHeight
number
OPEN-298 — the farthest x and y any placed part reaches, measured from the sheet origin (0,0), 3 decimals, in your unit; real sheet coordinates (with edgeClearance the edge gap is included). The used bounding box from the origin corner — what an ERP needs to charge a partial sheet (horizontal cut = usedHeight × w, vertical cut = usedWidth × h, boundary box = usedArea); the charging rule stays yours. A polygon sheet: the same, in its own coordinate frame.
usedArea
number
usedWidth × usedHeight, 2 decimals.
parts
array
Placements on this sheet (see below).
exclusions
object[]
The zones that applied to this sheet, echoed for rendering.
material
string
Present when the sheet carried a material (OPEN-256).
meta
object
Echoed from the stock row's meta (OPEN-224).
sheets[].parts[] (nest — placed)
name
string
The requested name, or the generated default.
sheet
integer
0-based index into sheets.
x, y
number
Translation, applied AFTER rotation about the part's origin.
rotation
number
⚠️ Degrees, applied about the part's own origin FIRST. A placed part carries this rigid transform, NOT a re-emitted polygon: rotate your input outline by rotation, then add (x, y) to reconstruct the placement exactly. May be negative; the reconstruction is exact regardless of sign.
material
string
The material this copy nested from (OPEN-256).
meta
object
The part's opaque passthrough (OPEN-224).
metrics (nest)
sheetCount
integer
Sheets used. Equals sheets.length. ⚠️ The honest, cross-comparable metric between nesting and the rectangular modes.
density
number
⚠️ Placed POLYGON area / used sheet area — the honest fill (concave pockets count as empty). NOT comparable to a rectangular packer's yieldPct, which counts each bounding box as solid and so reads higher for a worse result.
placed
integer
Part copies placed, after qty expansion.
total
integer
Part copies requested, after qty expansion.
totalPrice
number
Sum of used sheet prices.
cutLength
number
Σ sheets[].cutLength — placed parts only (outlines + holes), in your unit.
pierces
integer
Σ sheets[].pierces.
stockUsage[] (nest)
stock
integer
Index into the request stock[]. One entry per USED row, sorted by stock. The unit is the row, not the size: two rows of the same w × h with a different price or meta are two entries.
w, h
number
Present for rectangular rows.
material
string
Present when the row carries a material.
sheetCount
integer
Sheets cut from this row.
totalPrice
number
Sum of those sheets' prices (0 for a row without a price, like metrics.totalPrice).
density
number
Placed polygon area / total area of this row's sheets.
usedArea
number
OPEN-298 — Σ sheets[].usedArea of this row's sheets: the used bounding area to total per sheet size.
unplaced[] (nest)
name
string
The part name.
qty
integer
How many copies could not be placed.
rods[]
length
number
FULL bar length as supplied in stock.
price
number | null
Price of the stock row, or null.
remaining
number
USABLE off-cut left on this bar, 3 decimals — the kerf of the cut that frees it from the last piece is already deducted (OPEN-257), so it is the reclaimable length, not the raw gap. Reported even when below minOffcut.
parts
array
Placements, in cutting order along the bar.
offcuts
number[]
At most one entry: [remaining] (the kerf-corrected usable off-cut, OPEN-257) when it is > 0 and ≥ minOffcut, else []. Absent when excluded via include.
meta
object
Present only when the stock row carried meta — echoed from stock[].meta (OPEN-224). Applies to 1D and wood rods.
rods[].parts[]
name
string
The requested name, or the generated default.
pos
number
Offset of the part’s NEAR end from the bar end that trim.start trims.
length
number
The part length, as requested.
meta
object
Present only when the part carried meta — echoed from parts[].meta (OPEN-224).
metrics (1D)
rodCount
integer
Bars used. Equals rods.length.
yieldPct
number
Placed length ÷ total FULL bar length × 100, 2 decimals. Trim and kerf count as waste.
placed
integer
Pieces placed, after qty expansion.
total
integer
Pieces requested, after qty expansion.
cuts
integer
Total crosscuts across all used bars — one per placed piece.
totalPrice
number
Sum of used bar prices, 2 decimals.
toleranceAcceptedCount
integer
Pieces that fitted only because options.tolerance allowed an overshoot.
unplaced[] (1D)
name
string
The part name.
length
number
As requested.
qty
integer
How many could not be placed.
Piese dintr-un fișier (SVG · DXF)
O piesă nu trebuie să sosească neapărat sub formă de coordonate. Pune un document SVG sau DXF în parts[].source și serverul îi extrage conturul — și găurile — cu același cititor pe care aplicația CutOptim îl folosește când tragi un desen peste modul ei Nesting. Fișierul înlocuiește DOAR geometria: qty, material, allowedRotations, minQuality, priority și meta se comportă exact ca pe o piesă polygon, așa că o bibliotecă de piese care există deja ca fișiere CAD nu cere un aplatizor propriu de curbe și arce. O source descrie O singură piesă; un desen care conține mai multe componente separate returnează 400 și te trimite la endpointul de import de mai jos. Răspunsul poartă atunci un bloc imported: câte rânduri provin dintr-un fișier, câte vârfuri au produs și ce unități au declarat acele fișiere — raportate, niciodată aplicate, fiindcă acest API nu convertește nimic.
Nimic nu se stochează. Octeții există doar ca și corp al cererii, sunt prelucrați în memorie și dispar când răspunsul este scris: fără disc, fără bază de date, fără fișier temporar, fără linie de jurnal. Nu rămâne nimic de șters după aceea și nimic nu se păstrează — aceeași lipsă de stare pe care o ține fiecare alt endpoint.
Numele și tipurile câmpurilor fac parte din contract, așa că tabelele de mai jos rămân în engleză în toate limbile — un nume de câmp tradus ar documenta un API care nu există.
The file TEXT — not base64, not a URL, not a multipart upload. Both formats are text documents, so they travel inside the JSON body like any other field. At most 4,000,000 characters per file and 8,000,000 across one request; over either is a 413.
format
"svg" | "dxf"
Omit and the server sniffs the content (an <svg tag ⇒ svg, otherwise dxf). An explicit value ALWAYS wins — including over a filename whose extension disagrees, which is the case worth setting it for.
filename
string
Used for format detection AND as the part name when the row has no name of its own (extension stripped). It never touches a filesystem — there is no file on our side to name.
flattenTolerance
number
Curve and arc flattening tolerance, in the FILE’s own coordinate unit. Default 0.2. Larger = fewer vertices; this is the lever when a dense outline trips the 2,000-vertex per-ring cap.
imported — what the files contributed (response, absent without a source)
parts
integer
Part ROWS whose geometry came from a file.
vertices
integer
Total vertices those files produced after flattening, outlines and holes together — the number to watch against the per-ring cap.
units
string[]
⚠️ Units the files DECLARED (a DXF $INSUNITS), not units we applied. Empty when none declared one. More than one entry also raises a warning: mixing a millimetre drawing with an inch one produces a plan that validates and is physically wrong, and the server must not "fix" that by converting — no field in this API asserts a unit.
POST /v1/import/nest — un fișier, toate contururile
Când un singur desen conține mai multe piese diferite, importă-l mai întâi: acest endpoint returnează fiecare contur închis pe care îl conține, începând cu cel mai mare, exact în forma pe care o cere un rând parts[]. Lipește-le pe cele de care ai nevoie, adaugă propriile qty și material și trimite asta la /v1/optimize/nest. Este și modul în care vezi ce se află într-un fișier înainte să cheltuiești o rezolvare. Necesită o cheie — aplatizarea unei geometrii arbitrare este muncă reală de CPU, iar CPU anonim este o afacere proastă — dar nu rezervă nimic: cota ta rămâne neatinsă și nu se întorc anteturi de rate limit, exact ca la interogarea unui job.
The file text. Required. Same 4,000,000-character cap as parts[].source.
format
"svg" | "dxf"
Omit to sniff; an explicit value wins over the filename extension.
filename
string
Used for detection and to NAME the results: a file with one outline keeps the bare name, several are numbered "<name> 1", "<name> 2", …
flattenTolerance
number
As on parts[].source. Default 0.2.
POST /v1/import/nest — response
format
"svg" | "dxf"
The parser that actually ran — the useful bit when you let the server sniff.
parts[]
{ name, polygon, holes? }
One entry per closed outline, LARGEST FIRST. Each is already in the shape a parts[] row wants: paste it in and add your own qty / material / allowedRotations. Deterministic — the same bytes always give the same vertices in the same order.
vertices
integer
Total vertices after flattening. Compare it against the 2,000-per-ring cap before you build a large request.
sourceUnit
"mm" | "in"
A DXF $INSUNITS declaration, when the file carries one. REPORTED, NEVER APPLIED. Absent for SVG (the format has no unit) and for a DXF that declares none.
warnings
string[]
What the parser could not honour — an SVG transform= attribute (CAD part exports are flat, so we do not apply them), or geometry that never closed into a contour. Empty means the file was read whole.
engineEnabled
boolean
Whether THIS deployment can also SOLVE a nest. Importing is pure parsing and works everywhere; where the nesting engine is not built in, /v1/optimize/nest answers 503 and this flag says so up front. Same field /v1/validate/nest carries.
contractVersion
string
Nest contract version. Currently "1".
Motoare
heuristic(implicit) — algoritmul de împachetare ghilotină, cu mai multe strategii. Cea mai bună utilizare, fiecare aranjament tăiabil pe ferăstrău, întotdeauna un cutPlan complet.
balanced — un algoritm de împachetare MaxRects cu nesting liber. Mult mai rapid la lucrările mari (măsurat ~25× la 2.000 de piese), cu o utilizare puțin mai mică, iar aranjamentele sale adesea nu sunt ghilotină (guillotineValid: false, cutPlan: null). Nu modelează tolerance, minimizeCost, grainGroup, maxCutStages sau minimizeRotations — dacă setezi una, un avertisment îți spune că a fost ignorată.
max — nivelul asincron. La 2D este o căutare în arbore care atinge optimul demonstrat pe mult mai multe lucrări, cu prețul a câteva secunde până la un minut per rezolvare. Rămâne determinist și ghilotină-valid. Nu returnează un plan direct — vezi Lucrări asincrone mai jos. Modelează UN SINGUR format de stoc la dimensiunea completă a plăcii, cu disponibilitate nelimitată și un tipar ghilotină fix în 3 etape: un al doilea rând de stoc, trim, respectStock, material sau grainGroup sunt refuzate cu 400 înainte ca un apel să fie rezervat; tolerance, minimizeCost, maxCutStages, minimizeRotations, firstCut și effort trec, dar sunt ignorate cu un avertisment, iar un rezultat max nu raportează resturi. Trimite acele lucrări către motorul heuristic. La nesting true-shape (POST /v1/optimize/nest) max rulează întâi trecerea lbf și apoi caută, în limita options.timeBudgetMs (60 s implicit, 10–180), un aranjament cu mai puține plăci — niciodată mai multe; nesturile sale poartă deterministic: false. Acolo acceptă mai multe dimensiuni de placă per material și respectStock, și își păstrează rezultatul doar atunci când clasează mai bine decât cel al lui lbf (după preț cu minimizeCost, altfel după aria plăcii); o placă poligonală sau zonele de excludere sunt deservite de lbf din interiorul lucrării, cu un avertisment.
Lucrări asincrone (engine = max)
O rezolvare max durează de la câteva secunde până la un minut, așa că POST /v1/optimize/2d sau /v1/optimize/nest cu engine:"max" nu returnează un rezultat — returnează 202 Accepted cu un jobId, iar apelul este contorizat la trimitere. Interoghează GET /v1/jobs/{id} până când status este "succeeded" (result conține același răspuns pe care îl returnează o rezolvare sincronă a acelui mod — un plan 2D sau un nest; mode îți spune care) sau "failed" (error conține mesajul). Interogarea nu consumă cotă; vezi doar propriile lucrări. Acolo unde nivelul nu este activat pe o instalare, engine:"max" eșuează închis cu 503.
field
type
meaning
jobId
string
The 202 body's id. Poll GET /v1/jobs/{id}.
status
string
queued → running → succeeded | failed.
mode · engine
string
"2d" (submitted to POST /v1/optimize/2d) or "nest" (submitted to POST /v1/optimize/nest), and always "max".
pollAfterMs
integer
202 only — suggested delay before the first poll.
result
object
Present once succeeded — the same shape as the synchronous response of that mode: a 2D plan, or a nest.
error
string
Present once failed — the reason.
quota
object
202 only — reserved, used and limit: the one call metered at submission, and where the ACCOUNT stands this month.
createdAt
string
Poll only — when the job was submitted (ISO-8601).
finishedAt
string | null
Poll only — when the worker finished; null while queued or running.
Determinism și versionare
Fiecare răspuns conține engineVersion. Algoritmii sunt deterministici (singura excepție, căutarea max pentru nesting, limitată în timp, raportează deterministic: false), deci îmbunătățirea unuia schimbă rezultatul pentru aceeași intrare — ceea ce este o modificare incompatibilă dacă folosești cache. Fixează comportamentul trimițând engine explicit și urmărind engineVersion; versiunea din cale, /v1/, se schimbă doar dacă se schimbă structura răspunsului.
Fiecare motor este versionat independent, așa că o modificare la unul nu mișcă niciodată versiunea celuilalt.
Erori
400
invalid_request
Eroare de schemă. details.path indică câmpul problematic.
401
unauthorized
Cheie API lipsă sau necunoscută.
402
quota_exceeded
Cota lunară a fost atinsă. Retry-After indică secundele rămase până la începutul lunii următoare.
403
key_revoked
Cheia există, dar nu poate fi folosită: a fost revocată sau abonamentul Engine API al contului nu mai este activ. Câmpul message spune care dintre ele.
404
not_found
Nu există o astfel de rută — este și răspunsul pe care îl primești pentru calea corectă cu metoda greșită.
413
too_large
Intrare peste o limită (vezi Limite).
429
busy
Capacitate atinsă momentan sau — pe orice rută autentificată cu cheie — adresa ta a trimis prea multe chei API necunoscute într-un minut. Retry-After în secunde; acest răspuns nu se scade niciodată din cota ta.
500
internal
Eroare neașteptată sau backendul de autentificare este inaccesibil (cererile sunt respinse — fail closed).
503
service_unavailable
Un motor cerut nu poate fi servit acum — motorul de nesting sau motorul max asincron. Pentru max sunt două cauze, iar câmpul message spune care: nivelul nu este inclus în acest deployment, sau este inclus dar worker-ul care rezolvă lucrările nu răspunde. Fail-closed înainte ca un apel să fie rezervat, deci nu costă niciodată nimic.
504
solve_timeout
Rezolvarea a depășit limita sa strictă de timp. Pe traseele rectangulare o impune proxy-ul; pe /v1/optimize/nest motorul impune propriul buget, mai scurt, și răspunde cu solve_timeout în plicul obișnuit. Un apel abandonat de proxy nu este taxat.
Corpul erorii
Fiecare eroare produsă de motorul propriu-zis folosește același înveliș. Ramifică logica după error, care este un cod stabil; niciodată după message, a cărui formulare se poate schimba de la o versiune la alta. details este prezent la invalid_request, unde path numește câmpul problematic, și la too_large, unde max și got indică plafonul și valoarea pe care ai trimis-o.
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
{
"error": "invalid_request",
"message": "parts[0].h: must be greater than 0",
"details": { "path": "parts[0].h" }
}
Atât 402, cât și 429 conțin un header Retry-After exprimat în secunde. La 402 numără invers până la resetarea cotei, la 00:00 UTC în prima zi a lunii următoare; la 429 este o scurtă pauză înainte de reîncercare, iar un 429 nu consumă niciodată cotă — apelul rezervat este restituit.
O eroare de rutare răspunde cu not_found, un cod aflat intenționat în afara listei de mai sus, pentru că îl produce routerul, nu contractul API. Primești 404, și nu 405, atunci când calea este corectă, dar metoda este greșită: toate cele patru endpointuri de optimizare acceptă doar POST.
Pe traseele rectangulare, 504 vine de la reverse proxy, nu de la motor, așa că corpul răspunsului este al proxy-ului și nu acest înveliș JSON; o singură lucrare în limitele de intrare de mai jos rămâne mult sub ea, dar mai multe dintre cele mai grele lucrări sosite deodată o pot depăși în coadă — iar un apel abandonat de proxy nu este taxat. /v1/optimize/nest este excepția: acea rezolvare este un subproces cu buget propriu, ținut intenționat sub limita proxy-ului, deci acolo un timeout folosește acest înveliș, cu codul solve_timeout.
Headere de limitare a ratei
Un apel de optimizare reușit conține X-RateLimit-Limit (plafonul lunar al CONTULUI — toate cheile contului împart unul singur) și X-RateLimit-Remaining (apelurile rămase contului în luna curentă, după acesta). Sunt trimise doar de endpointurile de optimizare: contorul este rezervat ca parte a autorizării unei rezolvări, deci /v1/usage și /v1/health nu au nimic de raportat.
X-RateLimit-Limit
integer
The ACCOUNT’s monthly quota — the same number GET /v1/usage returns as limit, not a per-key cap (049).
X-RateLimit-Remaining
integer
Calls left this month on the ACCOUNT, after this one.
Doar citire: nu consumă un apel și nu trimite anteturi de rate limit. ⚠️ used și limit descriu CONTUL, nu cheia cu care ai apelat: fiecare cheie activă a contului consumă dintr-o singură alocare comună, deci mai multe chei nu înseamnă mai multă cotă. used numără luna calendaristică UTC curentă pentru toate, remaining este limit minus used și nu coboară niciodată sub zero, periodEnd este ziua de resetare ca dată simplă YYYY-MM-DD, iar keyPrefix este prefixul public al cheii folosite. Cheia în sine nu este returnată de niciun endpoint — se stochează doar hash-ul ei, așa că o cheie pierdută se înlocuiește, nu se recuperează.
plan
string
Tier slug frozen onto the key when it was created.
used
integer
Calls counted in the current UTC calendar month across EVERY key on the ACCOUNT — revoked keys included, so revoking a key cannot un-spend what it spent (053).
limit
integer
The ACCOUNT’s cap: the largest monthly quota frozen onto any of its ACTIVE keys (049). Not a per-key allowance — more keys do not add quota.
remaining
integer
limit − used, never negative.
periodEnd
string
Reset day as YYYY-MM-DD — a date, not a timestamp.
Fără cheie, fără cotă, fără bază de date. Nu atinge intenționat nimic care are stare, astfel încât o defecțiune a depozitului de chei să nu poată face serviciul să pară mort în ochii unui orchestrator. engines listează id-urile pe care această instalare le acceptă în engine, iar engineVersion este versiunea motorului implicit.
status
string
Always "healthy" when the process answers.
service
string
Always "cutoptim-engine".
contractVersion
string
Shape version. Currently "1".
engineVersion
string
The DEFAULT engine’s version, not a per-engine list.
engines
string[]
SYNCHRONOUS engine ids this deployment accepts in engine — ["heuristic","balanced"]. The async max tier is reported separately in maxEngines, never here.
modes
string[]
Optimize paths this deployment serves: "2d", "1d", "wood", plus "nest" only where the nesting engine is built in. Endpoint discovery without reading this page.
nestEngines
string[]
Nesting engine ids this deployment can serve — ["lbf"] on the production API, [] where the Rust nesting stage is not built in.
maxEngines
string[]
The async tree-search tier — ["max"] where it is enabled, [] otherwise. Health never advertises a capability it cannot serve.
uptimeSec
integer
Whole seconds since process start.
Limite
2,000 piese per cerere (cantitate totală, după expandarea qty)
50 rânduri de material · corpul cererii până la 1 MB
10 chei active per cont — împart O SINGURĂ cotă lunară: cheile separă mediile și integrările, nu adaugă alocare
10 MB corp al cererii pe cele două rute nest care pot transporta un desen (/v1/optimize/nest și /v1/import/nest); un fișier source cel mult 4.000.000 de caractere, 8.000.000 pe cerere
concurența este limitată pe server — rezolvările rulează pe rând, în spatele unei cozi scurte (circa 8 s); un burst peste ea primește 429, iar o cerere refuzată nu este niciodată taxată. Endpoint-urile validate fără cheie au în plus un plafon per adresă (429 cu Retry-After); cu o cheie validă nu ești niciodată limitat astfel. O adresă care trimite peste 30 de chei API necunoscute într-un minut primește 429 pentru restul acelui minut, înainte de orice verificare a cheii. Un cont poate avea 5 lucrări max în queued/running simultan.
Specificația OpenAPI
Un document OpenAPI 3.1, procesabil automat, descrie toate cele douăsprezece endpointuri, fiecare corp de cerere, fiecare structură de răspuns și fiecare eroare. Îndreaptă generatorul tău de client către el, în loc să transcrii această pagină. Documentul în sine este doar în engleză: este alcătuit din tokenuri de contract, iar OpenAPI nu are niciun mecanism de localizare.
A two-page summary of the Engine API — the four modes (2D, 1D, wood and true-shape nest), a request and response, determinism and pricing. Print-ready, with a QR back to the docs.