> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getmillwork.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Get your first model answer

> Submit one paid run on a saved model your administrator approved, read the answer, and keep the receipt that records what it used.

**Goal:** Get one real model answer for your organization, with a record of
the run and what it cost. This page continues
[Set up with the CLI](/get-started/tenant-start), which ended with a free test
run. Here you pay for one **live run**: a run that calls a model. A run is one
task you send to Millwork; the API calls it an `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.

Millwork returns the fee and the quota count automatically in one case: it
failed the run before any model attempt started. That happens when no model
could serve it, the output check (the API calls it a verifier) was unavailable,
or the selected model was not ready.
Read [Model access and billing](/concepts/access) for every outcome.

This diagram shows the requests you send with a request key (the value you put
in the `Idempotency-Key` header), the moment the fee is charged, the model call
whose usage appears on the receipt, and the two records you fetch.

```mermaid theme={null}
sequenceDiagram
    participant App as Your app
    participant Millwork
    participant Provider
    App->>Millwork: Submit the paid run with a request key
    Note over Millwork: Platform fee charged
    Millwork-->>App: 202 with execution_id, status queued
    Millwork->>Provider: Call the saved model
    Provider-->>Millwork: Answer and usage
    App->>Millwork: Read the status until completed
    App->>Millwork: Fetch the result
    Millwork-->>App: final_text
    App->>Millwork: Fetch the receipt
    Millwork-->>App: Receipt without prompt or output
