Docs

Quickstart

Add Amdahl to Claude or ChatGPT as a connector, connect a coding agent in one command, or get an API key and have Amdahl write a message from what you know about the recipient, with no data connected. Once your conversations sync, check what the workspace holds, ask it a question, and grade a draft against your own customers.

Your first result needs no data: tell Amdahl who you are writing to and what you know about them, and it writes the message in your own voice. Pick the path that fits you.

Pick your path

You use the Claude or ChatGPT app. Add Amdahl as a connector with the URL https://app.amdahl.ai/mcp and sign in with your work email. The steps are on Connect your agent. Then open a new chat and say who you want to write to and why.

You use a coding agent (Claude Code, Codex, Cursor). Run one command. It tells you how to sign in from the client, with your work email. There is no key to copy.

bash
npx @amdahl/cli install claude-code

Use codex or cursor in place of claude-code for those clients. Then paste this into the client:

text
Load and follow https://docs.amdahl.ai/skills/amdahl-optimizer/SKILL.md

Other clients are on Connect your agent. Onboard with an agent has the longer first run.

You are writing code or running a server. Get an API key and follow the steps below.

You have no workspace yet. Sign in at console.amdahl.ai/try with your work email and try the optimizer on your own recipients with 25 free runs. Your first run also puts you on the beta waitlist. When we let you in, create your workspace at console.amdahl.ai/new. If your company already uses Amdahl, ask an admin there to add you.

The API, step by step

Every call below goes to one base URL and sends your key in the X-API-Key header (Authorization: Bearer works the same):

code
https://app.amdahl.ai/api/platform/v1

Every successful JSON response is wrapped in a data envelope, so the field you want is always one level in: .data.results, not .results. Error shapes are on Errors.

What you are about to do
  1. Get a key

    One command, approved in the console. Access: Customer agent.

    amdahl keys create

  2. Write a message

    What you know about the recipient in, a message out. No data needed; usually 1 to 2 minutes.

    messages.optimize

  3. Check what is there

    Once synced. Row counts and dates. Works on any workspace, even an empty one.

    search.overview

  4. Ask a question

    Once synced. Plain language in, rows and the SQL it ran back out.

    search.query

  5. Grade a draft

    Once synced. A pass or fail per rubric line, with the customer quotes behind it.

    evals.run

Not sure which call fits your job? See Which call when. The API reference also sends any request on this page from your browser.

Step 1: Get an API key

bash
npx @amdahl/cli login
npx @amdahl/cli keys create --name "Quickstart" --preset agent

login signs you in through your browser. keys create shows a short code and opens the console. Type the code there and click Approve, and the terminal prints the key once. See Command line (CLI).

Prefer the console? Open your workspace at console.amdahl.ai, go to Settings, then Developer, and click Create key. Keep Access at Customer agent (the default). Copy the key when it is shown; it is not shown again.

You need Customer agent access: it can optimize (messages:execute) and run evals. A Read only key can run steps 3 and 4 but gets 403 on steps 2 and 5. See Authentication.

bash
export AMDAHL_KEY="amdhl_your_key_here"
export AMDAHL_BASE="https://app.amdahl.ai/api/platform/v1"

To confirm the key can optimize before you start:

bash
curl -s "$AMDAHL_BASE/setup/status" -H "X-API-Key: $AMDAHL_KEY" | jq '.data.optimize'

allowed: true means step 2 will work. Otherwise blocker names the one thing to fix (missing_scope, role_too_low or quota_exhausted). The full response is on Connections.

Step 2: Write a message (no data needed)

This is the fastest first result, and it works on a brand-new workspace. Tell the optimizer who the recipient is, why you write now, what you offer and what you want them to do, one fact per entry. It writes the message from that, scores each version it tries, and returns the one that graded best. A draft is optional: see Context.

bash
curl -s -X POST "$AMDAHL_BASE/messages/optimize" \
  -H "X-API-Key: $AMDAHL_KEY" \
  -H "Content-Type: application/json" \
  --max-time 310 \
  -d '{
    "channel": "email",
    "context": {
      "facts": [
        "Sam Ortiz is Head of RevOps at Brightpath, a 200-person logistics software company.",
        "Brightpath posted three SDR roles last week.",
        "We sell Relay, which drafts first-touch emails from CRM notes. A 150-person team using it cut SDR ramp from 90 to 45 days."
      ],
      "rules": ["Ask for a 20-minute call next week."],
      "max_words": 90
    },
    "include_tries": true
  }' | jq

It usually takes 1 to 2 minutes and returns on the same call; there is nothing to poll. Let the call wait up to 310 seconds before giving up. The response has this shape (an illustration, not a real run; run_id, notes and tries left out):

