Skip to main content
← API reference

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.

LanguageInstallRuns onSource
TypeScript / JavaScriptnpm install @cutoptim/engineNode 18+, Deno, Bun, edge runtimes, the browserGitHub · npm
Pythonpip install cutoptimPython 3.9+GitHub · PyPI
PHPcomposer require cutoptim/enginePHP 8.1+ with cURLGitHub · 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, a 503, 409 idempotency_in_progress and network errors are retried (twice by default), waiting Retry-After when the API sends it.
  • Any other non-2xx answer throws a typed error (CutOptimError, in PHP CutOptimException) with the status, the stable code, details and retryAfter. 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].