API reference
Job Nests
The Job Nests API nests a backlog of sheet-metal parts together on shared sheets: preview what a job nest would take, start one, read each order's and each…
The Job Nests API nests a backlog of sheet-metal parts together on shared sheets: preview what a job nest would take, start one, read each order's and each part's share of the sheets, export its sheets as cutting files (DXF, SVG or the JSON nest plan), and cancel it.
Auto-generated from the public OpenAPI spec — this page never drifts from the running API. Base URL
https://api.arcnm.io. Authenticate with theX-API-Keyheader (see Authentication).
List job nests
GET /api/v1/nest-runs
The job nests your organization started, newest first. The nest an assembly calculation runs for its own sheet components belongs to that calculation and is not listed.
A plain array: the page position travels in the Link,
X-Next-Cursor and X-Has-More response headers.
Paginated. Pass
cursor(from the previous response) to fetch the next page;limitcaps the page size.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
env_id |
query | string | no | Only this environment's job nests. A filter narrows the list: an id that matches no environment of your organization lists nothing. |
cursor |
query | string | no | Opaque position token from the previous page's next_cursor (or the Link / X-Next-Cursor response header). Omit it for the first page. Keep every other query parameter identical for the whole walk — a cursor replayed against different filters is rejected. |
limit |
query | integer | no | Maximum rows to return in one page. |
Request
curl -X GET https://api.arcnm.io/api/v1/nest-runs \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/nest-runs",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs", {
method: "GET",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
},
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
Successful Response |
422 |
Validation Error |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Start a job nest
POST /api/v1/nest-runs
Nest a backlog selection together, sheet by sheet.
Every line is read off its calculation: the part's shape as its
single-part price nested it, the thickness it is bought in, the margin
and spacing, the material and its price. Lines are grouped by
material, thickness and allowances — one nest never mixes them — and
the job nest is queued: poll GET /nest-runs/{run_id} until it ends.
It cuts from the environment's remnants of each material and thickness
before it buys a sheet — a remnant it cuts from leaves stock, and the
reusable rests its sheets leave go in. Nothing here changes a
calculation's price.
One job nest at a time: while one of your organization's is queued or running, a second is refused — wait until it ends, or cancel it.
When the month's runs are already used, the job nest is kept as a
draft: stored, not queued, spending no run. Start it with POST /nest-runs/{run_id}/start once the allowance has a run for it, or
cancel it. Your organization keeps at most 20 drafts.
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
allocation_rule |
area_proportional | equal_per_sheet |
no | How a sheet's cost is shared by the parts on it: by their area, or equally. |
batch_id |
string | no | Nest every calculation of this batch priced in the environment, each at its lot size. |
env_id |
string | yes | The costing environment the run nests in: its stock and remnants. |
folder_id |
string | no | Nest the newest calculation priced in the environment of every part filed in this folder or below it, each at its lot size. |
group_budget_s |
number | no | The longest each material and thickness may be nested for, in seconds of computing time: longer can use less sheet. |
items |
NestRunItemIn[] | no |
Request
curl -X POST https://api.arcnm.io/api/v1/nest-runs \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/nest-runs",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}),
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
201 |
Successful Response |
404 |
The environment, batch or folder is not your organization's (nest_run_selection_not_found). |
409 |
A job nest of your organization is still queued or running (nest_run_in_flight): wait until it ends, or cancel it. Or the month's runs are used and your organization keeps the most drafts it may (nest_run_too_many_drafts): start one or cancel one. |
422 |
An item can't be nested — details.items names each with its reason (nest_run_items_unusable) — or the batch or folder holds nothing that can (nest_run_nothing_to_nest), or more calculations than one run takes (nest_run_selection_too_large), or a line asks for more copies than one line takes (nest_run_line_too_large), or the lines together more than one run takes (nest_run_too_many_copies), or group_budget_s is past what your organization may ask for (nest_run_budget_too_long). |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
409 |
conflict |
A conflicting change, or an Idempotency-Key reused with a different body. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 201
| Field | Type | Description |
|---|---|---|
allocation_rule |
area_proportional | equal_per_sheet |
How each sheet's cost is shared by the parts on it. |
as_of |
string | The day its stock and prices are read at. |
copies |
integer | Copies it nests, over all its lines. |
counted_at |
string | When it was released; null while a simulation. |
counts_toward_savings |
boolean | True once the job nest was released: its stock is booked and what it saves adds to the total saved by nesting. False: a simulation, whose saving is potential only. |
created_at |
string | |
created_by_user_id |
string | |
env_id |
string | The costing environment it nests in. |
error |
string | Why it stopped, when it did not finish. |
finished_at |
string | |
group_budget_s |
number | The longest each material and thickness may be nested for, in seconds of computing time. |
groups_done |
integer | Of those, the ones finished so far: nested, or not nestable. |
groups_total |
integer | Materials and thicknesses it nests — each is nested on its own. |
id |
string | |
lines |
integer | Lines it nests. |
savings |
NestRunSavings | What it saves, once it ends; null before, or where no line compares. |
sheets |
integer | Sheets and remnants the nested materials and thicknesses are cut from; set once the job nest ends. |
started_at |
string | |
status |
draft | queued | running | solved | failed | cancelled |
draft: kept, not queued — it was started while the month's runs were used, so it spends no run until POST /nest-runs/{run_id}/start starts it. queued: waiting to start. running: nesting, several materials and thicknesses at a time. solved: every material and thickness is nested. failed: it stopped, error says why; what it nested before stands. cancelled: stopped on request; what it nested before stands. |
Example response
{
"allocation_rule": "area_proportional",
"as_of": "2026-06-01",
"copies": 0,
"counted_at": "2026-06-01T12:00:00Z",
"counts_toward_savings": false,
"created_at": "2026-06-01T12:00:00Z",
"created_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"error": "string",
"finished_at": "2026-06-01T12:00:00Z",
"group_budget_s": 184,
"groups_done": 0,
"groups_total": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"lines": 0,
"savings": {
"alone": 0,
"alone_kg": 0,
"alone_sheets": 0,
"by_order": [
{
"alone": 0,
"alone_kg": 0,
"alone_sheets": 0,
"copies": 0,
"currency": "EUR",
"lines": 0,
"lines_compared": 0,
"nested": 0,
"nested_kg": 0,
"nested_sheets": 0,
"order_ref": "string",
"rectangular": 0,
"rectangular_kg": 0,
"rectangular_sheets": 0,
"saved": 0,
"saved_combining": 0,
"saved_combining_kg": 0,
"saved_kg": 0,
"saved_true_shape": 0,
"saved_true_shape_kg": 0,
"split_lines": 0
}
],
"copies": 0,
"currency": "EUR",
"lines": 0,
"lines_compared": 0,
"nested": 0,
"nested_kg": 0,
"nested_sheets": 0,
"rectangular": 0,
"rectangular_kg": 0,
"rectangular_sheets": 0,
"saved": 0,
"saved_combining": 0,
"saved_combining_kg": 0,
"saved_kg": 0,
"saved_true_shape": 0,
"saved_true_shape_kg": 0,
"split_lines": 0
},
"sheets": 0,
"started_at": "2026-06-01T12:00:00Z",
"status": "draft"
}
Read a job nest
GET /api/v1/nest-runs/{run_id}
One job nest: each material and thickness with its sheets and utilisation, each line's and each order's share of the sheets, and the remnants it used, holds and created.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string | yes | Identifier of the run. |
Request
curl -X GET https://api.arcnm.io/api/v1/nest-runs/{run_id} \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/nest-runs/{run_id}",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/{run_id}", {
method: "GET",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
},
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
Successful Response |
402 |
An assembly calculation ran this job nest for its own sheet components, and your organization has no active Nesting add-on (addon_required). |
404 |
No run with this id in your organization (nest_run_not_found). |
422 |
Validation Error |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
404 |
not_found |
A referenced resource doesn't exist or isn't visible to your organisation. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 200
| Field | Type | Description |
|---|---|---|
allocation_rule |
area_proportional | equal_per_sheet |
How each sheet's cost is shared by the parts on it. |
as_of |
string | The day its stock and prices are read at. |
copies |
integer | Copies it nests, over all its lines. |
counted_at |
string | When it was released; null while a simulation. |
counts_toward_savings |
boolean | True once the job nest was released: its stock is booked and what it saves adds to the total saved by nesting. False: a simulation, whose saving is potential only. |
created_at |
string | |
created_by_user_id |
string | |
env_id |
string | The costing environment it nests in. |
error |
string | Why it stopped, when it did not finish. |
finished_at |
string | |
group_budget_s |
number | The longest each material and thickness may be nested for, in seconds of computing time. |
groups |
NestRunGroup[] | |
groups_done |
integer | Of those, the ones finished so far: nested, or not nestable. |
groups_total |
integer | Materials and thicknesses it nests — each is nested on its own. |
id |
string | |
items |
NestRunLine[] | |
left_out |
LeftOutLine[] | Calculations of the batch or folder it does not take, each with why. |
lines |
integer | Lines it nests. |
orders |
OrderTotal[] | Each order's lines totalled, once the job nest ends. |
remnants_created |
RemnantPublic[] | The reusable rests its sheets left, now in stock. |
remnants_held |
RemnantPublic[] | Remnants it holds while it runs, kept from other job nests. |
remnants_used |
RemnantPublic[] | Remnants from stock its sheets were cut from. |
savings |
NestRunSavings | What it saves, once it ends; null before, or where no line compares. |
sheets |
integer | Sheets and remnants the nested materials and thicknesses are cut from; set once the job nest ends. |
started_at |
string | |
status |
draft | queued | running | solved | failed | cancelled |
draft: kept, not queued — it was started while the month's runs were used, so it spends no run until POST /nest-runs/{run_id}/start starts it. queued: waiting to start. running: nesting, several materials and thicknesses at a time. solved: every material and thickness is nested. failed: it stopped, error says why; what it nested before stands. cancelled: stopped on request; what it nested before stands. |
Example response
{
"allocation_rule": "area_proportional",
"as_of": "2026-06-01",
"copies": 0,
"counted_at": "2026-06-01T12:00:00Z",
"counts_toward_savings": false,
"created_at": "2026-06-01T12:00:00Z",
"created_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"error": "string",
"finished_at": "2026-06-01T12:00:00Z",
"group_budget_s": 184,
"groups": [
{
"active_revision": 0,
"error": "string",
"finished_at": "2026-06-01T12:00:00Z",
"gauge_mm": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"material_category": "string",
"material_grade_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"position": 0,
"revisions": 0,
"sheets": 0,
"started_at": "2026-06-01T12:00:00Z",
"status": "pending",
"utilisation_net": 0
}
],
"groups_done": 0,
"groups_total": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"items": [
{
"allocation": {
"copies": 0,
"currency": "EUR",
"net": 0,
"net_per_copy": 0,
"purchased": 0,
"remnant_credit": 0,
"scrap_credit": 0
},
"calculation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"due": "2026-06-01",
"group_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"order_ref": "string",
"position": 0,
"priority": 0,
"quantity": 0
}
],
"left_out": [
{
"calculation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
}
],
"lines": 0,
"orders": [
{
"copies": 0,
"currency": "EUR",
"net": 0,
"net_per_copy": 0,
"order_ref": "string",
"purchased": 0,
"remnant_credit": 0,
"scrap_credit": 0
}
],
"remnants_created": [
{
"consumed_at": "2026-06-01T12:00:00Z",
"created_at": "2026-06-01T12:00:00Z",
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"gauge_mm": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"length_mm": 0,
"material_category": "string",
"material_grade_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"origin_group_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"quantity": 0,
"reserved_by_run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "available",
"width_mm": 0
}
],
"remnants_held": [
{
"consumed_at": "2026-06-01T12:00:00Z",
"created_at": "2026-06-01T12:00:00Z",
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"gauge_mm": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"length_mm": 0,
"material_category": "string",
"material_grade_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"origin_group_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"quantity": 0,
"reserved_by_run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "available",
"width_mm": 0
}
],
"remnants_used": [
{
"consumed_at": "2026-06-01T12:00:00Z",
"created_at": "2026-06-01T12:00:00Z",
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"gauge_mm": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"length_mm": 0,
"material_category": "string",
"material_grade_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"origin_group_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"quantity": 0,
"reserved_by_run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "available",
"width_mm": 0
}
],
"savings": {
"alone": 0,
"alone_kg": 0,
"alone_sheets": 0,
"by_order": [
{
"alone": 0,
"alone_kg": 0,
"alone_sheets": 0,
"copies": 0,
"currency": "EUR",
"lines": 0,
"lines_compared": 0,
"nested": 0,
"nested_kg": 0,
"nested_sheets": 0,
"order_ref": "string",
"rectangular": 0,
"rectangular_kg": 0,
"rectangular_sheets": 0,
"saved": 0,
"saved_combining": 0,
"saved_combining_kg": 0,
"saved_kg": 0,
"saved_true_shape": 0,
"saved_true_shape_kg": 0,
"split_lines": 0
}
],
"copies": 0,
"currency": "EUR",
"lines": 0,
"lines_compared": 0,
"nested": 0,
"nested_kg": 0,
"nested_sheets": 0,
"rectangular": 0,
"rectangular_kg": 0,
"rectangular_sheets": 0,
"saved": 0,
"saved_combining": 0,
"saved_combining_kg": 0,
"saved_kg": 0,
"saved_true_shape": 0,
"saved_true_shape_kg": 0,
"split_lines": 0
},
"sheets": 0,
"started_at": "2026-06-01T12:00:00Z",
"status": "draft"
}
Cancel a job nest
POST /api/v1/nest-runs/{run_id}/cancel
Stop a job nest that has not ended.
The materials and thicknesses already nested keep their result; the others are cancelled, and every remnant the job nest holds goes back to stock. A job nest cancelled before any material and thickness was nested gives its run back to the month's allowance; once one is nested, it counts as a run, as a job nest that stops on an error does. A draft is discarded the same way.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string | yes | Identifier of the run. |
Request
curl -X POST https://api.arcnm.io/api/v1/nest-runs/{run_id}/cancel \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/nest-runs/{run_id}/cancel",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/{run_id}/cancel", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
},
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
Successful Response |
402 |
An assembly calculation ran this job nest for its own sheet components, and your organization has no active Nesting add-on (addon_required). |
404 |
No run with this id in your organization (nest_run_not_found). |
409 |
The run has already ended (nest_run_not_cancellable), or it is an assembly calculation's own nest, which only that assembly changes (nest_run_internal). |
422 |
Validation Error |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
404 |
not_found |
A referenced resource doesn't exist or isn't visible to your organisation. |
409 |
conflict |
A conflicting change, or an Idempotency-Key reused with a different body. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 200
| Field | Type | Description |
|---|---|---|
allocation_rule |
area_proportional | equal_per_sheet |
How each sheet's cost is shared by the parts on it. |
as_of |
string | The day its stock and prices are read at. |
copies |
integer | Copies it nests, over all its lines. |
counted_at |
string | When it was released; null while a simulation. |
counts_toward_savings |
boolean | True once the job nest was released: its stock is booked and what it saves adds to the total saved by nesting. False: a simulation, whose saving is potential only. |
created_at |
string | |
created_by_user_id |
string | |
env_id |
string | The costing environment it nests in. |
error |
string | Why it stopped, when it did not finish. |
finished_at |
string | |
group_budget_s |
number | The longest each material and thickness may be nested for, in seconds of computing time. |
groups_done |
integer | Of those, the ones finished so far: nested, or not nestable. |
groups_total |
integer | Materials and thicknesses it nests — each is nested on its own. |
id |
string | |
lines |
integer | Lines it nests. |
savings |
NestRunSavings | What it saves, once it ends; null before, or where no line compares. |
sheets |
integer | Sheets and remnants the nested materials and thicknesses are cut from; set once the job nest ends. |
started_at |
string | |
status |
draft | queued | running | solved | failed | cancelled |
draft: kept, not queued — it was started while the month's runs were used, so it spends no run until POST /nest-runs/{run_id}/start starts it. queued: waiting to start. running: nesting, several materials and thicknesses at a time. solved: every material and thickness is nested. failed: it stopped, error says why; what it nested before stands. cancelled: stopped on request; what it nested before stands. |
Example response
{
"allocation_rule": "area_proportional",
"as_of": "2026-06-01",
"copies": 0,
"counted_at": "2026-06-01T12:00:00Z",
"counts_toward_savings": false,
"created_at": "2026-06-01T12:00:00Z",
"created_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"error": "string",
"finished_at": "2026-06-01T12:00:00Z",
"group_budget_s": 184,
"groups_done": 0,
"groups_total": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"lines": 0,
"savings": {
"alone": 0,
"alone_kg": 0,
"alone_sheets": 0,
"by_order": [
{
"alone": 0,
"alone_kg": 0,
"alone_sheets": 0,
"copies": 0,
"currency": "EUR",
"lines": 0,
"lines_compared": 0,
"nested": 0,
"nested_kg": 0,
"nested_sheets": 0,
"order_ref": "string",
"rectangular": 0,
"rectangular_kg": 0,
"rectangular_sheets": 0,
"saved": 0,
"saved_combining": 0,
"saved_combining_kg": 0,
"saved_kg": 0,
"saved_true_shape": 0,
"saved_true_shape_kg": 0,
"split_lines": 0
}
],
"copies": 0,
"currency": "EUR",
"lines": 0,
"lines_compared": 0,
"nested": 0,
"nested_kg": 0,
"nested_sheets": 0,
"rectangular": 0,
"rectangular_kg": 0,
"rectangular_sheets": 0,
"saved": 0,
"saved_combining": 0,
"saved_combining_kg": 0,
"saved_kg": 0,
"saved_true_shape": 0,
"saved_true_shape_kg": 0,
"split_lines": 0
},
"sheets": 0,
"started_at": "2026-06-01T12:00:00Z",
"status": "draft"
}
Export a job nest's sheets for cutting
GET /api/v1/nest-runs/{run_id}/export
The sheets of a job nest as cutting files.
Every solved material and thickness contributes its sheets: each
distinct layout once, with how many identical sheets it stands for.
A sheet's DXF holds the sheet's rectangle, the usable area, the reusable
rest it leaves, every copy's outer contour and holes, and a label per
copy with its part reference and order; the header and a title state
the job nest, the sheet, the material and thickness, the format or
remnant, the count and the utilisation. Contours are within 0.2 mm of
the unfolded flat pattern; arcs are polylines. The JSON nest plan
carries the same geometry as data. A job nest that is still running
exports what is solved so far. The sheets are each material and
thickness's ACTIVE revision — the layout the job nest reads — unless
revision names another; then every exported material and thickness
must have that revision. A file still being built after a minute is
answered 503 with Retry-After; /export-url never waits.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string | yes | Identifier of the run. |
format |
query | dxf | svg | json |
no | dxf (R2010, millimetres: one block per part and one insert per copy, on the layers SHEET, MARGIN, REMNANT, CUT_OUTER, CUT_INNER and LABEL), svg (the same geometry, the view box in millimetres) or json (the nest plan: every part's contour and every sheet's placements). |
sheet |
query | string | no | all: every sheet — a zip of one DXF or SVG per sheet with plan.json and manifest.csv, or the whole nest plan as JSON. Or one sheet's key, <group>:<index> as the plan names it, for that sheet's file. |
exploded |
query | boolean | no | DXF only: write each copy's contours as plain closed polylines in place, no blocks — for a cutting system that does not read block references. |
revision |
query | integer | no | Export this revision of every material and thickness's sheets instead of the active one; 1 is the job nest's own layout. |
Request
curl -X GET https://api.arcnm.io/api/v1/nest-runs/{run_id}/export \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/nest-runs/{run_id}/export",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/{run_id}/export", {
method: "GET",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
},
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
The file — Content-Disposition names it — or, for format=json, the nest plan. |
402 |
An assembly calculation ran this job nest for its own sheet components, and your organization has no active Nesting add-on (addon_required). |
404 |
No job nest with this id in your organization (nest_run_not_found), or sheet names no sheet of it (nest_run_sheet_not_found). |
409 |
No material and thickness of the job nest is solved yet (nest_run_nothing_to_export). |
422 |
Validation Error |
503 |
The file is still being built (nest_export_timeout): ask again after Retry-After seconds. |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
404 |
not_found |
A referenced resource doesn't exist or isn't visible to your organisation. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 200
| Field | Type | Description |
|---|---|---|
parts |
NestPlanPart[] | |
sheets |
NestPlanSheet[] | |
source |
NestPlanSource | |
tolerance |
NestPlanTolerance | |
units |
string | |
v |
integer | The schema version. |
Example response
{
"parts": [
{
"calculation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"holes": [
[]
],
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"net_area_mm2": 0,
"order_ref": "string",
"outline": [
[]
],
"placed": 0,
"quantity": 0,
"ref": "string"
}
],
"sheets": [
{
"container": {
"key": "string",
"kind": "format",
"length_mm": 0,
"width_mm": 0
},
"count": 0,
"gauge_mm": 0,
"group": 0,
"index": 0,
"key": "string",
"margin_mm": 0,
"material": "string",
"placements": [
{}
],
"remnant": {
"length_mm": 0,
"width_mm": 0,
"x_mm": 0,
"y_mm": 0
},
"repeat": 0,
"spacing_mm": 0,
"utilisation": 0
}
],
"source": {
"calculation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"created_at": "2026-06-01T12:00:00Z",
"environment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"finished_at": "2026-06-01T12:00:00Z",
"kind": "run",
"run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"tolerance": {
"arcs": "string",
"contour_mm": 0,
"note": "string"
},
"units": "string",
"v": 0
}
Link to a job nest's cutting files
GET /api/v1/nest-runs/{run_id}/export-url
The file GET /nest-runs/{run_id}/export answers, as a short-lived
download link — without waiting for it to be built.
200: the file is ready; url downloads it (no credentials, until
expires_in_s runs out) under file_name. 202: it is being built;
ask again after Retry-After seconds. A build that was refused is
answered with its own code and status; fresh=true builds it again.
Same parameters, scope and refusals as the export.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string | yes | Identifier of the run. |
format |
query | dxf | svg | json |
no | dxf (R2010, millimetres: one block per part and one insert per copy, on the layers SHEET, MARGIN, REMNANT, CUT_OUTER, CUT_INNER and LABEL), svg (the same geometry, the view box in millimetres) or json (the nest plan: every part's contour and every sheet's placements). |
sheet |
query | string | no | all: every sheet — a zip of one DXF or SVG per sheet with plan.json and manifest.csv, or the whole nest plan as JSON. Or one sheet's key, <group>:<index> as the plan names it, for that sheet's file. |
exploded |
query | boolean | no | DXF only: write each copy's contours as plain closed polylines in place, no blocks — for a cutting system that does not read block references. |
revision |
query | integer | no | Export this revision of every material and thickness's sheets instead of the active one; 1 is the job nest's own layout. |
fresh |
query | boolean | no | Build the file again when its last build was refused and nothing is building it now. Without it, a refusal is answered again for a few minutes. |
Request
curl -X GET https://api.arcnm.io/api/v1/nest-runs/{run_id}/export-url \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/nest-runs/{run_id}/export-url",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/{run_id}/export-url", {
method: "GET",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
},
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
Successful Response |
202 |
The file is being built: ask again after Retry-After seconds. |
402 |
An assembly calculation ran this job nest for its own sheet components, and your organization has no active Nesting add-on (addon_required). |
404 |
No job nest with this id in your organization (nest_run_not_found), or sheet names no sheet of it (nest_run_sheet_not_found). |
409 |
No material and thickness of the job nest is solved yet (nest_run_nothing_to_export). |
422 |
Validation Error |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
404 |
not_found |
A referenced resource doesn't exist or isn't visible to your organisation. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 200
| Field | Type | Description |
|---|---|---|
content_type |
string | |
expires_in_s |
integer | Seconds until the link expires. |
file_name |
string | |
size_bytes |
integer | The file's size in bytes. |
status |
string | |
url |
string | GET it, without credentials, before it expires; the response names the file. |
Example response
{
"content_type": "string",
"expires_in_s": 0,
"file_name": "string",
"size_bytes": 204800,
"status": "string",
"url": "string"
}
Check a layout of a job nest's sheets
POST /api/v1/nest-runs/{run_id}/groups/{group_id}/layout-check
What the cutter would find in this layout, before anything is stored.
Send the whole layout of one material and thickness: every sheet with
the stock format or remnant it is cut from and its placements. The
answer names every pair of parts closer than the spacing with the gap
between them, every part in the margin or off the sheet, every line
short or over its quantity, every container the job nest cannot cut
from, every copy turned against the environment's grain direction or
mirrored where it allows no mirroring (as the job nest read its shop
practice), and each sheet's utilisation and rest. ok is what
POST …/revisions accepts; complete says every line is placed
exactly as often as ordered. Nothing is stored.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string | yes | The job nest. |
group_id |
path | string | yes | The material and thickness. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
note |
string | no | Why this layout (kept with the revision; ignored by the check). |
sheets |
NestSheetIn[] | yes |
Request
curl -X POST https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/layout-check \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sheets": [
{
"container_id": "string",
"placements": [
{
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"mirrored": false,
"rot_deg": 0,
"x": 0,
"y": 0
}
],
"repeat": 1
}
]
}'
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/layout-check",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"sheets": [
{
"container_id": "string",
"placements": [
{
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"mirrored": False,
"rot_deg": 0,
"x": 0,
"y": 0
}
],
"repeat": 1
}
]
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/layout-check", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"sheets": [
{
"container_id": "string",
"placements": [
{
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"mirrored": false,
"rot_deg": 0,
"x": 0,
"y": 0
}
],
"repeat": 1
}
]
}),
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
Successful Response |
404 |
No job nest with this id in your organization (nest_run_not_found), or no material and thickness with this id in it (nest_run_group_not_found). |
409 |
This material and thickness is not nested yet (nest_run_not_editable). |
422 |
Validation Error |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
404 |
not_found |
A referenced resource doesn't exist or isn't visible to your organisation. |
409 |
conflict |
A conflicting change, or an Idempotency-Key reused with a different body. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 200
| Field | Type | Description |
|---|---|---|
complete |
boolean | Every line is placed exactly as often as its quantity: missing and extra are empty. |
containers_unknown |
string[] | |
extra |
NestLineCount[] | Lines with more copies placed than demanded (placed = the surplus). |
lines |
NestLineCount[] | |
missing |
NestLineCount[] | Lines with fewer copies placed than demanded (placed = the shortfall). |
ok |
boolean | Every sheet can be cut, every copy keeps the environment's shop practice, and no remnant is over-used: what POST …/revisions accepts. A line short of its quantity or over it does not make a layout unacceptable — missing and extra state it, and a revision is priced on the copies it cuts. |
remnants_over |
NestRemnantOver[] | |
sheets |
NestSheetCheck[] | |
sheets_total |
integer | Sheets, every one as often as it repeats. |
utilisation_net |
number | The parts' area over the sheet area used, reusable rests not counted. |
Example response
{
"complete": true,
"containers_unknown": [
"string"
],
"extra": [
{
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"placed": 0,
"quantity": 0
}
],
"lines": [
{
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"placed": 0,
"quantity": 0
}
],
"missing": [
{
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"placed": 0,
"quantity": 0
}
],
"ok": true,
"remnants_over": [
{
"available": 0,
"container_id": "string",
"pieces": 0
}
],
"sheets": [
{
"against_shop_practice": [
{}
],
"clashes": [
{}
],
"clashes_total": 0,
"container_id": "string",
"container_kind": "string",
"container_unknown": true,
"count": 0,
"index": 0,
"length_mm": 0,
"net_area_mm2": 0,
"ok": true,
"outside": [
0
],
"repeat": 0,
"rest": {
"length_mm": 0,
"reusable": true,
"width_mm": 0,
"x_mm": 0,
"y_mm": 0
},
"unknown_lines": [
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
],
"utilisation": 0,
"width_mm": 0
}
],
"sheets_total": 0,
"utilisation_net": 0
}
List a material and thickness's revisions
GET /api/v1/nest-runs/{run_id}/groups/{group_id}/revisions
Every revision of this material and thickness's sheets, oldest first — the job nest's own result is revision 1 — with what each saves; the active one is marked. Layouts are read per revision.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string | yes | The job nest. |
group_id |
path | string | yes | The material and thickness. |
Request
curl -X GET https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/revisions \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/revisions",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/revisions", {
method: "GET",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
},
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
Successful Response |
402 |
An assembly calculation ran this job nest for its own sheet components, and your organization has no active Nesting add-on (addon_required). |
404 |
No job nest with this id in your organization (nest_run_not_found), or no material and thickness with this id in it (nest_run_group_not_found). |
422 |
Validation Error |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
404 |
not_found |
A referenced resource doesn't exist or isn't visible to your organisation. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Post a layout as the next revision
POST /api/v1/nest-runs/{run_id}/groups/{group_id}/revisions
Store this layout as the material and thickness's next revision and make it the active one.
The body is the one POST …/layout-check takes; a layout the check
finds ok — every sheet cuttable, no remnant over-used — is accepted.
A line short of its quantity or over it is accepted as posted and
priced on the copies the layout cuts. It is priced as the job nest's
own result is (the run's allocation rule, the material's price and
credits), and the group's sheets, every line's share, the run's order
totals and what it saves follow the new revision. Earlier
revisions stay as they are: a calculation priced on a line keeps the
revision it read. To go back to an earlier layout, post it again. A
released job nest is frozen.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string | yes | The job nest. |
group_id |
path | string | yes | The material and thickness. |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
note |
string | no | Why this layout (kept with the revision; ignored by the check). |
sheets |
NestSheetIn[] | yes |
Request
curl -X POST https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/revisions \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sheets": [
{
"container_id": "string",
"placements": [
{
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"mirrored": false,
"rot_deg": 0,
"x": 0,
"y": 0
}
],
"repeat": 1
}
]
}'
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/revisions",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"sheets": [
{
"container_id": "string",
"placements": [
{
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"mirrored": False,
"rot_deg": 0,
"x": 0,
"y": 0
}
],
"repeat": 1
}
]
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/revisions", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"sheets": [
{
"container_id": "string",
"placements": [
{
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"mirrored": false,
"rot_deg": 0,
"x": 0,
"y": 0
}
],
"repeat": 1
}
]
}),
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
201 |
Successful Response |
404 |
No job nest with this id in your organization (nest_run_not_found), or no material and thickness with this id in it (nest_run_group_not_found). |
409 |
The job nest has not ended yet, or this material and thickness is not nested (nest_run_not_editable, details.reason); the job nest was released and its stock booked, so its sheets are frozen (nest_run_released); or it is an assembly calculation's own nest, whose layout prices that assembly's components and only that assembly changes (nest_run_internal). |
422 |
The layout cannot be cut as posted, or breaks the environment's shop practice (nest_layout_invalid): details is the check POST …/layout-check answers. A line short of its quantity or over it is no refusal. |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
404 |
not_found |
A referenced resource doesn't exist or isn't visible to your organisation. |
409 |
conflict |
A conflicting change, or an Idempotency-Key reused with a different body. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 201
| Field | Type | Description |
|---|---|---|
active |
boolean | The one the job nest reads its sheets from. |
created_at |
string | |
created_by_user_id |
string | |
note |
string | |
revision |
integer | |
savings |
object | What the lines save under this revision: rectangular blanks, each part nested alone, the parts nested together, what nesting saved and its split, summed and per order (by_order), and per line (by_line) — see the job nest's savings. |
sheets |
integer | Sheets and remnants it cuts. |
source |
string | nest — the job nest's own layout; edit — one posted here. |
utilisation_net |
number |
Example response
{
"active": true,
"created_at": "2026-06-01T12:00:00Z",
"created_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"note": "string",
"revision": 0,
"savings": {},
"sheets": 0,
"source": "string",
"utilisation_net": 0
}
Read a revision with its sheets
GET /api/v1/nest-runs/{run_id}/groups/{group_id}/revisions/{revision}
One revision with every sheet's placements — the body
POST …/revisions takes, as stored, to adjust and post again.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string | yes | The job nest. |
group_id |
path | string | yes | The material and thickness. |
revision |
path | integer | yes | The revision number. |
Request
curl -X GET https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/revisions/{revision} \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/revisions/{revision}",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/{run_id}/groups/{group_id}/revisions/{revision}", {
method: "GET",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
},
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
Successful Response |
402 |
An assembly calculation ran this job nest for its own sheet components, and your organization has no active Nesting add-on (addon_required). |
404 |
No job nest, material and thickness, or revision with this id (nest_run_not_found, nest_run_group_not_found, nest_run_revision_not_found). |
422 |
Validation Error |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
404 |
not_found |
A referenced resource doesn't exist or isn't visible to your organisation. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 200
| Field | Type | Description |
|---|---|---|
active |
boolean | The one the job nest reads its sheets from. |
created_at |
string | |
created_by_user_id |
string | |
group_id |
string | |
length_width_mm |
object | Each container's length and width, by container id. |
note |
string | |
revision |
integer | |
savings |
object | What the lines save under this revision: rectangular blanks, each part nested alone, the parts nested together, what nesting saved and its split, summed and per order (by_order), and per line (by_line) — see the job nest's savings. |
sheets |
integer | Sheets and remnants it cuts. |
sheets_laid |
NestRevisionSheet[] | |
source |
string | nest — the job nest's own layout; edit — one posted here. |
utilisation_net |
number |
Example response
{
"active": true,
"created_at": "2026-06-01T12:00:00Z",
"created_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"group_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"length_width_mm": {},
"note": "string",
"revision": 0,
"savings": {},
"sheets": 0,
"sheets_laid": [
{
"container_id": "string",
"container_kind": "string",
"count": 0,
"index": 0,
"net_area_mm2": 0,
"placements": [
{}
],
"repeat": 0,
"rest": {
"length_mm": 0,
"reusable": true,
"width_mm": 0,
"x_mm": 0,
"y_mm": 0
},
"utilisation": 0
}
],
"source": "string",
"utilisation_net": 0
}
Nest Run Remnant Labels
GET /api/v1/nest-runs/{run_id}/remnant-labels
Rack labels for the rests a released job nest put into stock, one per row in one SVG sized in millimetres — print it when the sheets come off the cutter.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string | yes | Identifier of the run. |
lang |
query | de | en |
no | The labels' language. |
Request
curl -X GET https://api.arcnm.io/api/v1/nest-runs/{run_id}/remnant-labels \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/nest-runs/{run_id}/remnant-labels",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/{run_id}/remnant-labels", {
method: "GET",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
},
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
One label per rest the released job nest put into stock. |
402 |
An assembly calculation ran this job nest for its own sheet components, and your organization has no active Nesting add-on (addon_required). |
404 |
No job nest with this id in your organization (nest_run_not_found), or it put no rest into stock — not released, or its sheets leave no reusable rest (nest_run_no_rests). |
422 |
Validation Error |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
404 |
not_found |
A referenced resource doesn't exist or isn't visible to your organisation. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Start a job nest kept as a draft
POST /api/v1/nest-runs/{run_id}/start
Queue a job nest that was kept as a draft.
A start made while the month's runs were used keeps the job nest as a
draft. Once the allowance has a run for it, this queues it: it spends
one run, is nested at today's stock and prices, and runs in the
background — poll GET /nest-runs/{run_id} until it ends. While the
month's runs are still used it is refused (402) and stays a draft. Only
a draft starts, and only once; one job nest of your organization runs
at a time, as for a start.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string | yes | Identifier of the run. |
Request
curl -X POST https://api.arcnm.io/api/v1/nest-runs/{run_id}/start \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/nest-runs/{run_id}/start",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/{run_id}/start", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
},
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
Successful Response |
402 |
The Nesting add-on is not active, or the month's runs are still used (details.feature nesting_runs, with used and limit): the draft stays a draft. |
404 |
No run with this id in your organization (nest_run_not_found). |
409 |
The job nest is not a draft (nest_run_not_draft: it was started already, or cancelled), another job nest of your organization is still queued or running (nest_run_in_flight), or it is an assembly calculation's own nest, which only that assembly changes (nest_run_internal). |
422 |
It has more materials and thicknesses than a run takes (nest_run_too_many_nests). |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
404 |
not_found |
A referenced resource doesn't exist or isn't visible to your organisation. |
409 |
conflict |
A conflicting change, or an Idempotency-Key reused with a different body. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 200
| Field | Type | Description |
|---|---|---|
allocation_rule |
area_proportional | equal_per_sheet |
How each sheet's cost is shared by the parts on it. |
as_of |
string | The day its stock and prices are read at. |
copies |
integer | Copies it nests, over all its lines. |
counted_at |
string | When it was released; null while a simulation. |
counts_toward_savings |
boolean | True once the job nest was released: its stock is booked and what it saves adds to the total saved by nesting. False: a simulation, whose saving is potential only. |
created_at |
string | |
created_by_user_id |
string | |
env_id |
string | The costing environment it nests in. |
error |
string | Why it stopped, when it did not finish. |
finished_at |
string | |
group_budget_s |
number | The longest each material and thickness may be nested for, in seconds of computing time. |
groups_done |
integer | Of those, the ones finished so far: nested, or not nestable. |
groups_total |
integer | Materials and thicknesses it nests — each is nested on its own. |
id |
string | |
lines |
integer | Lines it nests. |
savings |
NestRunSavings | What it saves, once it ends; null before, or where no line compares. |
sheets |
integer | Sheets and remnants the nested materials and thicknesses are cut from; set once the job nest ends. |
started_at |
string | |
status |
draft | queued | running | solved | failed | cancelled |
draft: kept, not queued — it was started while the month's runs were used, so it spends no run until POST /nest-runs/{run_id}/start starts it. queued: waiting to start. running: nesting, several materials and thicknesses at a time. solved: every material and thickness is nested. failed: it stopped, error says why; what it nested before stands. cancelled: stopped on request; what it nested before stands. |
Example response
{
"allocation_rule": "area_proportional",
"as_of": "2026-06-01",
"copies": 0,
"counted_at": "2026-06-01T12:00:00Z",
"counts_toward_savings": false,
"created_at": "2026-06-01T12:00:00Z",
"created_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"error": "string",
"finished_at": "2026-06-01T12:00:00Z",
"group_budget_s": 184,
"groups_done": 0,
"groups_total": 0,
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"lines": 0,
"savings": {
"alone": 0,
"alone_kg": 0,
"alone_sheets": 0,
"by_order": [
{
"alone": 0,
"alone_kg": 0,
"alone_sheets": 0,
"copies": 0,
"currency": "EUR",
"lines": 0,
"lines_compared": 0,
"nested": 0,
"nested_kg": 0,
"nested_sheets": 0,
"order_ref": "string",
"rectangular": 0,
"rectangular_kg": 0,
"rectangular_sheets": 0,
"saved": 0,
"saved_combining": 0,
"saved_combining_kg": 0,
"saved_kg": 0,
"saved_true_shape": 0,
"saved_true_shape_kg": 0,
"split_lines": 0
}
],
"copies": 0,
"currency": "EUR",
"lines": 0,
"lines_compared": 0,
"nested": 0,
"nested_kg": 0,
"nested_sheets": 0,
"rectangular": 0,
"rectangular_kg": 0,
"rectangular_sheets": 0,
"saved": 0,
"saved_combining": 0,
"saved_combining_kg": 0,
"saved_kg": 0,
"saved_true_shape": 0,
"saved_true_shape_kg": 0,
"split_lines": 0
},
"sheets": 0,
"started_at": "2026-06-01T12:00:00Z",
"status": "draft"
}
Preview a job nest
POST /api/v1/nest-runs/preview
What a job nest of this selection would take, without starting it.
Takes the body POST /nest-runs takes and reads it the same way: the
lines it would nest, each material and thickness they fall into with
the remnant pieces of it in stock now, and what a batch or folder
leaves out and why. It refuses a missing add-on and a run with too
many nests. When the month's runs are already used it still returns
the plan, with kept_as_draft set, and nothing is stored.
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
allocation_rule |
area_proportional | equal_per_sheet |
no | How a sheet's cost is shared by the parts on it: by their area, or equally. |
batch_id |
string | no | Nest every calculation of this batch priced in the environment, each at its lot size. |
env_id |
string | yes | The costing environment the run nests in: its stock and remnants. |
folder_id |
string | no | Nest the newest calculation priced in the environment of every part filed in this folder or below it, each at its lot size. |
group_budget_s |
number | no | The longest each material and thickness may be nested for, in seconds of computing time: longer can use less sheet. |
items |
NestRunItemIn[] | no |
Request
curl -X POST https://api.arcnm.io/api/v1/nest-runs/preview \
-H "X-API-Key: $ARCNM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'
import requests
resp = requests.post(
"https://api.arcnm.io/api/v1/nest-runs/preview",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/preview", {
method: "POST",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}),
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
Successful Response |
404 |
The environment, batch or folder is not your organization's (nest_run_selection_not_found). |
422 |
An item can't be nested — details.items names each with its reason (nest_run_items_unusable) — or the batch or folder holds nothing that can (nest_run_nothing_to_nest), or more calculations than one run takes (nest_run_selection_too_large), or a line asks for more copies than one line takes (nest_run_line_too_large), or the lines together more than one run takes (nest_run_too_many_copies), or group_budget_s is past what your organization may ask for (nest_run_budget_too_long). |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
409 |
conflict |
A conflicting change, or an Idempotency-Key reused with a different body. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 200
| Field | Type | Description |
|---|---|---|
copies |
integer | Copies it would nest, over all its lines. |
env_id |
string | |
groups |
NestPlanGroup[] | |
items |
NestPlanLine[] | |
kept_as_draft |
boolean | The month's runs are already used. Starting stores this selection as a draft and does not queue it or spend a run. |
left_out |
LeftOutLine[] | Calculations of the batch or folder it would not take, each with why. |
lines |
integer | Lines it would nest. |
Example response
{
"copies": 0,
"env_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"groups": [
{
"copies": 0,
"gauge_mm": 0,
"lines": 0,
"material_category": "string",
"material_grade_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"remnants_in_stock": 0
}
],
"items": [
{
"calculation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"group": 0,
"order_ref": "string",
"position": 0,
"quantity": 0
}
],
"kept_as_draft": false,
"left_out": [
{
"calculation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
}
],
"lines": 0
}
Read the total saved by nesting
GET /api/v1/nest-runs/savings
What the job nests your organization released have saved together — rectangular blanks cut part by part against the parts nested together, split into each part's true shape and the orders nested together, the money per currency and the sheet metal over all — and, apart from it, the potential of the job nests that are still simulations. A job nest is a simulation, and stays out of the total, until it is released.
Request
curl -X GET https://api.arcnm.io/api/v1/nest-runs/savings \
-H "X-API-Key: $ARCNM_API_KEY"
import requests
resp = requests.get(
"https://api.arcnm.io/api/v1/nest-runs/savings",
headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.arcnm.io/api/v1/nest-runs/savings", {
method: "GET",
headers: {
"X-API-Key": process.env.ARCNM_API_KEY!,
},
})
const data = await resp.json()
Responses
| Status | Description |
|---|---|
200 |
Successful Response |
422 |
Validation Error |
Errors
Standard error responses — see the Errors catalog for the full envelope, request_id, and retry-safety table.
| Status | Code | When |
|---|---|---|
401 |
invalid_api_key |
Missing, malformed, or revoked API key. |
403 |
insufficient_scope |
The key is valid but lacks a scope this endpoint requires. |
429 |
rate_limited |
Per-key or per-org rate limit exceeded — back off with jitter and retry. |
Response body 200
| Field | Type | Description |
|---|---|---|
alone_kg |
number | The same with each part nested alone, in kg, over the lines in split_lines. |
lines_compared |
integer | Their lines the figures cover. |
money |
NestSavingsMoney[] | The released job nests' savings per currency, in currency order. |
nested_kg |
number | The same nested together, in kg. |
potential |
NestSavingsSum | The simulations' savings, summed the same way: what releasing them would add. Never part of the total. |
rectangular_kg |
number | The sheet the released lines take as rectangular blanks, in kg. |
runs_counted |
integer | Released job nests, each with a saving. |
runs_simulated |
integer | Job nests with a saving that are not released: simulations. |
saved_combining_kg |
number | alone_kg less nested_kg, over the lines in split_lines. |
saved_kg |
number | rectangular_kg less nested_kg. |
saved_true_shape_kg |
number | rectangular_kg less alone_kg, over the lines in split_lines. |
split_lines |
integer | Of those, the lines the split of the saving covers. |
Example response
{
"alone_kg": 0,
"lines_compared": 0,
"money": [
{
"alone": 0,
"currency": "EUR",
"lines_compared": 0,
"nested": 0,
"rectangular": 0,
"runs": 0,
"saved": 0,
"saved_combining": 0,
"saved_true_shape": 0,
"split_lines": 0
}
],
"nested_kg": 0,
"potential": {
"alone_kg": 0,
"lines_compared": 0,
"money": [
{
"alone": 0,
"currency": "EUR",
"lines_compared": 0,
"nested": 0,
"rectangular": 0,
"runs": 0,
"saved": 0,
"saved_combining": 0,
"saved_true_shape": 0,
"split_lines": 0
}
],
"nested_kg": 0,
"rectangular_kg": 0,
"runs": 0,
"saved_combining_kg": 0,
"saved_kg": 0,
"saved_true_shape_kg": 0,
"split_lines": 0
},
"rectangular_kg": 0,
"runs_counted": 0,
"runs_simulated": 0,
"saved_combining_kg": 0,
"saved_kg": 0,
"saved_true_shape_kg": 0,
"split_lines": 0
}