json
{
  "data": {
    "ok": true,
    "outcome": "rewritten",
    "message": "Hi Sam,\n\nSaw Brightpath posted three SDR roles last week. Ramping three new reps at once is a lot of first emails to learn.\n\nRelay drafts first-touch emails from your CRM notes. A 150-person team cut SDR ramp from 90 to 45 days with it.\n\nWorth a 20-minute call next week?",
    "lift": null,
    "evidence": { "requested": "off", "used": false, "reason": "not_requested" },
    "on_unsupported": "flag",
    "unsupported_claims": null,
    "context": { "rules": 1, "voice_examples": 0, "facts": 3, "max_words": 90 }
  }
}
  • The message is only as good as the facts. Every specific line above comes from a fact. Send one thin fact and you get a generic message. Context says what to send.
  • Check what arrived. context counts the facts and rules the optimizer received. If facts is 0 or 1, add more before you judge the message.
  • tries shows the path. include_tries: true adds every version the run scored, each with its Optimizer score, human_tone, what it aimed_at, and whether it was the one returned. Those scores come from the optimizer's own grader during the search, so they show the path, not proof. See Reading tries.
  • Use message as returned. Do not polish it with another model. To change it, add a rule or a fact and run it again, or edit it yourself.
  • It is a style pass. With no evidence, the message is judged on how it reads and how clearly it asks. Nothing checked its claims against your customers, which is why unsupported_claims is null.
  • Say the ask in a rule. Without one, the optimizer may close with a question the reader can answer in one line.
  • A message field is the starting point. If your code sends a draft with facts or rules, the message starts from it and keeps what it says. A draft sent alone is improved, and the response carries a draft_improved_no_context note. Put what your code knows into the facts of context.
  • ok: false means nothing was written and there is no message field. Read reason, or error.code for quota_exceeded: API keys and connected agents get 1,000 optimizer calls per workspace per month, then 429. On success, omitted lists what the message left out because your context could not back it, each with why; [] means nothing.
  • Once your conversations have synced, add "evidence": "workspace" to also grade the message against what your customers said (slower). On a workspace with recipient evidence turned on, also send recipient, and Amdahl adds what it finds about them in your own data. See Recipient fields.

Every field of this response, read end to end, is in Optimize a cold email; the reference is Message Optimizer.

Once your conversations are synced

Steps 3 to 5 read your own customer conversations. On a workspace with nothing synced yet they return empty results and not_applicable, so connect a CRM or call recorder in the console first and wait for the first sync. Step 3 tells you whether it has landed.

Step 3: Check what is there

This call needs nothing but a key, and it tells you whether the rest of this page has anything to read:

bash
curl -s "$AMDAHL_BASE/search/overview" -H "X-API-Key: $AMDAHL_KEY" | jq
json
{
  "data": {
    "surfaces": [
      { "surface": "interactions", "row_count": 41822, "earliest_at": "2025-01-06T15:02:11.000Z", "latest_at": "2026-09-27T21:40:03.000Z", "time_field": "timestamp" },
      { "surface": "deals", "row_count": 612, "earliest_at": "2024-11-30T00:00:00.000Z", "latest_at": "2026-12-31T00:00:00.000Z", "time_field": "close_date" },
      { "surface": "deal_qualification", "row_count": 188, "earliest_at": "2026-06-02T08:15:40.000Z", "latest_at": "2026-09-28T06:01:12.000Z", "time_field": "computed_at" }
    ]
  }
}
  • Every row_count is 0: your workspace has not synced any conversations yet. Search will return empty results and the eval in step 5 will come back not_applicable with empty_corpus. Connect a CRM or call recorder in the console, wait for the first sync, and come back. There is no shared demo workspace today.
  • A row_count is null: that read failed. It is not a zero. Retry.
  • latest_at on interactions is your newest conversation. On deals it is the latest close date, which can be in the future.

Also data-free: GET /evals/prompt-and-message-eval returns the eval you will run in step 5, with its input fields.

Step 4: Ask a question

POST /search/query runs one blocking call over your warehouse. Ask in plain language with "mode": "fuzzy" and it turns the ask into SQL, runs it, and returns the rows and the SQL:

bash
curl -s -X POST "$AMDAHL_BASE/search/query" \
  -H "X-API-Key: $AMDAHL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "how many calls did we have in the last 30 days?", "mode": "fuzzy" }' \
  | jq
json
{
  "data": {
    "success": true,
    "mode_ran": "fuzzy",
    "results": [{ "calls": 214 }],
    "compiled": {
      "sql": "SELECT COUNT(DISTINCT interaction_id) AS calls FROM interactions WHERE …"
    },
    "detail": {
      "internal": { "status": "ok", "rows": [{ "calls": 214 }], "row_count": 1 },
      "message": "Found 1 row(s) in your workspace data.",
      "coverage": {
        "latest_event_at": "2026-07-21T05:01:50Z",
        "total_rows": 41822,
        "days_behind": 1,
        "is_stale": false
      }
    }
  }
}

