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
| Plan | Fast | Thorough | Price |
|---|---|---|---|
| Free | 100/day | — | €0 |
| Pro | 500/day | 50/day | €39/mo |
| Custom | Custom | Custom | Contact us |
Job History
Retrieve past optimization requests for your account.
GET /v1/account/jobsQuery parameters:
| Parameter | Type | Description |
|---|---|---|
limit | int | Results per page. Default: 20, max: 100. |
offset | int | Pagination offset. Default: 0. |
strategy | string | Filter by strategy: fast or thorough. |
status | string | Filter 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/webhooksDelete 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
- SDKs — Official client libraries
- API Reference — Full endpoint documentation
- Examples — Code samples per use case
- MCP Server — Connect AI agents via the Model Context Protocol
- OpenAPI spec — Machine-readable definition