CutWeaver Public API Contract v1¶
This document is the canonical public contract for the hosted HTTP API under /api/v1/....
It is also the reference JSON shape for integrations that call the versioned solve surface through the C API.
Scope¶
Version v1 defines:
- canonical request field names,
- success and error envelopes,
- request and transport limits,
- additive versioning rules for the public service surface.
Request payloads are strict in v1: unknown, legacy, or snake_case aliases are rejected with invalid_schema.
Conventions¶
- All field names are camelCase.
- All linear dimensions are integers in millimetres.
- Areas are square millimetres.
- Request bodies use
application/json. - Every JSON response includes
contractVersion: "v1". - Supported public strategies in
v1aregreedy,alns,alnsStripe,glass, and opt-ingenetic. Hosted default isalns.
Success Envelope¶
{
"ok": true,
"contractVersion": "v1",
"result": { }
}
When strategy is alns, alnsStripe, or glass, the response may also include a top-level alnsStats object.
When includeSvg is true, the response may also include a top-level svg array.
Error Envelope¶
{
"ok": false,
"contractVersion": "v1",
"error": {
"code": "string",
"message": "human-readable"
}
}
For domain validation failures, error.validationErrors is included.
{
"ok": false,
"contractVersion": "v1",
"error": {
"code": "validation_failed",
"message": "Input validation failed",
"validationErrors": [
{
"code": "InvalidDimension",
"fieldPath": "pieces[0]",
"message": "Piece width must be positive"
}
]
}
}
Solve Request¶
Top-level fields¶
| Field | Type | Required | Notes |
|---|---|---|---|
pieces |
array | yes | Array of piece specifications |
sheets |
array | yes | Array of stock sheet specifications |
params |
object | no | Global cutting parameters |
builderConfig |
object | no | Advanced greedy/tree-builder knobs |
strategy |
"greedy" \| "alns" \| "alnsStripe" \| "glass" \| "genetic" |
no | Defaults to "alns" (W4 D3). alns = TreeBuilder/CutNode search; alnsStripe = stripe portfolio (stripeObjective default dual); glass = parallel greedy+alns+alnsStripe+genetic pick (opt-in, not hosted default); greedy = low-latency opt-in; genetic = opt-in, not hosted default |
alnsConfig |
object | no | Parsed when present; used for strategy = "alns", "alnsStripe", or "glass" |
includeSvg |
boolean | no | If true, response includes top-level svg array |
Legacy roots such as layout, production_params, and config are not supported in v1.
pieces[]¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
originalIndex |
int | no | array index | Returned in pieceId and unplacedIndices |
width |
int | yes | - | Piece width |
height |
int | yes | - | Piece height |
quantity |
int | no | 1 |
Number of identical pieces |
canRotate |
bool | no | true |
Piece-level rotation permission |
priority |
int | no | 0 |
Higher priority increases penalty when unplaced |
sheets[]¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
width |
int | yes | - | Sheet width |
height |
int | yes | - | Sheet height |
kerfWidth |
int | no | 0 |
Только 0 (no-op echo). Non-zero → invalid_schema; канон — params.kerfWidth |
marginLeft |
int | no | 0 |
Канон — запас слева |
marginRight |
int | no | 0 |
Канон — запас справа |
marginTop |
int | no | 0 |
Канон — запас сверху |
marginBottom |
int | no | 0 |
Канон — запас снизу |
quantity |
int | no | 1 |
Expanded into repeated concrete sheets by the input layer |
defects |
array | no | [] |
Rectangular forbidden zones |
Each defects[] entry uses:
| Field | Type | Required | Notes |
|---|---|---|---|
x |
int | yes | Defect origin X |
y |
int | yes | Defect origin Y |
width |
int | yes | Defect width |
height |
int | yes | Defect height |
params¶
If omitted, the input layer uses the default glass profile.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
maxStages |
int | no | see note | 0 = unlimited. If the whole params object is omitted, glass defaults apply (maxStages=5). If params is present but maxStages is omitted, boundary keeps unlimited (0) (F3-06b). |
alternateOrientation |
bool | no | true |
Alternate cut direction by stage |
firstCutVertical |
bool | no | true |
First cut direction |
kerfWidth |
int | no | 0 |
Канон — глобальный kerf для engine |
minTrimSize |
int | no | 0 |
Minimum accepted trim fragment |
minReusableWidth |
int | no | 0 |
Min W for reusable offcut (OGD MinOffCut); 0 = off |
minReusableHeight |
int | no | 0 |
Min H for reusable offcut; orientation-invariant with width |
marginLeft |
int | no | 0 |
Только 0 (no-op echo). Non-zero params.margin* → invalid_schema; канон — sheets[].margin* |
marginRight |
int | no | 0 |
Только 0 (no-op echo); non-zero → invalid_schema |
marginTop |
int | no | 0 |
Только 0 (no-op echo); non-zero → invalid_schema |
marginBottom |
int | no | 0 |
Только 0 (no-op echo); non-zero → invalid_schema |
allowRotation |
bool | no | true |
Global rotation permission |
params must not contain ALNS fields. For example, params.strategy, params.timeLimitMs, and params.threadCount are rejected.
Канон kerf / margins (v1)¶
В JSON есть dual fields. Семантика движка:
| Что | Канонический вход | Заметки |
|---|---|---|
| Kerf | params.kerfWidth |
sheets[].kerfWidth только 0 (echo); non-zero → invalid_schema |
| Margins | sheets[].marginLeft/Right/Top/Bottom |
params.margin* только 0 (echo); non-zero → invalid_schema |
Отклонённый alias: params.kerf (только params.kerfWidth). Только camelCase.
defects и strategies¶
sheets[].defects are honored on Greedy and ALNS paths (TreeBuilder / partialRebuild / TreeRepair). See BACKLOG_ALNS_DEFECTMAP.md.
builderConfig¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
beamWidth |
int | no | 10 |
Beam width per level |
beamWidthAtRoot |
int | no | 50 |
Beam width on the root level |
maxBuildCalls |
int | no | 0 |
0 means adaptive budget |
maxRecursionDepth |
int | no | 500 |
Safety limit for tree depth |
scoreNoise |
double | no | 0.0 |
Score diversification noise |
noiseSeed |
uint64 | no | 0 |
Seed for builder noise |
preferScarceStockFirst |
bool | no | true |
Try a remnant-first candidate; production lex may still keep a cheaper standard map |
scarceMinUtilization |
double | no | 0.90 |
Rollback remnant maps below this utilization |
consumeRemnantsFirst |
bool | no | true |
Shop / OffCut-first (2026-09-16 product): pack unused remnants before opening a new standard, even if that uses more sheets. No-op when stock has no remnants. Explicit false restores pre-shop collapse-to-standard. Applies to omit-builderConfig hosted alns / greedy / glass payload parse. strategy=genetic still forces OffCut when stock has a remnant via applyOgdOffcutListIfRemnants (cannot fully turn that phase off by omitting the key). Console checkbox defaults on and always sends the flag. |
absorbTrailingSparse |
bool | no | true |
Genetic / glass genetic clone. After layout, pull a sparse last standard into already-open sheets (GE-70/75). false disables that post-pack compressor (PLAN_NOW item 1 probe). Ignored by greedy/alns Solver. |
alnsConfig¶
All fields are optional. The public v1 parser accepts the following keys:
| Field | Type | Notes |
|---|---|---|
maxIterations |
int | Maximum number of ALNS iterations. Small console values (40 / 250) are raised when the wall-fill policy applies (see timeLimitMs). |
maxNoImprove |
int | Stop after N iterations without best improvement. Raised together with maxIterations under wall-fill so a 200-iter plateau does not abort a 20–40 s wall. |
timeLimitMs |
double | Time budget in milliseconds (cap). For strategy=glass with timeLimitMs ≥ 5000, and for C-API alns / alnsStripe / genetic with timeLimitMs ≥ 20000, small maxIterations / maxNoImprove are raised so the wall is the stop. Hosted omit-config alns at 10000 ms is unchanged. |
minAlnsLoopBudgetMs |
double | ALNS loop floor after greedy. <0 auto (clamp 20% of timeLimit into [50,2000]), 0 disable (legacy early-return), >0 explicit ms |
numThreads |
int | 1 = single-threaded, 0 = hardware concurrency |
fullRebuildProbability |
double | Probability of full rebuild instead of partial repair |
earlyStopThreshold |
double | Multi-thread early-stop threshold |
earlyStopMinIter |
int | Earliest iteration for multi-thread early-stop |
coolingRate |
double | Simulated annealing cooling factor |
initialAcceptProb |
double | Initial probability for accepting worse candidates |
minTemperature |
double | Lower temperature floor |
minDestructionRate |
double | Minimum destruction fraction |
maxDestructionRate |
double | Maximum destruction fraction |
scoreNoise |
double | ALNS diversification noise |
seed |
int64 | Random seed for deterministic runs |
unplacedPenalty |
int64 | Cost penalty for unplaced pieces |
priorityPenalty |
int64 | Additional penalty for unplaced priority |
cutPenalty |
int64 | Penalty per cut |
directionChangePenalty |
int64 | Penalty for cut-direction changes |
liquidOffcutBonus |
double | Bonus for reusable offcuts |
reheatingInterval |
int | Stagnation interval before reheating |
reheatingFactor |
double | Temperature multiplier during reheating |
maxReheats |
int | Maximum reheating events |
returnToBestAfter |
int | Stagnation threshold before return-to-best |
returnToBestProb |
double | Probability of return-to-best |
weightDecay |
double | Operator-weight EMA decay |
initialOperatorWeight |
double | Initial weight of destroy operators |
minOperatorWeight |
double | Lower bound for operator weight |
maxNoBestImproveMs |
double | Optional time-based early stop |
maxInterSheetTransfersPerIter |
int | Local-search transfer attempts |
maxTreeOperatorsPerIter |
int | Tree-operator attempts |
repairStrategy |
string | adaptive (default roulette), fullRebuild, treeNative, or treeNativeRegret. Non-adaptive values force that repair operator each iteration (ALNSEngine, 2026-08-04). |
stripeObjective |
string | alnsStripe only. dual (default) | nesting | packing. Dual keeps DumpMaps/L1-oriented primary in result and packing in alternatives[] when packing uses fewer sheets. Unknown values → invalid_schema. glass always runs dual internally and ignores this field. |
enableStripeResearchOps |
bool | alnsStripe only. Default false (product thin). true enables research neighbourhoods (compress / emptySheet / nestCompress / qualitySeed). |
Successful Result Payload¶
result¶
| Field | Type | Notes |
|---|---|---|
sheetsUsed |
int | Number of concrete sheets used |
totalPlaced |
int | Number of placed piece instances |
totalUsedArea |
int64 | Sum of placed piece area |
totalWasteArea |
int64 | Sum of waste area |
totalUnplacedPriority |
int64 | Total priority penalty of unplaced pieces |
unplacedIndices |
array | originalIndex values that were not placed |
sheets |
array | Per-sheet result objects |
result.sheets[]¶
| Field | Type | Notes |
|---|---|---|
sheetIndex |
int | Index of the concrete sheet instance |
placedCount |
int | Number of placed pieces on this sheet |
usedArea |
int64 | Used area on this sheet |
wasteArea |
int64 | Waste area on this sheet |
largestOffcut |
int64 | Largest remaining offcut area |
topOffcuts |
array | Top-N waste leaves by area↓ (max 8); {width,height,area}; derived from cut tree; empty if no tree |
maxDepth |
int | Maximum depth of the cut tree |
sheetRotated |
bool | Whether the sheet was rotated |
utilization |
double | Used area divided by sheet area |
placements |
array | Flattened piece placements |
cutTree |
object or null | Nested cut tree for normal-depth cases |
cutTreeFlat |
array | Flat cut-tree fallback for deep trees |
result.sheets[].placements[]¶
| Field | Type | Notes |
|---|---|---|
pieceId |
int | originalIndex of the placed piece |
width |
int | Final placed width |
height |
int | Final placed height |
x |
int | Placement origin X |
y |
int | Placement origin Y |
rotated |
bool | Whether the piece was rotated |
result.sheets[].cutTree¶
cutTree is a nested object when the tree depth is within the safe JSON limit.
| Field | Type | Notes |
|---|---|---|
type |
string | cut, piece, or waste |
x |
int | Absolute X coordinate of the node region |
y |
int | Absolute Y coordinate of the node region |
width |
int | Region width |
height |
int | Region height |
direction |
string | H or V |
stage |
int | Stage number |
depth |
int | Tree depth |
kerfSize |
int | Kerf consumed by this split |
piece |
object or null | Piece placement object for type = piece |
child |
object or null | First child region |
next |
object or null | Sibling region after the cut |
When piece is present inside cutTree, it uses:
| Field | Type | Notes |
|---|---|---|
pieceId |
int | originalIndex |
width |
int | Final width |
height |
int | Final height |
x |
int | Placement X |
y |
int | Placement Y |
rotated |
bool | Rotation flag |
priority |
int | Piece priority |
result.sheets[].cutTreeFlat[]¶
cutTreeFlat is used when the nested tree would become too deep for safe JSON serialization.
| Field | Type | Notes |
|---|---|---|
type |
string | cut, piece, or waste |
width |
int | Region width |
height |
int | Region height |
direction |
string | H or V |
stage |
int | Stage number |
depth |
int | Tree depth |
kerfSize |
int | Kerf consumed by this split |
piece |
object or null | Piece payload identical to cutTree.piece |
childIndex |
int | Index of the child node in the same array |
nextIndex |
int | Index of the sibling node in the same array |
Optional Top-level Extensions¶
alnsStats¶
Returned only when the request uses strategy: "alns", "alnsStripe", or "glass".
| Field | Type | Notes |
|---|---|---|
iterations |
int | Iterations completed |
improvements |
int | Number of best-solution improvements |
elapsedMs |
double | Wall-clock solve time |
stripeObjective |
string | alnsStripe / glass. Echo of requested nesting | packing | dual. glass always "dual". |
selectedObjective |
string | alnsStripe / glass. Objective of top-level result: nesting or packing |
portfolioWinner |
string | glass only. Packing clone that won production lex: greedy | alns | alnsStripe | genetic |
portfolioNesting |
string | glass only. Nesting source: alnsStripe or the packing clone |
portfolioClones |
array | glass only. Always ["greedy","alns","alnsStripe","genetic"] |
Phase budget fields (greedyPhaseMs, tailBudgetMs, tailActualMs, alnsLoopBudgetMs) may also appear for observability.
alternatives¶
Returned for strategy: "alnsStripe" with alnsConfig.stripeObjective: "dual", and for strategy: "glass", only when a packing candidate uses fewer sheets than the nesting primary.
| Field | Type | Notes |
|---|---|---|
alternatives |
array | Usually 0–1 entries |
alternatives[].objective |
string | packing (today) |
alternatives[].sheetsUsed |
int | Sheet count of the alternative |
alternatives[].result |
object | Same shape as top-level result |
Primary result for alnsStripe dual remains the nesting (L1-safe) solution unless stripeObjective is packing.
For glass, primary is nesting unless the packing clone is strictly better on production lex (unplaced → newStandardSheetsUsed → sheetsUsed). Remnant + N standards at the same sheet count is exported as packing, not as dual all-standard nesting.
svg¶
Returned only when includeSvg is true.
| Field | Type | Notes |
|---|---|---|
svg |
string array | One SVG document per returned sheet, in sheet order |
Error Codes¶
| Code | Meaning |
|---|---|
invalid_json |
Request body is not valid JSON |
invalid_schema |
Unknown field, wrong type, or unsupported alias |
validation_failed |
Domain validation failed after parsing |
payload_too_large |
Request exceeds MAX_REQUEST_BYTES |
too_many_items |
pieces or sheets exceed MAX_ARRAY_ITEMS |
invalid_email |
POST /api/v1/request-key email is malformed |
unsupported_plan |
Public key issuance requested a non-free plan |
api_key_required |
Endpoint requires X-API-Key |
invalid_api_key |
API key is unknown or inactive |
unsupported_media_type |
Request is not application/json |
rate_limit_exceeded |
Daily or monthly hosted quota has been exhausted |
not_found |
HTTP route does not exist |
internal_error |
Unexpected server-side failure |
Hard Limits¶
| Constant | Value | Notes |
|---|---|---|
MAX_REQUEST_BYTES |
1,048,576 |
Maximum request body size in bytes |
MAX_ARRAY_ITEMS |
10,000 |
Maximum number of entries in pieces or sheets |
Strictness Rules¶
The v1 request parser rejects:
- snake_case aliases such as
alns_config, - legacy roots such as
layout,production_params, andconfig, - unsupported strategy values,
- unknown fields inside canonical objects.
Examples of request fields that are rejected in v1:
rotatableparams.kerfparams.strategyparams.threadCounthpgConfig
Versioning Policy¶
Current public version: v1.
- Additive response fields can be introduced without changing the version.
- New optional request fields require a documented additive change.
- Renaming, removing, or changing the meaning of existing fields requires a new contract version.