Skip to main content
Use the TypeScript SDK to connect a provider account, save a model, submit a run (one task you send to Millwork; the API calls it an execution), and read its result and receipt. You can also register, test, and connect an output check (a verifier in the API). It provides typed requests and responses for TypeScript and JavaScript on Node.js.

Install

Requires Node.js 20 or later (Millwork tests 20 and 22) and an organization API key.
The package is @millwork/solver on npm. The same package includes both the typed SDK and the millwork command-line tool, so this page and the CLI setup use the same version. The tool reads SOLVERAPI_API_KEY and optional SOLVERAPI_BASE_URL; Set up with the CLI covers it. For the shared setup and private key-entry rules, follow Connect an output check. Use the generated REST lifecycle reference for exact API fields and the verifier-dock contract for the endpoint request and response. The installed CLI reports its supported commands and flags offline with millwork verifier capabilities --json.

Make a test run

First configure your organization key in this terminal. The SDK accepts the key and base URL as arguments; it does not choose an organization for you. Save this JavaScript example as example.mjs next to the package.json where you installed the SDK. It runs with Node directly and uses the same client and methods as TypeScript. It submits a free test run and reads its receipt.
Run it from the same terminal:
Expected result: completed echo. For another outcome, keep the run ID and follow Errors and retries. To get a model answer, continue with Run a model. A test run (the API calls it Echo, mode: "echo") has no platform fee or model cost. It takes no output check, a check applied to a run’s output, so the example omits verifier_id.

Register and test an output check

Start with a running endpoint that implements the output-check contract. Set VERIFIER_URL to its HTTPS address. Save this as output-check.mjs next to the installed package and run node output-check.mjs from the terminal with your organization key configured:
Keep the returned verifier_id. The test reports endpoint health; it does not authorize a paid run. If the endpoint needs a key, continue with the private handoff below. Do not put that key in verifiers.create.

Use an output check on a run

First save a model and keep its arm_id. Connect and test the check above, then keep its verifier_id. A live run has a platform fee when accepted and may incur model usage. Review the exact task, model, limits, check, and charge before authorizing this run; the shared run procedure owns approval and recovery. Set ARM_ID, VERIFIER_ID, MAX_COST_USD, MAX_RUNTIME_S, and a unique RUN_REQUEST_KEY in your terminal. Record that key with the exact request before submitting; reuse it only for an uncertain retry of the same request. Choose positive limits for the task you reviewed; the model budget is separate from the platform fee. Save this as checked-run.mjs next to the installed package. Run node checked-run.mjs only for the run you have authorized:
Expected result: an execution_id and an initial status. Keep the request key unchanged if you need to recover an uncertain submission. Read the result and receipt by that ID; a completed run can still have a rejected or unavailable check.

Connect, replace, or restore a protected output check

@millwork/solver@0.1.21 includes verifierConnection. Register the endpoint as above, then start the private handoff from your server. This example uses Response.redirect to send the intended person’s browser to Millwork’s private page. Keep verifierOperationKey in that person’s server-side session between the two requests. Do not log or retain the private URL. The person enters the endpoint key on that page; the key is never an SDK argument.
The Connect action calls beginProtected(verifierId, session) and returns its redirect response. After private entry, the person returns to your app manually and uses Continue, which calls finishProtected(verifierId, session). The intent_id saved by the first action is the operation key used by the second. The 90-day stop choice is an example; choose a supported stop policy for your application. For a replacement or restoration, create a new intent for the same verifier_id and repeat the handoff. Read solver.verifiers.connection(verifierId) or solver.verifierConnection.inspect(verifierId, { operationKey }) to see the current state. The SDK also exposes testAndPromote and revoke as lower-level operations; use continue and disconnect for read-backed lifecycle results. To disconnect, obtain explicit confirmation from the person who owns the connection, then save a new operation key for this disconnect:
Keep disconnectKey unchanged if the result asks you to resume. Set the last argument to true only after that explicit confirmation. Millwork stopping use of its key does not revoke that key at your endpoint. The shared connection guide owns the deployment, private entry, governed-run approval, and receipt sequence. Connecting or testing a check never authorizes a paid run.

What the client does

You construct one Solver with apiKey and baseUrl. The client never changes the base URL you give it. Each resource below calls one API endpoint and returns the response typed. A saved model is an arm in the API, an output check is a verifier, and an organization is a tenant. These methods return newest first: receipts.list(), arms.list(), verifiers.list(), and proposals.list(). For arms, verifiers, and proposals, use nextCursor and .next() to fetch older items. The receipts list has no next page. Run submission takes a request key, sent as the Idempotency-Key header. You own the key; the client forwards it unchanged, so a retry with the same key returns the same run instead of starting a second one. Most other writes accept a request key as an option. The client offers no request-key option for cancel, proposal decisions, and template plans, so each of those gets one attempt. Template apply and template resume require a request key. The client retries on its own only when a replay cannot apply work twice: a read, or a write that carries your request key. It retries those after a network failure or a 5xx response, up to two more attempts by default (maxRetries), with exponential backoff from 500 ms (retryBackoffMs), and replays the same bytes each time. It never retries a 4xx response, and a write without a request key gets exactly one attempt.

Connect your provider account

Call modelSourceProfiles.list() before requesting a provider credential. Follow Connect a provider account for the returned method and the handoff, test and model-refresh sequence. A browser step finishing does not mean the connection passed its test or a paid run started. For replacement or removal, sourceConnections.rotate() and sourceConnections.revoke() operate on the existing connection ID. The replacement guide explains when to remove old provider access and how to recover a response you did not receive.

Errors

Every non-2xx response throws SolverApiError. It carries the API’s error object as fields, so you never parse the message: type (the error code), status, detail, instance, errors (per-field messages, when present), and retryAfterS (the seconds to wait, when the API sends one). A network failure, or an error response whose body is not valid JSON, throws SolverApiNetworkError with the underlying cause. Errors and retries lists each error code and the next step. If a run was accepted and the provider later failed, inspect its status, events and receipt. That is a run failure, not necessarily an SDK transport failure; repeat browser setup only when access needs replacement.

API basics

The base URL, bearer authentication, request keys, polling, and the error format.

Make your first API call

Send a free test run with curl and read its receipt.