Official SDKs
Three open-source clients, generated from the same OpenAPI document: install one and call the engine in a few lines.
Install
One client per language, all MIT-licensed, with no dependencies of their own. Each is generated from the OpenAPI 3.1 document, so the types match the contract this reference describes.
| Language | Install | Runs on | Source |
|---|---|---|---|
| TypeScript / JavaScript | npm install @cutoptim/engine | Node 18+, Deno, Bun, edge runtimes, the browser | GitHub · npm |
| Python | pip install cutoptim | Python 3.9+ | GitHub · PyPI |
| PHP | composer require cutoptim/engine | PHP 8.1+ with cURL | GitHub · Packagist |
A free test key (co_test_…, never billed) is enough to build the whole integration — create one on your dashboard. The SDKs are a convenience, not a requirement: the API stays plain HTTPS with JSON (see the Quickstart).
Your first plan
The client reads the key you pass it and has one method per endpoint — optimize2d, optimize1d, optimizeWood, optimizeNest (in Python optimize_2d and so on). The request and the response are the ones in the 2D guide:
// TypeScript
import { CutOptim } from '@cutoptim/engine';
const client = new CutOptim({ apiKey: process.env.CUTOPTIM_KEY });
const plan = await client.optimize2d({
parts: [{ name: 'Side', w: 720, h: 560, qty: 2 }],
stock: [{ w: 2800, h: 2070 }],
options: { kerf: 4 },
});
if (!CutOptim.isJobAccepted(plan)) console.log(plan.metrics.sheetCount, plan.metrics.yieldPct);# Python
import os
from cutoptim import CutOptim, is_job_accepted
client = CutOptim(api_key=os.environ["CUTOPTIM_KEY"])
plan = client.optimize_2d({
"parts": [{"name": "Side", "w": 720, "h": 560, "qty": 2}],
"stock": [{"w": 2800, "h": 2070}],
"options": {"kerf": 4},
})
if not is_job_accepted(plan):
print(plan["metrics"]["sheetCount"], plan["metrics"]["yieldPct"])// PHP
use CutOptim\Engine\Client;
$client = new Client(getenv('CUTOPTIM_KEY'));
$plan = $client->optimize2d([
'parts' => [['name' => 'Side', 'w' => 720, 'h' => 560, 'qty' => 2]],
'stock' => [['w' => 2800, 'h' => 2070]],
'options' => ['kerf' => 4],
]);
if (!Client::isJobAccepted($plan)) echo $plan['metrics']['sheetCount'], ' ', $plan['metrics']['yieldPct'];isJobAccepted tells a finished plan from the 202 an engine: "max" request answers. The validate endpoints need no key, and neither does the client for them: new CutOptim().validate2d(request) checks a request for free.
Async max jobs
waitForJob (wait_for_job) polls GET /v1/jobs/{id} — which costs no quota — until the job has finished, or gives up after the timeout you set (the job keeps running):
const accepted = await client.optimize2d({ ...request, engine: 'max' });
if (CutOptim.isJobAccepted(accepted)) {
const job = await client.waitForJob(accepted.jobId, { timeoutMs: 5 * 60_000 });
if (job.status === 'succeeded') console.log(job.result);
}Verifying webhooks
Each SDK checks CutOptim-Signature on the raw body, with the 5-minute replay window and both secrets of a rotation handled — the rules are in the webhooks guide:
verifyWebhook(rawBody, signatureHeader, secret) // TypeScript (async)
verify_webhook(raw_body, signature_header, secret) # Python
Webhook::verify($rawBody, $signatureHeader, $secret); // PHP
// during a rotation pass both secrets: [newSecret, oldSecret]Retries, idempotency and errors
- Every optimize call carries an
Idempotency-Key— a fresh UUID, or the one you pass — and every retry of that call sends the same key, so a retry is never charged twice (see Idempotency-Key). 429 busy, a503,409 idempotency_in_progressand network errors are retried (twice by default), waitingRetry-Afterwhen the API sends it.- Any other non-2xx answer throws a typed error (
CutOptimError, in PHPCutOptimException) with the status, the stablecode,detailsandretryAfter. Branch on the code — the list is on the errors page.
Pass your own key to keep a retry safe across a process restart: client.optimizeNest(request, { idempotencyKey: 'order-1042' }).
Versions and support
The SDKs follow semantic versioning on their own: a new field in the API contract is a minor release, and the SDKs never break inside /v1 (see versioning). Each README covers every option; report a bug or ask for a feature in the repository's GitHub Issues, or write to [email protected].