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.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.
example.py:
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
SetVERIFIER_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:
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. SetVERIFIER_ID to a public check you registered above. Save this
as test_check_async.py and run python test_check_async.py:
Use an output check on a run
First save a model and keep itsarm_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:
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 published0.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_capability.py and run python check_capability.py. It
uses no API key and makes no network request:
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:
connect_check.py and run python connect_check.py:
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:
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:
resume result.
What the client does
Both clients expose onerequest 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 frommillwork_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.