Docs

Improve a prompt

messages.optimize_prompt grades a reusable prompt by the emails it writes for real recipients, and in improve mode rewrites it. It runs for minutes and returns a run_id at once; messages.get_prompt_run reads the grade, the emails and the held-out lift.

A prompt is the instructions a writer follows to write an outbound email to any recipient: the template behind a sequence, an agent's system prompt, a team's playbook. messages.optimize_prompt grades a prompt by the emails it writes for real recipients. In improve mode it also rewrites the prompt, and it returns the revision only when the revision wrote better email on recipients it did not learn from.

The grade measures the message against your evidence and this rubric; it does not predict replies.

OperationRESTMCPScope
Start a prompt runmessages.optimize_promptPOST /messages/optimize_promptoptimize_promptmessages:execute
Read a prompt runmessages.get_prompt_runGET /messages/optimize_prompt/{id}get_prompt_runmessages:execute

Both need the editor role or above. The run is also a resource, optimize_prompt_run://<id> (over MCP, optimize-prompt-run://<id>). To grade one message, see Grade a message.

A prompt, not a message

A prompt that names one recipient, holds one fixed email to rewrite, or asks for copy that is not an email is one message, not a template. Such a prompt is refused with invalid_argument and details.reason: "prompt_is_one_off" before it runs. Write that email with messages.optimize instead.

What a run does

Both modes write one email per recipient from the prompt. Each recipient is resolved from your CRM and calls, as an optimize_message recipient is, so each email is written from what Amdahl knows about that account.

  • grade: each email is graded by Amdahl's final judge (Claude Sonnet 5.5 with thinking off) on the five rubric lines. The prompt's grade is the mean of its emails' scores, with the pass rate of each line.
  • improve (the default): the recipients are split in two. The first half are train recipients. Their emails are graded by the in-loop judge, and the run revises the prompt from the lines that failed, up to two times. A revision may not add a product claim, a number, a customer name or an outcome that the prompt does not supply, and a revision much longer than the original is refused before it is graded. The rest are held-out recipients. The original prompt and the best revision each write one email per held-out recipient, and the final judge grades them. lift is the held-out difference only.

A run takes minutes. The call returns a run_id at once.

Start a run

json
{
  "prompt": "Write a first-touch email to a Head of Platform at a company that just shipped SSO. Open with what they shipped, name the next manual job SSO leaves, and ask for a 15-minute call. Under 90 words.",
  "mode": "improve",
  "recipients": [
    { "recipient": { "name": "Jordan Lee", "company": "Corvid Health", "attributes": { "title": "Head of Platform" } } },
    { "recipient": { "company": "Brightpath" } },
    { "recipient": { "company": "Helix" }, "recipient_notes": "Met their CTO at the GTM dinner." },
    { "recipient": { "domain": "northwind.com" } }
  ],
  "context": {
    "facts": ["We automate user provisioning after SSO."],
    "rules": ["Never mention pricing."]
  },
  "idempotency_key": "sso-prompt-2026-10-22"
}
FieldRequiredNotes
promptyesThe reusable prompt, up to 20,000 characters.
modenoimprove (the default) or grade.
recipientsno1 to 6 sample recipients. Each is { recipient, recipient_notes?, label? }: recipient holds any of name, company, email, domain, linkedin_url and attributes (such as title); recipient_notes is what you know about this recipient, given to the writer as a fact; label is shown on the emails written for them (default: name and company). improve needs at least 2.
contextnoYour rules, facts, voice_examples and constraints, shared by every email the prompt writes. Facts here are about the sender or the offer, not one recipient.
channelnoemail or linkedin.
idempotency_keynoAny string. A second call with the same key within 24 hours returns the run the first call started, in its mode, and starts no second run. One that asks for another mode (an omitted mode is improve) is refused with 409 conflict (reason: idempotency_key_reused), and nothing runs.

With no recipients, Amdahl picks up to 6 of your workspace's own accounts whose customers spoke on calls or meetings, under your data access. Their company names appear on the emails. A workspace with no such account is refused with no_sample_accounts: send recipients.

The call returns 202:

json
{
  "data": {
    "success": true,
    "run_id": "opt_9b1d40",
    "status": "pending",
    "mode": "improve",
    "recipients": { "labels": ["Jordan Lee, Corvid Health", "Brightpath", "Helix", "Northwind"], "sampled": false },
    "replayed": false,
    "poll": { "uri": "optimize_prompt_run://opt_9b1d40", "wait_ms_max": 30000 }
  }
}

replayed is true when an idempotency_key returned a run an earlier call started. A retry without the key starts a second run, unless the request is identical, in which case it names the same run.

Read the run

GET /messages/optimize_prompt/{id}?wait_ms=30000 (MCP get_prompt_run with run_id and wait_ms) holds the read for up to 30 seconds and returns when the run settles. While it runs, the result is run_id, status (pending or running) and phase. Only the sender can read a run: another member's run, another workspace's run and an unknown id return the same not-found.

A finished improve run:

