Sawvant

Examples

Validated request and response samples for the Sawvant cutting optimization API.

Every request below validates against the OpenAPI spec. All dimensions are in millimetres. Optimization is asynchronous: POST /v1/optimize answers 202 with a job_id, and you read the result from GET /v1/jobs/{id}.

Basic optimization

Two part types on one sheet type with unlimited stock.

curl -X POST https://api.sawvant.com/v1/optimize \
  -H "X-API-Key: sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "parts": [
      { "id": "shelf",   "length": 600, "width": 400, "quantity": 8, "grain": "none" },
      { "id": "bracket", "length": 300, "width": 200, "quantity": 4, "grain": "none" }
    ],
    "sheets": [
      { "id": "mdf-18", "length": 2440, "width": 1220, "quantity": 0, "grain": "none" }
    ],
    "machine": { "blade_thickness": 3.2, "max_levels": 3, "cut_direction": "default" }
  }'

The result summary:

{
  "total_sheets": 1,
  "yield_percent": 72.56,
  "waste_percent": 27.44,
  "waste_area": 816800,
  "sheets_used": [
    { "sheet_id": "mdf-18", "quantity": 1, "yield_percent": 72.56 }
  ]
}

Grain direction and edge banding

A furniture panel run on veneered board. grain: "length" pins the part to the sheet grain so it cannot be rotated. edge_banding adds material per side before packing, so the placement dimensions you get back already include the banding. trim_margins removes an unusable border from each sheet edge.

curl -X POST https://api.sawvant.com/v1/optimize \
  -H "X-API-Key: sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "parts": [
      {
        "id": "door-front", "length": 700, "width": 396, "quantity": 6,
        "grain": "length",
        "edge_banding": { "top": 2, "bottom": 2, "left": 2, "right": 2 }
      },
      { "id": "side-panel", "length": 720, "width": 500, "quantity": 4, "grain": "length" }
    ],
    "sheets": [
      {
        "id": "oak-veneer-18", "length": 2800, "width": 2070, "quantity": 0,
        "grain": "length",
        "trim_margins": { "top": 10, "bottom": 10, "left": 10, "right": 10 },
        "article_number": "OAK-18-2800"
      }
    ],
    "machine": { "blade_thickness": 4.4, "max_levels": 3, "cut_direction": "rip" }
  }'

article_number is passed straight through to sheets_used, so you can map consumption back to your own SKUs:

{
  "total_sheets": 1,
  "yield_percent": 54.91,
  "sheets_used": [
    {
      "sheet_id": "oak-veneer-18",
      "article_number": "OAK-18-2800",
      "quantity": 1,
      "yield_percent": 54.0
    }
  ]
}

Grain values are none, length, width and free_same. Use free_same when parts may rotate as long as they all end up in the same orientation. cut_direction is default, rip or cross.

Cost calculation

Add cost_tariffs to get a cost breakdown alongside the plan. Cost and production metrics are Pro features.

curl -X POST https://api.sawvant.com/v1/optimize \
  -H "X-API-Key: sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "parts": [
      { "id": "shelf",   "length": 600, "width": 400, "quantity": 16, "grain": "none" },
      { "id": "divider", "length": 380, "width": 250, "quantity": 12, "grain": "none" }
    ],
    "sheets": [
      { "id": "mdf-18", "length": 2440, "width": 1220, "quantity": 0, "grain": "none" }
    ],
    "machine": { "blade_thickness": 3.2, "max_levels": 3, "cut_direction": "default" },
    "cost_tariffs": {
      "setup_cost": 15,
      "cost_per_meter": 0.12,
      "cost_per_rotation": 0.05,
      "cost_per_stack": 0.5,
      "cost_per_cycle": 0.8
    }
  }'

The result carries metrics and cost next to the usual summary and layouts:

{
  "metrics": {
    "total_cut_length": 47120,
    "total_cuts": 56,
    "total_rotations": 6,
    "total_stacks": 2,
    "cut_cycles": 2
  },
  "cost": {
    "setup_cost": 15,
    "cutting_cost": 5.6544,
    "stacking_cost": 1,
    "rotation_cost": 0.3,
    "cycle_cost": 1.6,
    "total_cost": 23.5544
  }
}

total_cut_length is in millimetres, so cutting_cost is total_cut_length / 1000 * cost_per_meter. Tariffs you leave out count as zero.

Reading placements

Every layout holds the placements for one sheet pattern. quantity is how many identical sheets use that pattern.

{
  "sheet_id": "mdf-18",
  "quantity": 1,
  "placements": [
    {
      "part_id": "shelf",
      "sheet_id": "mdf-18",
      "x": 0,
      "y": 0,
      "width": 600,
      "height": 400,
      "rotated": false,
      "grain": "none"
    },
    {
      "part_id": "shelf",
      "sheet_id": "mdf-18",
      "x": 603.2,
      "y": 0,
      "width": 600,
      "height": 400,
      "rotated": false,
      "grain": "none"
    }
  ]
}

x and y are measured from the top-left of the usable area, after trim margins. The 3.2 mm gap between the two placements above is the blade kerf. width and height include edge banding when you set it.

On this page