execution. The CLI and API
call an organization a tenant.
You are done when: the run’s status is completed, final_text in its
result holds the model’s answer, and its receipt names the saved model you
asked for and the usage the provider reported. A saved model is a model
your administrator added to your organization; the API calls it an arm. A
receipt is the record of a run without its prompt or output.
What this does not finish. A completed run proves that this saved model
answered a paid request. It does not move your setup application past
echo_proved, and its receipt does not confirm which model OpenRouter
actually ran. Step 5 says what the receipt does confirm.
What this run costs
- Platform fee: charged when Millwork accepts the run, not when it completes. If you cancel, the fee and the quota count stay, and Millwork does not stop a model call that is already running.
- Model usage: this run uses a saved model on OpenRouter. OpenRouter bills the provider account your administrator identified, under that account’s terms. Millwork bills nothing for the usage.
Idempotency-Key header), the moment the fee is charged, the model call
whose usage appears on the receipt, and the two records you fetch.
Before you begin
You finished Set up with the CLI, so you already have:- an organization approved for the private preview;
- an organization API key in an approved secret store, available as
SOLVERAPI_API_KEY; - a setup application at
echo_proved: you fetched the receipt for its test run. A test run (the API calls it Echo) has no platform fee and no model cost.
SOLVERAPI_API_KEY holds the same organization key that the other REST guides
call MILLWORK_API_KEY. This page keeps the name you set during CLI setup.
This page also needs:
- A saved model your administrator approved. Your administrator saves one
model on OpenRouter for this organization, confirms its status is
ready, and gives you its id and the name of the provider account that pays for its usage. Millwork does not create that provider account or add credit to it; step 1 says what the administrator sends instead. - Credit and quota. Your organization needs Millwork credit for the platform fee, and room in your quota for one more live run. Your quota is the number of runs your organization may submit per calendar month (UTC), and how many may run at once. An owner can check both in the dashboard under Usage & spend. The provider account needs its own credit with OpenRouter.
curlandjq, in an empty working directory. The examples save the request, status, result, and receipt as files there. They never save your key.
You do not run
millwork tenant start again on this page. Without --template starter, that command uses its default setup, which
needs a model Millwork provides. With today’s catalog it stops with pool_not_certified
and creates nothing. The steps below call the API directly.1
Confirm the saved model
Ask your administrator for the exact id of the approved saved model. Do not
pick a different saved model because it appears first in a list.Expected result:
provider is openrouter, kind is model, status
is ready, and data_class_grants includes public. Keep ARM_ID; every
later step pins the run to it.ready means the saved model’s configuration passed Millwork’s checks. It does
not prove that the next provider call will succeed.If your administrator still needs to save the model
If your administrator still needs to save the model
An administrator saves the model with
POST /v1/arms and an organization API
key. The request body is:credential_ref names a provider key already stored with Millwork for this
organization. It is a reference, not the provider key itself. This path
requires an existing reference from your administrator. If your organization
does not have one, ask support to arrange secure setup before
continuing. Send the provider name and organization ID, never the key.Leave out endpoint and model_deployment_id for this kind of saved model.
The response returns arm_id, status, and status_reason. Saving a model
does not call it and does not add credit. If the response is lost, list the
saved models with GET /v1/arms before saving the same model a second time.2
Review and submit one paid run
Write the request to a file first, so you can review it before anything is
charged. This task asks for one word and pins the run to your saved model:What each field does:Expected result: the
mode: "live"calls a model. A test run (mode: "echo") would not.data_classes: ["public"]allows only public information in this task.max_cost_usdis the model-usage budget. Once recorded spend reaches it, Millwork starts no further provider calls. A call already started can finish above this amount. The platform fee is separate.max_runtime_sis the run’s time limit.on_eval: []means Millwork does nothing more on its own after the output check, not even a switch to another saved model.routing.required_arm_idpins the run to your saved model.- There is no
verifier_id, so Millwork applies its built-in output check. A basic check confirms only that output is present. It does not judge whether the answer is right.
Idempotency-Key header. A retry with the same key
and the same body returns the same run instead of starting the work twice.jq line prints execution_id, "status": "queued",
and "mode": "live" from the 202 response. On failure, curl reports the HTTP
status and the problem body lands in the file. Millwork charged the platform fee at this moment. 202
means Millwork accepted the run, not that it completed. Keep
execution_id; every status, result, receipt, and support request uses it.If the response is lost, send the same body with the same request key. Do not
submit a second run to find out what happened to the first; both would be
charged.3
Wait for a final status
Read the id from the saved response, then read the run’s status:While
status is queued, running, or progress, repeat only this request,
at least two seconds apart. Stop after 90 seconds and keep the id; a run still
in progress is not lost. Do not submit again.
Run a model shows a complete polling example with its
own environment setup.Expected result: status is completed. The other final statuses are
failed, cancelled, and expired. For those, skip to
If it fails.4
Read the answer
Fetch the result, the model’s answer, only after Expected result:
completed:final_text holds the model’s answer, in this example the
word ok. retention_expires_at is when the result stops being available, or
null when Millwork has not reported an expiry. Copy the answer while you have
it; the receipt never contains it.For this kind of saved model, model_provenance is null. Millwork did not
record which model OpenRouter actually ran. Do not fill that in from the saved
model’s name or from a catalog entry.5
Keep the receipt
Fetch the receipt for every final status, including Expected result: This is your Millwork balance, not your OpenRouter balance, and it does not
itemize this run. If your role cannot see the balance, ask an organization
owner. A missing value is not zero.
failed and cancelled:execution_id matches, mode is live, and
content_policy is content_free. In each route, required_arm_id and
selected_arm both equal your ARM_ID, and fallback_chain is empty.
model_costs holds the tokens and model cost the provider reported, and
totals sums them. Neither includes the platform fee.The receipt confirms which saved model answered and the usage the provider
reported.
It does not confirm which model OpenRouter actually ran. Keep the receipt as
your record; Results and receipts says what
to store and for how long.To see your remaining Millwork credit:If it fails
Errors and retries lists every error code and
what to send support without sharing a secret.
Next step
Run a model
Let Millwork choose from your catalog instead of pinning one saved model.