Sawvant

Sawvant API

Cutting optimization API. Send sheets and parts as JSON, get an optimized cutting plan back.

Overview

Cutting optimization API. Send sheets and parts as JSON, receive coordinates, efficiency metrics, and optional cost breakdown.

Quick Start

1. Get an API Key

Create a free account and generate an API key from the Dashboard.

2. Submit a job

curl -X POST https://api.sawvant.com/v1/optimize \
  -H "X-API-Key: YOUR_API_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" }
  }'

All dimensions are in millimetres. quantity: 0 on a sheet means unlimited stock. grain: "none" lets the solver rotate the part. blade_thickness is the kerf your saw removes per cut.

Use grain: "length" or "width" on a part to say which way its grain runs, and the solver will only place it so that grain lines up with the sheet's. A sheet that leaves grain out counts as having its grain along its length, so you do not have to set it on both sides to get a cuttable plan.

Optimization runs asynchronously, so the API answers 202 with a job ID:

{
  "job_id": "27ad491b-ba15-4bd4-a13f-89c7f6d09fd0",
  "status": "pending",
  "poll_url": "/v1/jobs/27ad491b-ba15-4bd4-a13f-89c7f6d09fd0",
  "stream_url": "/v1/jobs/27ad491b-ba15-4bd4-a13f-89c7f6d09fd0/stream"
}

3. Fetch the result

Poll the job until status is completed. Most jobs finish in well under a second.

curl -s https://api.sawvant.com/v1/jobs/JOB_ID \
  -H "X-API-Key: YOUR_API_KEY"
{
  "job_id": "27ad491b-ba15-4bd4-a13f-89c7f6d09fd0",
  "status": "completed",
  "progress": 100,
  "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 }
      ]
    },
    "layouts": [
      {
        "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"
          }
        ]
      }
    ]
  }
}

placements is truncated above. Each entry gives the position of one part in millimetres from the top-left of the usable sheet area, after trim margins. x and width run along the sheet's length, y and height along its width, so x + width never exceeds the usable length. rotated is true when the part was turned a quarter turn, which swaps its length and width. quantity on a layout is how many identical sheets use that pattern.

Prefer live updates over polling? Use GET /v1/jobs/{id}/stream for Server-Sent Events with progress, completed and failed events.

Authentication

All requests require an X-API-Key header with your API key. Keys are prefixed with sk_.

Rate Limits

PlanFastThoroughPrice
Free100/day—€0
Pro500/day50/day€39/mo
CustomCustomCustomContact us

Job History

Retrieve past optimization requests for your account.

GET /v1/account/jobs

Query parameters:

ParameterTypeDescription
limitintResults per page. Default: 20, max: 100.
offsetintPagination offset. Default: 0.
strategystringFilter by strategy: fast or thorough.
statusstringFilter by status: completed, failed, running, pending.

Response includes jobs array and total count.

Webhooks

Available on the Custom plan. Sawvant sends a signed POST request to your endpoint when a job completes. Contact us to enable webhooks.

Register a webhook

POST /v1/account/webhooks
{ "url": "https://example.com/webhooks/sawvant" }

Response includes a secret field. Copy it now — it is not stored and cannot be retrieved later.

List webhooks

GET /v1/account/webhooks

Delete a webhook

DELETE /v1/account/webhooks/{id}

Payload

{
  "event": "job.completed",
  "job_id": "jb_abc123",
  "status": "completed",
  "strategy": "fast",
  "created_at": "2024-01-15T10:30:00Z"
}

Verifying signatures

Every request includes an X-Sawvant-Signature header: an HMAC-SHA256 hex digest of the raw request body, keyed with your signing secret.

import hmac, hashlib

def verify(secret: str, body: bytes, signature: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Webhooks can be managed from the Dashboard.

Reference

On this page