Skip to main content
Use the Python client to submit a run (one task you send to Millwork; the API calls it an execution), follow its progress, and read its receipt. It includes a synchronous Solver and an asynchronous AsyncSolver. Both use the same requests and organization API key as the REST API. The published 0.1.2 wheel can register and test a public output check (a verifier in the API) and manage a protected check through private key entry.

Install

Requires CPython 3.11 to 3.14 and an organization API key.
The package is millwork-solver on PyPI. Import it as millwork_solver. For output checks, use the generated REST lifecycle reference for operation IDs and exact fields, and the verifier-dock contract for the endpoint request and response. Keep protected endpoint keys on Millwork’s private entry page. For a protected endpoint, use the published Python connection flow. The key is entered only on Millwork’s private page, never in a Python argument.

Make a test run

First configure your organization key in the same terminal. This example submits a test run and reads its receipt without calling a model. Solver() reads SOLVERAPI_API_KEY and SOLVERAPI_BASE_URL from the environment. You can also pass them as api_key and base_url.
Save this as example.py:
Run it in the same Python environment where you installed the client:
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. AsyncSolver has the same calls: await client.request(...), async for over pages and events, and async with or aclose() to close.

Register and test a public output check

Set VERIFIER_URL to your running endpoint’s HTTPS address. The endpoint must implement the output-check contract. Save this as output_check.py and run it with python output_check.py in the same environment where you installed the pinned version above:
Keep the returned verifier_id. This test observes endpoint health; it does not authorize a paid run. AsyncSolver accepts the same operation IDs through await client.request(...). The shared connection guide owns deployment, protected key entry, run approval, and receipt inspection. For the endpoint’s exact request and response fields, read the verifier-dock contract. The published 0.1.2 wheel has these verifier operation IDs. The generated REST lifecycle reference may list newer REST operations that this wheel cannot call by request(). For get, patch, test, and delete, pass path_parameters={"verifierId": verifier_id}.

Test a public check with AsyncSolver

The published wheel also supports asynchronous calls to its public verifier operations. Set VERIFIER_ID to a public check you registered above. Save this as test_check_async.py and run python test_check_async.py:
This tests endpoint health without submitting a model run. Protected keys still go through the private entry flow.

Use an output check on a run

First save a model and keep its arm_id. Connect and test a public 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.py and run python checked_run.py 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

The published 0.1.2 wheel includes verifier_connection. Register your endpoint without a key as shown above and keep its verifier_id.
Create an intent, send only its continue_url to the intended person for private key entry, and continue the connection until the server confirms done. A new intent follows the same path when you replace or restore a key. This connection does not authorize a paid run.
Check the wheel installed in the Python environment you will actually run. Save this as check_capability.py and run python check_capability.py. It uses no API key and makes no network request:
For 0.1.2, the last line prints True. If it prints False, check that this script uses the Python environment where you installed 0.1.2 before attempting a connection. Set VERIFIER_ID to the registered endpoint, MILLWORK_APP_ORIGIN to the trusted origin of your Millwork private-entry app, and STOP_DAYS to the chosen 30, 90, 180, or 365-day Millwork stop period. The origin must come from your deployment configuration, not from the returned URL. For the hosted app:
This example runs on the intended person’s computer. A server app must hand the URL to that person through its private channel instead of opening its own browser. Save this as connect_check.py and run python connect_check.py:
Keep the operation key for recovery. enter_key means the person has not finished private entry; resume means keep that key and retry with a fresh request key. start_again means correct the endpoint or key and create a new intent. inspect means read the operation before another change. A refusal reports a sanitized status and title; 401 or 403 raises ProblemError. Only read-backed done confirms the connection. Do not log the private URL or endpoint key. After the owner separately confirms disconnection, use a new operation key. This snippet is not part of connect_check.py:
If the result asks for resume, keep the same disconnect operation key for the next attempt. Millwork stopping use of a key does not revoke it at your endpoint.

Async protected connection

AsyncSolver.verifier_connection is an AsyncVerifierConnection. Its six coroutine methods have the same arguments and outcomes as the synchronous methods; await each call: The normal path uses continue_connection(); it handles the lower-level test_and_promote() call and reads back the result. disconnect() similarly handles revoke(). This runnable example starts an async intent and continues it after private entry. Set the same environment values as above and save it as connect_check_async.py:
For a separately confirmed async disconnect, use the same confirmation boundary and await the result:
Keep the same operation key for an uncertain disconnect or a resume result.

What the client does

Both clients expose one request call: name the REST operation by its operation ID from the API reference and pass path parameters, query values, and a body. pages walks a list one page at a time, and lists of your organization’s own items, such as saved models, return the newest first. events follows a run’s events in the order they happened until a final status. A test run (the API calls it Echo, mode: "echo") reaches a final status and returns a receipt. It calls no model, so it has no result content and no platform fee, and reading its result is not part of the test-run flow. You own the request key. Pass it as idempotency_key when you submit a run and the client sends it as the Idempotency-Key header. The client never makes one up for you, and a submit without one is sent once. A read (GET or HEAD) that hits a network failure or a 5xx response is retried, up to two times by default, with a doubling wait that starts at 500 milliseconds. A write with a request key is retried the same way. A write without a request key is never retried by the client. A 429 is not retried either; read retry_after_seconds and wait. Change the defaults with max_retries and retry_backoff_ms on the constructor, or with SOLVERAPI_MAX_RETRIES and SOLVERAPI_RETRY_BACKOFF_MS in the environment.

Errors

Every exception is importable from millwork_solver. An empty list is a normal answer, not an error. For what each error code means and which are safe to retry, read Errors and retries.

API basics

Authentication, request keys, and the response shapes the client returns.

Make your first API call

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