```

## Before you begin

You finished [Set up with the CLI](/get-started/tenant-start), 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.
* `curl` and `jq`, in an empty working directory. The examples save the
  request, status, result, and receipt as files there. They never save your
  key.

<Note>
  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.
</Note>

<Steps>
  <Step title="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.

    ```bash theme={null}
    ARM_ID="<approved ready model arm ID>"

    curl --fail-with-body \
      --header "Authorization: Bearer $SOLVERAPI_API_KEY" \
      "https://api.getmillwork.dev/v1/arms/$ARM_ID" |
    jq '{arm_id, kind, provider, model_id, status, data_class_grants}'
    ```

    **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.

    <Accordion title="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:

      ```json theme={null}
      {
        "kind": "model",
        "display_name": "First answer model",
        "provider": "openrouter",
        "model_id": "<approved exact model ID>",
        "credential_ref": "<registered same-organization credential reference>",
        "capability_tags": ["drafting"],
        "data_class_grants": ["public"],
        "cost_class": "standard"
      }
      ```

      `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](/help/contact) 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.
    </Accordion>
  </Step>

  <Step title="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:

    ```bash theme={null}
    jq -n --arg arm "$ARM_ID" '{
      mode: "live",
      task: {objective: "Reply with the single word: ok"},
      policy: {
        data_classes: ["public"],
        budget: {max_cost_usd: 0.05, max_runtime_s: 60},
        on_eval: []
      },
      routing: {required_arm_id: $arm}
    }' > first-answer-request.json
    ```

    What each field does:

    * `mode: "live"` calls a model. A test run (`mode: "echo"`) would not.
    * `data_classes: ["public"]` allows only public information in this task.
    * `max_cost_usd` is 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_s` is 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_id` pins 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.

    If an assistant runs these steps for you, it must get your explicit approval
    before it sends the next request. Approving setup or a provider connection is
    not approval for this run.

    Choose one **request key** for this run and keep it with the request file.
    Millwork reads it from the `Idempotency-Key` header. A retry with the same key
    and the same body returns the same run instead of starting the work twice.

    ```bash theme={null}
    RUN_KEY="<unique key for this approved run>"

    if ! curl --fail-with-body \
      --request POST \
      --header "Authorization: Bearer $SOLVERAPI_API_KEY" \
      --header "Idempotency-Key: $RUN_KEY" \
      --header "Content-Type: application/json" \
      --data @first-answer-request.json \
      --output first-answer-execution.json \
      "https://api.getmillwork.dev/v1/executions"; then
      echo "The request failed. Read first-answer-execution.json before retrying." >&2
      exit 1
    fi

    jq '{execution_id, status, mode}' first-answer-execution.json
    ```

    **Expected result:** the `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.
  </Step>

  <Step title="Wait for a final status">
    Read the id from the saved response, then read the run's status:

    ```bash theme={null}
    EXECUTION_ID="$(jq -er '.execution_id' first-answer-execution.json)"

    curl --fail-with-body \
      --header "Authorization: Bearer $SOLVERAPI_API_KEY" \
      --output first-answer-status.json \
      "https://api.getmillwork.dev/v1/executions/$EXECUTION_ID"

    jq '{execution_id, status, mode}' first-answer-status.json
    ```

    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](/get-started/builder) 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](#if-it-fails).
  </Step>

  <Step title="Read the answer">
    Fetch the **result**, the model's answer, only after `completed`:

    ```bash theme={null}
    curl --fail-with-body \
      --header "Authorization: Bearer $SOLVERAPI_API_KEY" \
      --output first-answer-result.json \
      "https://api.getmillwork.dev/v1/executions/$EXECUTION_ID/result"

    jq '{execution_id, final_text, model_provenance, retention_expires_at}' \
      first-answer-result.json
    ```

    **Expected result:** `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.
  </Step>

  <Step title="Keep the receipt">
    Fetch the receipt for every final status, including `failed` and `cancelled`:

    ```bash theme={null}
    curl --fail-with-body \
      --header "Authorization: Bearer $SOLVERAPI_API_KEY" \
      --output first-answer-receipt.json \
      "https://api.getmillwork.dev/v1/receipts/$EXECUTION_ID"

    jq '{execution_id, status, mode, content_policy, totals,
      routes: [.slices[].route], model_costs: [.slices[].cost]}' \
      first-answer-receipt.json
    ```

    **Expected result:** `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](/concepts/results-and-receipts) says what
    to store and for how long.

    To see your remaining Millwork credit:

    ```bash theme={null}
    curl --fail-with-body \
      --header "Authorization: Bearer $SOLVERAPI_API_KEY" \
      "https://api.getmillwork.dev/v1/account" | jq '{balance_usd: .balance.balance_usd}'
    ```

    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.
  </Step>
</Steps>

## If it fails

| What you see                                               | What it means                                                                                                                                                                                                                                                                                                       | What to do                                                                                                                                                                                                                |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The saved model's `status` is `degraded` or `disabled`     | Millwork refuses a run pinned to it with `invalid_state` (HTTP `409`) before any run is created: no fee is charged and no quota is used. If the model stops being ready after Millwork accepted a run, that run fails with `arm_not_ready` before any model attempt, and the fee and quota come back automatically. | Read `status_reason` and ask your administrator. Do not pick another saved model on your own.                                                                                                                             |
| `insufficient_credit` when you submit                      | Your organization cannot fund the platform fee. No fee was charged.                                                                                                                                                                                                                                                 | Ask an owner to add credit under **Billing & plan**, then submit again with a new request key. Adding credit does not submit a run.                                                                                       |
| `quota_exceeded` when you submit                           | This month's run quota, or the number of runs allowed at once, is used up. No fee was charged.                                                                                                                                                                                                                      | Read `quota.executions.reset_at` from `GET /v1/account` and wait, or ask an owner about your limits.                                                                                                                      |
| `idempotency_conflict` when you submit                     | You reused a request key with a different body.                                                                                                                                                                                                                                                                     | Send the original body, or choose a new key for new work.                                                                                                                                                                 |
| The submit response never arrived                          | Millwork may already have accepted the run.                                                                                                                                                                                                                                                                         | Send the same body with the same request key. Millwork returns the run it already accepted and does not charge a second fee.                                                                                              |
| Still `queued` or `running` after 90 seconds               | The run is not lost.                                                                                                                                                                                                                                                                                                | Keep the id and read its status again later. Do not submit again; both runs would be charged.                                                                                                                             |
| `failed`, `cancelled`, or `expired`                        | The run ended without an answer.                                                                                                                                                                                                                                                                                    | Read the status, events, and receipt. For a failed run, the [pre-attempt failure rule](/concepts/access#if-millwork-cannot-start-the-run) determines whether the fee comes back. Expired and cancelled runs keep the fee. |
| The result returns `404`                                   | The run is not `completed`, or the result passed its `retention_expires_at`.                                                                                                                                                                                                                                        | Check the status. The receipt cannot restore an expired answer.                                                                                                                                                           |
| `pool_not_certified` from `millwork tenant start`          | You ran the default command. It needs a model Millwork provides, and none is approved for the preview yet. Nothing was created.                                                                                                                                                                                     | Use `--template starter` for setup and this page for the answer. Retrying or adding credit cannot make the missing model appear.                                                                                          |
| A missing key, preview approval, invitation, or permission | Your access is not complete.                                                                                                                                                                                                                                                                                        | Follow [If the plan is blocked](/get-started/tenant-start#if-the-plan-is-blocked). Retrying creates no organization or key.                                                                                               |

[Errors and retries](/guides/errors-and-retries) lists every error code and
what to send support without sharing a secret.

## Next step

<Card title="Run a model" icon="play" href="/get-started/builder">
  Let Millwork choose from your catalog instead of pinning one saved model.
</Card>