Past parameter validation this lane returns success: true and reports problems as typed fields: detail.internal.status is ok or failed. compiled.sql is the query as you asked for it; the gate adds your workspace filter before running it. Typed filters, meaning-based matching and exact-phrase search go through the same endpoint; see Search.

Step 5: Grade a draft

This is the call the product is built around. Send a drafted message, and optionally the prompt behind it; the eval grades it against quotes from your own conversations and writes an improved version:

bash
curl -s -X POST "$AMDAHL_BASE/evals/run" \
  -H "X-API-Key: $AMDAHL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "eval": "prompt-and-message-eval",
    "inputs": {
      "prompt": "Write a short first-touch email to a VP of Engineering about our rollout tooling.",
      "message": "Hi Dana - saw Northwind is scaling fast. We help engineering teams ship 10x faster with best-in-class rollout tooling. Worth a quick 15 minutes next week?"
    }
  }' | jq

If the draft leans on something you know first-hand that no recorded conversation holds (what the recipient told you at a dinner, say), add it beside inputs so the grade does not mark a true claim as invented:

json
"context": {
  "facts": ["Met Dana at the GTM dinner on Sep 24; she said her SDRs rewrite every AI draft."]
}

A fact licenses exactly what it says and no more, and is never counted as a customer quote. See What you know.

It returns a handle at once, not a verdict:

json
{
  "data": {
    "success": true,
    "run_id": "b1c2d3e4-...",
    "reused": false,
    "status": "queued",
    "eval_slug": "prompt-and-message-eval",
    "eval_version": "2.38.0",
    "resource": "eval-run://b1c2d3e4-...",
    "console_url": "https://console.amdahl.ai/..."
  }
}

A full run takes a few minutes. Wait for it with wait_ms, which makes the server block for up to 30 seconds per read instead of you polling in a tight loop:

bash
RUN_ID="b1c2d3e4-..."
until s=$(curl -s "$AMDAHL_BASE/eval-runs/$RUN_ID?wait_ms=30000" -H "X-API-Key: $AMDAHL_KEY" | jq -r .data.status);
      [[ "$s" =~ ^(complete|failed|canceled)$ ]]; do :; done
echo "$s"

The finished status is complete, never completed. Then read the report card:

bash
curl -s "$AMDAHL_BASE/eval-runs/$RUN_ID/report" -H "X-API-Key: $AMDAHL_KEY" | jq -r .data.report.markdown

The card leads with how many rubric lines your draft passed, the quotes it was graded against, and the improved prompt. Three things to know when you read it:

  • Quote the checks fraction (headline.submitted.checks on the report, for example 1 of 5). It is the only number on the card about your writing.
  • The improved message is an illustration, marked illustration_only. The improved prompt is what to keep.
  • not_applicable is not a fail. It means nothing was graded, and not_applicable_reason says why.

In a pipeline, send "mode": "gate" inside inputs and read GET /eval-runs/{id}/gate for a send/hold bit. See Gate before send.

Step 6: Hand it to an agent

With Amdahl connected to your AI client (see Pick your path), paste:

text
Load and follow https://docs.amdahl.ai/skills/amdahl-optimizer/SKILL.md

The agent finds a few people you are about to write to in the tools it can reach, gathers what it knows about each one, and has Amdahl write each message. It shows you each message and every try it scored, and changes nothing until you approve. After that, "write to this person" works for any recipient.

A coding agent such as Codex or Claude Code can use the key instead: set AMDAHL_KEY in its environment, not in the chat. Onboard with an agent has the full tour, which goes on to search and evals once your data is synced.

What's next

  • Context: what to send so the message is specific.
  • Optimize a cold email: one optimizer response, every field read end to end.
  • Optimize a campaign and Optimize before you send: the Optimizer on a whole spreadsheet of recipients, and in front of the code that sends them.
  • Message Optimizer: the fields, the monthly cap, and what happens when it cannot write a message.
  • Primitives: the Optimizer, the two calls over your corpus, and the orchestration layer on top.
  • Patterns: cohort then voice, gate before send, pinned-evidence A/B.
  • Grade a cold email: one eval run read end to end.
  • Chat: hand a multi-step investigation to a server-side agent and poll for the answer.
  • Read the docs over MCP: point your coding agent at https://docs.amdahl.ai/mcp and it reads these pages itself. Public, no key.

If something breaks, start with Errors, then Rate limits if you see 429.