json
{
  "data": {
    "run_id": "opt_9b1d40",
    "status": "complete",
    "prompt_mode": "improve",
    "outcome": "improved",
    "prompt": "Write a first-touch email to ...",
    "grade": {
      "original": { "mean": 2.6, "n": 2, "emails_passing_bar": 0, "bar": 4.2, "lines": { "cta_clarity": { "passed": 2, "total": 2, "rate": 1 } } },
      "revised": { "mean": 3.4, "n": 2, "emails_passing_bar": 1, "bar": 4.2, "lines": { "cta_clarity": { "passed": 2, "total": 2, "rate": 1 } } }
    },
    "lift": null,
    "lift_reportable": false,
    "lift_not_reportable_reason": "held_out_under_3",
    "emails": [
      {
        "recipient": 2, "label": "Helix", "variant": "original", "split": "held_out", "email": "...",
        "grade": { "score": 2.6, "passed": 2, "total": 5, "...": "..." },
        "placeholders": [{ "placeholder": "[First Name]", "asks": "What is the recipient's first name?", "source": "writer" }],
        "unsupported_claims": []
      }
    ],
    "train": { "recipients": [0, 1], "candidates": [{ "name": "revision_1", "train_mean": 3.0, "n": 2, "refused": null }], "kept": "revision_1", "judge": { "model": "...", "judge_version": "...", "role": "in_loop" } },
    "judges": { "final": { "model": "claude-sonnet-5-5", "judge_version": "v7", "role": "final" } },
    "disclosures": { "single_run": true, "not_reply_prediction": true, "train_scores_are_not_lift": true, "repeat_spread": { "...": "..." } }
  }
}
FieldNotes
statuspending, running, complete or failed.
outcomeimproved (the revision wrote better email on the held-out recipients), unchanged (no revision beat the original there) or graded (a grade run).
promptThe revised prompt. Present only when outcome is improved.
grade.original, grade.revisedEach is mean (1 to 5, or null when no email was graded), n (emails graded), emails_passing_bar, bar (4.2) and lines, the pass rate of each line (passed, total, and rate, which is null for a line that was never graded). On an improve run they are over the held-out emails. revised is null on a grade run.
lift, lift_reportable, lift_not_reportable_reasonSee Reading lift.
emails[]Each email the run wrote and graded: the recipient's position in your request, its label, the variant (original or revised), the split (held_out, graded or train), the email text, its grade, and its placeholders and unsupported_claims. See Checks on each email.
trainimprove only. The candidates the loop scored on the train recipients, which one it kept, and why a candidate was refused (for example too_long, adds_figures, adds_claims or leaves_placeholders). These are the in-loop judge's scores.
judges.final, disclosures, notesThe judge that graded what you read, and the same disclosures a grade carries, plus train_scores_are_not_lift on an improve run.

A run that finished with a result Amdahl could not read returns an error with code optimizer_error, and it is not billed.

Checks on each email

Each email carries two checks. For both, null means the check did not run and [] means it ran and found nothing. An email the writer did not write has null for both. The claim check also does not run (null) for a recipient Amdahl found no quotes for, or when the grade failed.

  • placeholders: the unfilled slots the email still holds, such as [First Name], {company} or XX%. Each is { placeholder, asks, source }, the same shape messages.optimize returns: asks is one question that names the missing fact, and source is caller when your prompt or context already held the slot (a merge field), sender when the slot stands for the sender rather than the recipient ([Your Name], or a name slot in the sign-off), and writer otherwise. A writer slot means the prompt does not give the writer what that slot needs. The writer signs with the sender's name and company when Amdahl has them, and otherwise leaves the name line out. A merge tag that your prompt or one of your rules says to keep as written (keep {{first_name}} as written) gets no row.
  • unsupported_claims: the claims in the email that nothing backs, neither what Amdahl read for that recipient from your CRM and calls nor the facts you sent. Each is { span, kind, why, dimension, status, fix }, the same shape messages.optimize returns. span is verbatim from the email. status is always kept, because nothing rewrote the email. The add_evidence fix asks for a source in the facts of that recipient's context.

The checks do not change an email's grade. In improve mode, a revised prompt is never returned when one of its emails holds an unfilled placeholder, other than a sender slot. A candidate whose train emails hold one is refused with leaves_placeholders. A kept revision whose held-out emails hold one is not returned, whatever its grade: outcome is unchanged, there is no prompt, and notes carries revision_left_placeholders. grade.revised and lift still report what was measured. The original prompt is never refused: its emails carry the same checks, so you can see its slots.

Reading lift

lift is the revised prompt's held-out mean minus the original's. It is a number only when lift_reportable is true. Otherwise it is null, and lift_not_reportable_reason says why: held_out_under_3 (fewer than 3 held-out emails on a side), no_revision (no revision was kept) or a_side_not_graded. Send 6 recipients for a reportable lift on 3 held-out emails.

The train scores are never lift. A prompt revised against the emails it graded always looks better on those emails, so only the recipients it did not learn from can say whether it improved.

A held-out lift says the revised prompt wrote email that met this rubric better, for these recipients, on one reading. It does not predict replies.

Refusals

A refused call runs nothing and is not billed.

Codedetails.reasonWhen
invalid_argumentprompt_is_one_offThe prompt reads as one message. Use messages.optimize.
invalid_argumentimprove_needs_two_recipientsimprove with no recipients, when only one sample account was found. Send recipients, or use grade.
invalid_argumentno_sample_accountsNo recipients, and no account in the workspace has customer speech to write to.
invalid_inputnoneA malformed field, for example an empty prompt, more than 6 recipients, or improve with fewer than 2 recipients. The 400 names the field.
quota_exceedednoneThe workspace's monthly cap is used up.
rate_limitedoptimizer_busyThe Optimizer is failing requests right now. Wait details.retry_after_seconds.
optimizer_unconfigured, optimizer_errornoneThe Optimizer is not available.

Billing

A prompt run is billed once, when it finishes, on its own meter (prompt_optimize_run). A refused run, a failed run and a run whose result could not be read are not billed. Whether a run is billed is decided by the credential that started it, not the one that read it. With an API key or an external OAuth token, a run counts toward the same monthly cap as an optimize, and the cap is checked before the run starts.

Prompts graded before 22 October 2026

Until 22 October 2026, evals graded a prompt on a 5-line prompt rubric. That rubric did not track the quality of the emails a prompt writes, and evals retired that day. Its scores do not compare with a grade from this call. See the changelog.