optimizacija drva — 1D s usklađivanjem poprečnog presjeka
POST
/v1/optimize/nest
true-shape ugnježđivanje — nepravilni poligoni na fiksnim pločama, sa zonama isključenja (laser / plazma / vodeni mlaz)
POST
/v1/validate/2d
validirajte zahtjev bez rješavanja — besplatno, bez ključa, bez kvote (2d / 1d / wood / nest)
POST
/v1/validate/1d
validirajte zahtjev bez rješavanja — besplatno, bez ključa, bez kvote (2d / 1d / wood / nest)
POST
/v1/validate/wood
validirajte zahtjev bez rješavanja — besplatno, bez ključa, bez kvote (2d / 1d / wood / nest)
POST
/v1/validate/nest
validirajte zahtjev bez rješavanja — besplatno, bez ključa, bez kvote (2d / 1d / wood / nest)
GET
/v1/jobs/{id}
ispitajte asinkroni posao max motora — vraća njegov status i, kada završi, plan (bez kvote; samo vlastiti poslovi)
POST
/v1/import/nest
čita obrise dijelova iz SVG ili DXF datoteke — traži ključ, ne troši kvotu
GET
/v1/usage
potrošnja i kvota RAČUNA u tekućem mjesecu (svi ključevi dijele jednu)
GET
/v1/health
liveness — bez ključa, bez ograničenja brzine, bez baze podataka
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.
Jedinice
API je neovisan o jedinicama. Odaberite jednu jedinicu — milimetre, inče, bilo što — koristite je za svaki broj koji šaljete, i svaki broj koji dobijete natrag u istoj je jedinici. Ništa se ne pretvara na strani poslužitelja i nijedan naziv polja ne određuje jedinicu.
To obuhvaća dimenzije dijelova i zalihe, kerf, tolerance, trim i minOffcut na ulazu, te svaku koordinatu, poziciju, preostalu duljinu, ostatak i duljinu reza na izlazu. Miješanje jedinica unutar jednog zahtjeva daje plan koji prolazi validaciju, a fizički je pogrešan — i poslužitelj to ne može otkriti.
Koordinatni sustav
Ishodište je gornji lijevi kut ploče: x raste udesno duž širine ploče, y raste prema dolje duž visine ploče. x i y dijela njegov su gornji lijevi kut, a w i h dimenzije su kako je postavljen — već zamijenjene kada je rotated true — tako da je pravokutnik x, y, w, h otisak na ploči bez daljnjeg računanja. Pravokutnici ostataka koriste isti sustav.
Obrez pomiče postavljanja: trim.left pomiče svaki dio udesno, a trim.top pomiče svaki dio prema dolje, jer se dijelovi ugnježđuju unutar iskoristive površine, a zatim se ponovno pomiču natrag na cijelu ploču. trim.right i trim.bottom smanjuju iskoristivu površinu bez pomicanja ishodišta. w i h ploče uvijek su pune dimenzije zalihe, uključujući obrez — zbog čega se obrez u yieldPct broji kao otpad.
zadano 1 — proširuje se na strani poslužitelja; broji se prema ograničenju od 2,000
name
neobavezna oznaka, ponovljena pri svakom postavljanju
rotatable
zadano true — smije li se dio zakrenuti za 90°
grainGroup
članovi grupe zadržavaju se na jednoj ploči (usklađivanje žice)
priority
obavezan rez: dobiva prostor na ploči kada je zaliha ograničena (uz respectStock)
edgeBanding
kantiranje po strani: imenujte referencu tipa na bilo kojem od top / right / bottom / left (slobodan niz, vaš vlastiti kod) — odgovor zbraja metre po referenci. Samo metapodatak, nikada ne pomiče dio. Samo 2D
material
oznaka materijala (slobodan niz, vaš vlastiti kod): dijelovi i zaliha istog materijala pakiraju se samo zajedno. Za razliku od edgeBanding, mijenja raspored. Izostanak = jedan neodređeni skup. Radi u svakom načinu
Polja zalihe
w i h su obavezni. qty je zadano 1 i čvrsto je ograničenje samo uz respectStock. price je po ploči i pokreće totalPrice i način troška. priority (boolean) troši ovu zalihu prvu; material (slobodan niz) ograničava je na dijelove istog materijala.
Opcije
kerf
širina lista pile (zadano 0)
tolerance
prihvaćaj rezove koji prekoračuju do ove vrijednosti
rangiraj kandidate prema najnižoj ukupnoj cijeni zalihe; uz više dimenzija zalihe s cijenom kombinira ih (2D) ili bira najjeftiniju duljinu (1D) kako bi smanjio račun, čak i ako se time troši više materijala
respectStock
tretiraj qty svakog retka zalihe kao čvrsto ograničenje
minOffcut
prijavljuj samo ostatke čija je kratka strana barem ova
maxCutStages
ograničenje faza pile za ploče — broj faza, a ne sirova dubina stabla
minimizeRotations
preferiraj rasporede koji zakreću manje dijelova
effort
'fast' | 'balanced' (zadano 'balanced'). Dubina pretrage: 'balanced' izvodi potpuni best-of s više strategija; 'fast' preskače jedno skupo pretraživanje kombinacija površina po ploči — znatno brži na velikim zadacima uz cijenu nekoliko bodova iskoristivosti, i dalje giljotinski i nikad gušći od 'balanced'. Na malim zadacima obično je identično. Samo motor heuristic
include radi dvije stvari. SUŽAVANJE — "cutPlan" i "offcuts" uključeni su prema zadanom; prisutan niz zadržava samo tokene sužavanja koje navodi (prazan niz izbacuje oba). ADITIVNI IZVOZ — "svg", "csv" i "dxf" svaki dodaje taj izvoz u odgovor kao STRING: svg samostalni 2D crtež rasporeda (samo 2D — 1d/wood zahtjev umjesto toga vraća upozorenje), dxf R12/AC1009 crtež na slojevima STOCK/PARTS/LABELS, csv popis rezanja. Tokeni izvoza ne utječu na sužavanje, pa include:["svg"] dodaje svg i — ne imenujući nijedan token sužavanja — izbacuje cutPlan/offcuts; koristite ["cutPlan","offcuts","svg"] da zadržite sve i dodate 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.
Opcija effort odmjerava vrijeme izračuna naspram iskoristivosti. Evo tog kompromisa, izmjerenog na jednom zahtjevnom zadatku — svaka brojka dolazi iz stvarnog packera.
Na većini (manjih) zadataka njih dva su identična; razlika se otvara tek na velikim zadacima poput ovoga. balanced je zadana vrijednost i nikada nije gušći nego što fast može doseći.
I ionako je brzo: čak i najveći proizvodni poslovi — 2.000 dijelova i više — rješavaju se u nekoliko sekundi na zadanom motoru, ugodno unutar vremenskog proračuna API-ja.
cutLines vs sawPasses — cutLines spaja kolinearne rezove (jedna postavka graničnika); sawPasses broji svaki prolaz. Dvije poštene mjere istog plana, a ne tvrdnja da se poklapaju s brojem bilo kojeg konkurenta.
guillotineValid / cutPlan — Kada se raspored ne može izrezati od ruba do ruba, guillotineValid je false, a cutPlan je null. To je stvarna informacija — ne može se izraditi na pili za ploče — a ne greška.
unplaced + warnings — Neizvediv zadatak vraća 200 s komadima navedenima u unplaced i napomenom u warnings. Plan po kojem možete djelovati vrijedi više od statusnog koda.
edgeBanding — Kada bilo koji dio nosi edgeBanding, odgovor dodaje edgeBanding blok: linearne metre koje svaka referenca tipa troši, po dijelu i kao ukupno za narudžbu. To je točna geometrija bez dodatka za otpad — radionica ga dodaje sama — i pretpostavlja unos u milimetrima (÷1000 za metre). Ključ potpuno izostaje za zadatak bez kantiranja.
materials / unmatchedMaterials — Kada bilo koji dio ili zaliha nosi material, odgovor dodaje materials (zbroj po materijalu — material, sheetCount/rodCount, yieldPct, placed, total, totalPrice) i unmatchedMaterials (potražnja čiji material nema odgovarajuću zalihu). Kod drva material umjesto toga ide po odjeljku poprečnog presjeka. Oba ključa izostaju za zadatak bez materijala, koji ostaje bajt-istovjetan.
Polja odgovora
Nazivi i tipovi polja su ugovor, pa donje tablice ostaju na engleskom u svakom jeziku — prevedeni naziv polja dokumentirao bi API koji ne postoji.
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
Kada bilo koji dio nosi edgeBanding, odgovor dodaje edgeBanding blok: linearne metre koje svaka referenca tipa troši, po dijelu i kao ukupno za narudžbu. To je točna geometrija bez dodatka za otpad — radionica ga dodaje sama — i pretpostavlja unos u milimetrima (÷1000 za metre). Ključ potpuno izostaje za zadatak bez kantiranja.
Kada bilo koji dio ili zaliha nosi material, odgovor dodaje materials (zbroj po materijalu — material, sheetCount/rodCount, yieldPct, placed, total, totalPrice) i unmatchedMaterials (potražnja čiji material nema odgovarajuću zalihu). Kod drva material umjesto toga ide po odjeljku poprečnog presjeka. Oba ključa izostaju za zadatak bez materijala, koji ostaje bajt-istovjetan.
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 — što jedan step fizički znači
step je jedno kretanje lista pile, a popis je poredan kako zapravo možete piliti: nadređeni rez prije rezova unutar komada koji je proizveo, jer ne možete poprečno rezati traku prije nego što ste je uzdužno odvojili. axis "h" znači da list pile putuje duž x i odvaja gornje od donjeg; axis "v" znači da putuje duž y i odvaja lijevo od desnog. pos je rub lista pile s NAJNIŽOM koordinatom — vrijednost y za "h", vrijednost x za "v" — a ne njegova središnja linija: kerf zauzima od pos do pos + kerf, pa list pile jede u smjeru u kojem koordinata raste, što je prema dolje za "h" i udesno za "v". Materijal na niskoj strani linije — iznad nje za "h", lijevo od nje za "v" — komad je koji taj rez oslobađa. length je koliko daleko list pile putuje pri tom jednom rezu: opseg područja koje prelazi, a ne širina cijele ploče.
stage je prolaz stroja. Počinje od 1 i povećava se samo kada axis promijeni smjer u odnosu na nadređeni rez, pa je uzdužno rezanje ploče u šest traka jedna faza, a njihovo poprečno rezanje sljedeća. To je smisao „trostupanjskog rezanja“ kod pile za ploče, a ne dubina stabla rezanja, i upravo to ograničava maxCutStages. sheet je indeks u sheets s bazom 0, a step se ponovno pokreće od 1 na svakoj ploči umjesto da teče kroz cijeli zadatak.
cutPlan je null — ne nedostajuće, ne prazno — kad god je guillotineValid false: raspored koji se ne može izrezati od ruba do ruba nema slijed rezanja za vratiti. Potpuno izostaje iz sadržaja ako ste ga izostavili iz include.
Dijelovi uzimaju length (uz qty, name, priority); zaliha uzima length, qty i price. I dijelovi i zaliha također prihvaćaju opcionalnu oznaku material (zaliha i priority) — material ograničava dio na zalihu istog materijala, a odgovor tada dodaje materials i unmatchedMaterials kao u 2D-u. Opcije su kerf, tolerance, trim.start / trim.end, minimizeCost, respectStock i minOffcut. Odgovor vraća rods umjesto sheets, svaki sa svojim dijelovima, preostalom duljinom i ostacima.
rods zamjenjuje sheets i nema guillotineValid, jer je linearni rez uvijek izvediv. pos svakog dijela je pomak njegovog bližeg kraja od onog kraja šipke koji trim.start reže, pa prvi dio počinje točno na trim.start, a svaki sljedeći pos dodaje jedan kerf. remaining je ISKORISTIVI ostatak: kerf reza koji ga oslobađa od posljednjeg komada već je odbijen, pa je to duljina koja se može ponovno iskoristiti, a ne sirovi razmak. Prijavljuje se na svakoj šipki čak i kad je ispod minOffcut — minOffcut filtrira samo polje offcuts, koje sadrži najviše jedan unos. U planu rezanja, sheet je indeks šipke, axis je uvijek "v", stage je uvijek 1, a length je uvijek 0: poprečni rez šipke nema udaljenost putovanja za prijaviti, zbog čega 1D metrike nose cuts, ali ne i cutLength.
Drvo ima identitet koji obična šipka nema: dio 50×150 ne može izaći iz zalihe 50×100, koliko god duljine preostalo. Dijelovi i zaliha stoga nose sw i sh, dvije strane poprečnog presjeka, bilo kojim redom — 50×100 i 100×50 ista su okrenuta greda i usklađuju se kao jedan presjek. Zadatak se dijeli prema poprečnom presjeku, svaki se presjek usklađuje s vlastitom zalihom i rješava zasebno, a jedan poziv vraća cjelinu. Dijelovi i zaliha također prihvaćaju opcionalnu oznaku material (zaliha i priority): s njom, hrast 50×100 i bor 50×100 postaju dva zasebna odjeljka, a svaki odjeljak nosi svoj materijal. Opcije su iste kao kod 1D.
sections zamjenjuje rods na najvišoj razini: svaki unos je jedan poprečni presjek s vlastitim rods (identičnog oblika kao 1D) i vlastitim metrics, pa su brojke po materijalu tu bez ponovnog izračuna. unmatched nema ekvivalent u 1D — to je potražnja za čiji poprečni presjek uopće niste dali zalihu, što je drugačiji problem od unplaced (dijelovi koji su imali zalihu i nisu stali) i ima drugačije rješenje, pa se to dvoje nikada ne miješa. metrics.total broji svaki komad koji ste tražili, uključujući unmatched. U planu rezanja svaki step također imenuje svoj section, a sheet je indeks šipke UNUTAR tog presjeka, a ne brojač za cijeli zadatak.
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.
Tri gornja načina rada pakiraju pravokutnike. POST /v1/optimize/nest pakira PROIZVOLJNE POLIGONE: dio je obris (polygon, s neobaveznim unutarnjim holes), a ne širina×visina, pa se dijelovi uklapaju u konkavne džepove jedan drugoga i zrak u urezu koji granični okvir troši ponovno se iskorištava — na reprezentativnom zadatku, 6 ploča ondje gdje isti dijelovi prema graničnom okviru trebaju 9. To je drugačija klasa algoritma (geometrijski motor kolizija, a ne giljotinski pakirač), za rezanje laserom, plazmom i vodenim mlazom. Uz to dolaze dvije stvari koje pravokutni API ne može izraziti: zone isključenja po ploči (stock[].exclusions — defekt, otisak stezaljke, prethodno otisnuto područje; zona s quality 0 zabranjeno je područje za bilo koji dio) i true-shape rupe. material particija i prosljeđivanje meta rade kao i svugdje drugdje. Primjer u nastavku jedan je stvarni zabilježeni poziv — osam dijelova na jednoj ploči s isključenim oštećenim kutom.
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, instant) or "sparrow" (advertised for a future higher-density build; currently served by lbf with a warning).
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: wins sheet space when stock is capped (options.respectStock).
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, for cost mode + totalPrice.
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.
seed
integer
Determinism: a fixed seed → a reproducible layout. Omit and the server pins a fixed default so the response stays reproducible.
minimizeCost
boolean
Rank plans by total sheet price rather than sheet count.
respectStock
boolean
Treat each stock qty as a hard cap.
simplifyTolerance
number
Polygon simplification tolerance (max area deviation as a fraction). Speeds up dense DXF outlines with hundreds of vertices. 0 disables.
timeBudgetMs
integer
Wall-clock budget for the metaheuristic (engine "sparrow"). Ignored by "lbf" (single-pass).
Svaki unos u sheets jedna je iskorištena ploča; postavljeni dio nosi kruti transform (rotation u stupnjevima, zatim x/y translaciju), a NE ponovno ispisani poligon — zakrenite svoj ulazni obris za rotation oko njegovog ishodišta i dodajte (x, y) da točno rekonstruirate postavljanje. rotation može biti negativan; rekonstrukcija je točna bez obzira na predznak. ⚠️ density je površina postavljenog POLIGONA naspram površine iskorištene ploče — pošteno popunjenje, uz konkavne džepove brojene kao prazne — i NIJE usporediva s yieldPct pravokutnog pakirača (koji svaki granični okvir broji kao pun, pa čita više za lošiji rezultat); usporediva metrika između njih dvije jest sheetCount na istim dijelovima. Raspored je determinističan: postavite options.seed da ga reproducirate. exclusions se ponavlja na svakoj ploči za prikaz.
POST /v1/optimize/nest — top level
engine
string
Which nesting engine ran: "lbf" or "sparrow".
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
Always true — guaranteed by the pinned seed.
sheets
array
One entry per used sheet.
metrics
object
Job totals (see below).
unplaced
array
Demand that could not be placed — a plan-plus-warning, not an error.
warnings
string[]
e.g. an engine substitution ("sparrow" served by "lbf"), unplaced parts, or a material with no matching stock.
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.
polygon
number[][]
Present for arbitrary-outline sheets instead of w/h.
price
number | null
The stock row's price, or null.
density
number
This sheet's fill = placed polygon area / sheet area.
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.
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.
Dijelovi iz datoteke (SVG · DXF)
Dio ne mora stići kao koordinate. Stavite SVG ili DXF dokument u parts[].source i poslužitelj iz njega izvlači obris — i njegove rupe — istim čitačem koji CutOptim aplikacija koristi kad crtež ispustite na njezin Nesting način. Datoteka zamjenjuje SAMO geometriju: qty, material, allowedRotations, minQuality, priority i meta ponašaju se točno kao na polygon dijelu, pa knjižnica dijelova koja već postoji kao CAD datoteke ne traži vlastito rastavljanje krivulja i lukova. Jedan source opisuje JEDAN dio; crtež s više odvojenih komponenti vraća 400 i upućuje na uvozni endpoint niže. Odgovor tada nosi imported blok: koliko je redaka došlo iz datoteke, koliko su vrhova proizveli i koje su jedinice te datoteke deklarirale — prijavljeno, nikad primijenjeno, jer ovaj API ništa ne pretvara.
Ništa se ne pohranjuje. Bajtovi postoje isključivo kao tijelo zahtjeva, obrađuju se u memoriji i nestaju kad se odgovor ispiše: bez diska, bez baze podataka, bez privremene datoteke, bez zapisa u dnevniku. Poslije nema što obrisati i ništa ne ostaje — ista bezstanjnost koju drži svaki drugi endpoint.
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 — jedna datoteka, svi obrisi
Kad jedan crtež sadrži više različitih dijelova, prvo ga uvezite: ovaj endpoint vraća svaki zatvoreni obris koji sadrži, počevši od najvećeg, točno u obliku koji redak parts[] očekuje. Zalijepite one koji vam trebaju, dodajte vlastiti qty i material i to pošaljite na /v1/optimize/nest. To je i način da vidite što je u datoteci prije nego na nju potrošite izračun. Traži ključ — rastavljanje proizvoljne geometrije stvaran je posao procesora, a anonimni procesor loša je pogodba — ali ništa ne rezervira: vaša kvota ostaje netaknuta i ne vraćaju se rate limit zaglavlja, jednako kao pri dohvaćanju joba.
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".
Motori
heuristic(zadano) — giljotinski pakirač s više strategija. Najveća iskoristivost, svaki raspored može se izrezati na pili, uvijek potpuni cutPlan.
balanced — MaxRects pakirač sa slobodnim ugnježđivanjem. Znatno brži na velikim zadacima (izmjereno ~25× pri 2.000 dijelova) uz malu cijenu iskoristivosti, a njegovi rasporedi često nisu giljotinski (guillotineValid: false, cutPlan: null). Ne modelira tolerance, minimizeCost, grainGroup, maxCutStages ni minimizeRotations — postavite jedno i upozorenje vam kaže da je zanemareno.
max — asinkroni sloj s pretraživanjem stabla (samo 2D): doseže dokazani optimum na znatno više zadataka po cijenu od nekoliko sekundi do minute po izračunu. I dalje determinističan i giljotinski valjan. Ne vraća plan izravno — vidi Asinkroni poslovi u nastavku. Modelira JEDAN format zaliha u punoj veličini ploče, u neograničenoj količini i s fiksnim 3-faznim giljotinskim uzorkom: drugi redak zaliha, trim, respectStock, material ili grainGroup odbijaju se kodom 400 prije nego što se poziv rezervira; tolerance, minimizeCost, maxCutStages, minimizeRotations, firstCut i effort prolaze, ali se zanemaruju uz upozorenje, a max rezultat ne prijavljuje otpatke. Takve poslove šaljite motoru heuristic.
Asinkroni poslovi (engine = max)
max izračun traje od nekoliko sekundi do minute, pa POST /v1/optimize/2d s engine:"max" ne vraća plan — vraća 202 Accepted s jobId, a poziv se naplaćuje pri predaji. Ispitujte GET /v1/jobs/{id} dok status ne bude "succeeded" (result tada sadrži isti 2D odgovor koji vraća sinkroni izračun) ili "failed" (error sadrži poruku). Ispitivanje ne troši kvotu; vidite samo vlastite poslove. Ondje gdje sloj nije omogućen na instalaciji, engine:"max" odbija se — fail closed — s 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
Always "2d" and "max".
pollAfterMs
integer
202 only — suggested delay before the first poll.
result
object
Present once succeeded — the same shape as a synchronous 2D response.
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.
Determinizam i verzioniranje
Svaki odgovor nosi engineVersion. Algoritam je determinističan, pa njegovo poboljšanje mijenja izlaz za isti ulaz — što je breaking change ako predmemorirate. Fiksirajte ponašanje tako da eksplicitno šaljete engine i pratite engineVersion; verzija putanje /v1/ mijenja se samo ako se promijeni struktura odgovora.
Svaki se motor verzionira neovisno, pa promjena jednog nikada ne pomiče verziju drugog.
Greške
400
invalid_request
Greška sheme. details.path pokazuje na problematično polje.
401
unauthorized
API ključ nedostaje ili je nepoznat.
402
quota_exceeded
Mjesečna kvota dosegnuta. Retry-After daje broj sekundi do prijelaza mjeseca.
403
key_revoked
Ključ postoji, ali se ne smije koristiti: opozvan je ili pretplata računa na Engine API više nije aktivna. Polje message govori o kojem se slučaju radi.
404
not_found
Takva ruta ne postoji — isto dobivate za ispravnu putanju s pogrešnom metodom.
413
too_large
Ulaz iznad ograničenja (vidi Ograničenja).
429
busy
Trenutačno na granici kapaciteta. Retry-After u sekundama — to se nikada ne broji u vašu kvotu.
500
internal
Neočekivana greška ili auth pozadina nije dostupna (zahtjevi se odbijaju — fail closed).
503
service_unavailable
Traženi motor trenutačno se ne može poslužiti — motor za nesting ili asinkroni max motor. Za max postoje dva uzroka, a polje message govori koji: sloj nije ugrađen u ovaj deployment, ili jest ugrađen ali worker koji rješava poslove ne odgovara. Fail-closed prije nego što se poziv rezervira, pa nikad ništa ne košta.
504
solve_timeout
Izračun je premašio svoju čvrstu vremensku granicu. Na pravokutnim rutama nameće je proxy; na /v1/optimize/nest motor nameće vlastiti, kraći proračun i odgovara kodom solve_timeout u uobičajenoj omotnici.
Tijelo greške
Svaka greška koju sam motor proizvede koristi istu omotnicu. Grananje radite prema error, koji je stabilan kod; nikada prema message, čiji se tekst može promijeniti između izdanja. details je prisutan kod invalid_request, gdje path imenuje problematično polje, i kod too_large, gdje max i got daju ograničenje i ono što ste poslali.
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" }
}
402 i 429 oboje nose Retry-After zaglavlje u sekundama. Kod 402 odbrojava do resetiranja kvote u 00:00 UTC prvog dana sljedećeg mjeseca; kod 429 riječ je o kratkom odgađanju, a 429 nikada ne troši kvotu — rezervirani poziv se vraća.
Greška usmjeravanja odgovara s not_found, kodom namjerno izvan gornjeg popisa jer ga proizvodi usmjerivač, a ne API ugovor. Dobivate 404, a ne 405, kada je putanja ispravna, ali je metoda pogrešna: sva četiri endpointa za optimizaciju primaju isključivo POST.
Na pravokutnim rutama 504 dolazi od reverse proxyja, ne od motora, pa je njegovo tijelo proxyjevo, a ne ova JSON omotnica; unutar donjih ograničenja ulaza trebao bi biti nedostižan. Iznimka je /v1/optimize/nest: taj je izračun podproces s vlastitim proračunom, namjerno držanim ispod granice proxyja, pa istek vremena ondje koristi ovu omotnicu, s kodom solve_timeout.
Zaglavlja ograničenja brzine
Uspješan poziv optimizacije nosi X-RateLimit-Limit (mjesečno ograničenje RAČUNA — svi ključevi računa dijele jedno) i X-RateLimit-Remaining (preostali pozivi računa ovaj mjesec, nakon ovoga). Šalju ih samo endpointi za optimizaciju: brojač se rezervira kao dio autorizacije izračuna, pa /v1/usage i /v1/health nemaju što prijaviti.
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.
Samo za čitanje: ne troši poziv i ne šalje zaglavlja ograničenja brzine. ⚠️ used i limit opisuju RAČUN, a ne ključ kojim ste pozvali: svaki aktivni ključ na računu crpi iz jedne zajedničke dozvole, pa stvaranje više ključeva ne stvara više kvote. used broji tekući UTC kalendarski mjesec kroz sve njih, remaining je limit minus used i nikada nije negativan, periodEnd je dan resetiranja kao obični YYYY-MM-DD datum, a keyPrefix je netajni prikazni prefiks ključa kojim ste pozvali. Sam ključ nijedan endpoint nikada ne vraća — pohranjuje se samo njegov hash, pa se izgubljeni ključ zamjenjuje, a ne obnavlja.
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.
Bez ključa, bez kvote, bez baze podataka. Namjerno ne dodiruje ništa sa stanjem, tako da ispad u pohrani ključeva ne može učiniti da usluga izgleda mrtvo orkestratoru. engines navodi id-ove koje ova instalacija prihvaća u engine, a engineVersion je verzija zadanog motora.
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.
Ograničenja
2,000 dijelova po zahtjevu (ukupna količina, nakon proširenja qty)
50 redaka zalihe · tijelo zahtjeva do 1 MB
10 aktivnih ključeva po računu — dijele JEDNU mjesečnu kvotu, pa ključevi razdvajaju okruženja i integracije, ne dodaju dozvolu
10 MB tijela zahtjeva na dvije nest rute koje mogu nositi crtež (/v1/optimize/nest i /v1/import/nest); jedna source datoteka najviše 4.000.000 znakova, 8.000.000 po zahtjevu
konkurentnost je ograničena na poslužitelju — burst dobiva 429, nikad sporu čekaonicu. Validate krajnje točke bez ključa dodatno imaju gornju granicu po adresi (429 s Retry-After); s ključem se nikad tako ne ograničava. Račun može imati 5 max poslova istovremeno u queued/running.
OpenAPI specifikacija
Strojno čitljiv OpenAPI 3.1 dokument opisuje svih dvanaest endpointa, svako tijelo zahtjeva, svaku strukturu odgovora i svaku grešku. Usmjerite svoj generator klijenta na njega umjesto da prepisujete ovu stranicu. Sam dokument je samo na engleskom: sastoji se od ugovornih tokena, a OpenAPI nema mehanizam lokalizacije.
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.