Quickstart
Go from a new account to your first trace and your first cost finding in about 10 minutes. Then tell Morse what success means, so your weekly performance brief can rank your agents.
Morse works in three moves: measure → prioritize → verify. This page covers measure: getting your runs, spend and outcomes into Morse. The brief that prioritizes fills in as labeled outcomes build up. These are the same three steps as Connect Morse , and the setup wizard walks you through them in the app.
Morse TVListen · 3 short clips · 1:21
- 1How do I send my first trace?
- Your API key is shown once: copy it
- Python, TypeScript or OpenTelemetry
- One real model call gives you a cost
Transcript
Step one, instrument one agent. In the setup wizard, generate an API key. It is shown once, so copy it into your environment now. Pick one path: the Python SDK, the TypeScript SDK, or plain OpenTelemetry. Then run your agent with one real model call. Without that call, Morse has nothing to price.
- 2What should my first trace show?
- Agent span, model call, tokens and a cost
- No cost? No model name or token counts
- Forming checks are waiting, not $0
Transcript
Open Traces and click your run. A good trace shows the agent, the model call under it, the token counts and a cost. No cost? The span has no model name or token counts, for example a streamed call in SDK zero point six. Then open Cost findings. Checks marked forming are waiting for what they need. Morse never shows a forming check as a zero dollar finding.
- 3Why don't I have a verdict yet?
- Report outcomes from a check your code makes
- Never hard-code success
- Verdict: 20 labeled runs per agent in 7 days, 4 days
Transcript
Step two, map success. A run with no outcome is unlabeled, and Morse never counts it as a failure. Report an outcome from something your code can actually check, and never hard-code success. A verdict needs twenty labeled runs for an agent in the last seven days, on at least four different days. Until then, the brief tells you what it still needs.
Before you start
- Python 3.11 or newer (hard requirement), or Node.js 18.19+ / 20.6+. Check with
python3 --versionornode --version. On an older Python (macOS still ships 3.9 aspython3on some machines),pip install morse-aistops with “No matching distribution found for morse-ai” and does not name the real cause. Onlypip install -vshows it: `requires-python=3.11
. Usepython3.11` or newer, for example in a virtual environment. - A Morse API key. You create it in the wizard below.
- A model-provider key (Anthropic or OpenAI) is not needed for the first step. The connection test only needs your Morse key. You add a model call as the next step, because Morse needs one real model call to show you a cost. A run with no model call is recorded but has nothing to price.
- About 10 minutes.
Already using OpenTelemetry? You can skip the SDK: go to path C below, or read the OTLP guide.
The three steps
- Instrument — create your account and an API key, send a test trace (no model key needed), then add a model call to see your first cost finding.
- Map success — tell Morse what a successful run means, and report outcomes from your code.
- Read your first brief — open the ranked weekly brief and see what it needs before it can rank an agent.
1. Instrument
Create your account
- If signup is open, go to app.morsehq.dev/signup and enter your name, email and a password. If Morse is still invite-only (the banner above says which), open the link in your invitation email instead.
- Open the verification email and click the link.
- Sign in. Morse opens the setup wizard automatically. If you leave it, go back to
app.morsehq.dev/onboarding.
Create an API key
On the wizard’s first screen, click Generate API key.
-
The key is shown once. Copy it now.
-
Store it in your environment, not in your code:
export MORSE_API_KEY='<paste-your-morse-key>' # replace the placeholder, keep the quotesOn Windows PowerShell,
exportdoes not exist. Use$env:MORSE_API_KEY = '<paste-your-morse-key>'(this session only), orsetx MORSE_API_KEY "<paste-your-morse-key>"and open a new terminal. Ifnpmstops with “running scripts is disabled on this system”, PowerShell’s execution policy is blockingnpm.ps1. RunSet-ExecutionPolicy -Scope CurrentUser RemoteSignedonce, or callnpm.cmdinstead. -
The panel names the environment the key sends data to (production, by default). To add environments or generate more keys later, open Settings → API keys.
Click Continue.
Were you given a different Morse API host, for example a test environment? Set
MORSE_ENDPOINT=https://<your-api-host>/api/v1/traces/batch in the same shell before you run a
snippet below, and use that host in place of api.morsehq.dev in path C. Most accounts leave it
unset.
Test your connection (no model key)
Start with a bare run. It needs only MORSE_API_KEY, so you can prove your key and network work
before you wire in a model. The snippets are the same as the wizard’s connection test tiles.
Python:
python3 -m venv .venv && source .venv/bin/activate
pip install morse-aiimport morse_ai
morse_ai.init() # reads MORSE_API_KEY from your environment
with morse_ai.run("connection-test"):
print("sent a test trace")
morse_ai.flush() # short scripts: send everything before the process exitsTypeScript / Node (save as connection-test.mjs; the .mjs extension matters because top-level
await needs an ES module):
npm install @morsehq-dev/sdkimport * as morse from "@morsehq-dev/sdk";
morse.init(); // reads MORSE_API_KEY from your environment
await morse.run({ agentName: "connection-test" }, async () => {
console.log("sent a test trace");
});
await morse.flush(); // short scripts: send everything before the process exitsA bare run has no model, tokens or cost, so Morse records it as a low-signal trace. Traces shows it automatically while your workspace is new: the list only hides low-signal runs once the workspace has 10 real AI traces, and after that you click Show them. See Low-signal traces. The test proves the connection. It produces no cost and no finding, so move on to the next step.
Add a model call
Pick one path. Each path sends a trace with a real model call, so you get a cost and a first finding. Each snippet also reports an outcome only from something the code can check, never a hard-coded success. These paths need a model-provider key (Anthropic or OpenAI); a path without one is the connection test above.
Both languages are supported, and the adapter lists differ slightly. Python has LangChain, LangGraph, OpenAI, OpenAI Agents, Anthropic and Claude Agent SDK. TypeScript has LangChain, LangGraph, OpenAI, OpenAI Agents, Anthropic, Claude Agent SDK and the Vercel AI SDK.
A. Python
python3 -m venv .venv && source .venv/bin/activate
pip install "morse-ai[anthropic]" anthropic # quotes matter in zsh
export ANTHROPIC_API_KEY='<your-anthropic-key>'Save as agent.py:
import anthropic
import morse_ai
from morse_ai.adapters.anthropic import wrap
morse_ai.init() # reads MORSE_API_KEY from your environment
client = wrap(anthropic.Anthropic()) # every messages.create call is traced, with tokens and cost
with morse_ai.run("quickstart-agent") as r:
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=200,
messages=[{"role": "user", "content": "Give three uses for a paperclip, one per line."}],
)
answer = response.content[0].text
# Declare an outcome only from something your code can check.
looks_right = len([line for line in answer.splitlines() if line.strip()]) == 3
r.set_outcome(success=looks_right, outcome="three_items" if looks_right else "wrong_shape")
morse_ai.flush() # short scripts: send everything before the process exits
print(answer)python agent.pyUsing OpenAI instead? Install pip install "morse-ai[openai]" openai, set OPENAI_API_KEY,
and swap the client and the call:
import openai
from morse_ai.adapters.openai import wrap
client = wrap(openai.OpenAI())
# inside the run:
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Give three uses for a paperclip, one per line."}],
)
answer = resp.choices[0].message.content or ""Using a framework? LangGraph, LangChain, OpenAI Agents and the Claude Agent SDK each have a snippet in Frameworks. Run that snippet against your own agent, then come back to See your first trace.
B. TypeScript / Node
npm install @morsehq-dev/sdk @anthropic-ai/sdk
export ANTHROPIC_API_KEY='<your-anthropic-key>'No Anthropic or OpenAI key? Stay with the connection test above: it needs only your Morse key.
Save as agent.mjs. The .mjs extension matters: top-level await needs an ES module (or set
"type": "module" in your package.json). In a CommonJS file, node stops with a SyntaxError
on the first await.
import Anthropic from "@anthropic-ai/sdk";
import * as morse from "@morsehq-dev/sdk";
import { wrapAnthropic } from "@morsehq-dev/sdk/anthropic";
morse.init(); // reads MORSE_API_KEY from your environment
const client = wrapAnthropic(new Anthropic());
await morse.run({ agentName: "quickstart-agent" }, async (handle) => {
const msg = await client.messages.create({
model: "claude-haiku-4-5",
max_tokens: 200,
messages: [{ role: "user", content: "Give three uses for a paperclip, one per line." }],
});
const answer = msg.content.find((block) => block.type === "text")?.text ?? "";
// Declare an outcome only from something your code can check.
const looksRight = answer.split("\n").filter((line) => line.trim()).length === 3;
handle.setOutcome(looksRight, looksRight ? "three_items" : "wrong_shape");
console.log(answer);
});
await morse.flush(); // short scripts: send everything before the process exitsnode agent.mjsOpenAI works the same way: wrapOpenAI from @morsehq-dev/sdk/openai wraps chat.completions.create.
For LangGraph, LangChain, OpenAI Agents, the Claude Agent SDK and the Vercel AI SDK, see
Frameworks → TypeScript.
C. OpenTelemetry (OTLP/HTTP, no SDK)
If your app already emits OpenTelemetry traces, point the traces exporter at Morse. Use the
signal-specific variables shown here, not OTEL_EXPORTER_OTLP_ENDPOINT.
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://api.morsehq.dev/api/v1/otlp/v1/traces"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer%20$MORSE_API_KEY"
export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL="http/protobuf"- HTTP only. There is no gRPC ingest endpoint.
- The key goes in
Authorization: Bearer …. Write the space as%20in the environment variable. Other headers such asX-API-Keyare not accepted. - Set
morse.outcome.success(a real boolean, not the string"true") on any span that knows whether the run worked. Without it Morse can price the trace but cannot judge it. See OTLP.
To test the endpoint without touching your app, this script sends one two-span trace (an agent span and one priced model call). It needs Python 3.8+ and no other packages. The outcome here is a fixed test value, which is fine for a smoke test and not for a real agent:
# otlp_smoke.py
import json
import os
import secrets
import time
import urllib.request
def attr(key, value):
if isinstance(value, bool):
return {"key": key, "value": {"boolValue": value}}
if isinstance(value, int):
return {"key": key, "value": {"intValue": str(value)}}
return {"key": key, "value": {"stringValue": str(value)}}
now = time.time_ns()
trace_id, root_id, llm_id = secrets.token_hex(16), secrets.token_hex(8), secrets.token_hex(8)
payload = {
"resourceSpans": [
{
"resource": {"attributes": [attr("service.name", "otlp-quickstart")]},
"scopeSpans": [
{
"scope": {"name": "quickstart"},
"spans": [
{
"traceId": trace_id,
"spanId": root_id,
"name": "otlp-quickstart",
"kind": 1,
"startTimeUnixNano": str(now - 2_000_000_000),
"endTimeUnixNano": str(now),
"attributes": [
attr("morse.span_type", "agent"),
attr("morse.outcome.success", True),
attr("morse.outcome.label", "answered"),
],
"status": {"code": 1},
},
{
"traceId": trace_id,
"spanId": llm_id,
"parentSpanId": root_id,
"name": "chat gpt-4o-mini",
"kind": 3,
"startTimeUnixNano": str(now - 1_500_000_000),
"endTimeUnixNano": str(now - 500_000_000),
"attributes": [
attr("gen_ai.operation.name", "chat"),
attr("gen_ai.system", "openai"),
attr("gen_ai.request.model", "gpt-4o-mini"),
attr("gen_ai.usage.input_tokens", 1200),
attr("gen_ai.usage.output_tokens", 300),
],
"status": {"code": 1},
},
],
}
],
}
]
}
req = urllib.request.Request(
"https://api.morsehq.dev/api/v1/otlp/v1/traces",
data=json.dumps(payload).encode(),
method="POST",
headers={
"Authorization": f"Bearer {os.environ['MORSE_API_KEY']}",
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(req, timeout=15) as resp:
print(resp.status, resp.read().decode())python3 otlp_smoke.py # expect: 200 and a small JSON bodySee your first trace
- Go back to the wizard’s Verify screen. It checks every few seconds and moves on by itself when your trace lands. If nothing arrives within a minute, see Troubleshooting.
- Open Traces in the sidebar and click the run named
connection-test(low-signal, no cost),quickstart-agentorotlp-quickstart. You should see an agent span with a model-call span under it, the model name, token counts and a cost. - “1 low-signal trace hidden”? The Traces list hides a run that has no model call, no tokens, no cost and no span other than the agent span, and that lasted 100 ms or less, once your workspace has 10 real AI traces. Click Show them to see it. The connection-test run above is exactly that, and the model-call snippets make a real call so you get a cost. See Low-signal traces.
Find your first finding
Open Cost & Budget → Cost findings (also shown as the First cost findings card on Home). The first item appears as soon as one trace in the window has a price:
- Where spend went. Your priced spend by agent and by trace over the window at the top of the page (7, 14 or 30 days). It measures spend and makes no claim that the spend was good or bad.
Other checks appear as traffic builds. Until they can run, the page lists them as still forming, with a count of the checks waiting for traffic and what each one needs:
| Check | What it needs |
|---|---|
| Repeated tool calls / retry loops | Tool-call spans from new traffic (older traces are not backfilled) |
| Prompt caching estimate | An Anthropic agent with at least 20 calls sharing a long prompt prefix |
| Larger-than-usual context | At least 5 earlier traces for the agent (14-day baseline) |
| Outcome coverage | Shows the share of spend with no confirmed outcome yet. That is not the same as failed. |
A check that cannot run yet is listed as forming. Morse never shows it as a $0 finding. See Cost findings for how each figure is produced.
2. Map success
Morse never guesses whether a run worked. A run with no outcome counts as unlabeled: it is counted as activity and coverage, never as a failure. Ranked verdicts need labeled outcomes.
- In the wizard’s Map & outcome screen, Morse may show what it inferred about your agents and a proposed definition of success. Confirm it or write your own. On a brand-new account either card may be missing. That is normal: click Continue. Confirming a definition does not label any runs. Runs are labeled by the sources below.
- Report outcomes from your code (the strongest label; nothing overrides it):
r.set_outcome(...)in Python,handle.setOutcome(...)in TypeScript, ormorse.outcome.successon an OTLP span, as in step 1. Report success only when your code can actually check it. A hard-codedsuccess=Truemarks every run as a success, and because a label from your code outranks every later eval, classifier or webhook signal, Morse cannot correct it afterwards. If you cannot tell, leave the outcome unset and the run stays unlabeled. - Or let a later business event label runs. Examples: a ticket reopened, a pull request merged,
an order refunded. Use the record outcomes API, a
GitHub connection or outcome rules. These
need a record key on each run. Attach record keys with
record_keys=orlink_record()in Python andlinkRecord()in TypeScript (SDK 0.7.0 and later), or over REST or OTLP. - Check coverage in Settings → Outcomes. Coverage shows how many runs per agent have an outcome and from which source (Reported, Rule, Inferred, Eval, Unlabeled). The top line names your single next step.
See Set up outcomes for every way to label runs.
3. Read your first brief
On Home, click Open weekly summary. The page is titled Weekly Summary. It ranks your agents by which one needs attention first: a verdict leads the ranking, then dollars at stake, then the size of the success drop. Outcomes, spend and sampled quality stay separate; they are never blended into one score.
The brief stays honest when the evidence is thin:
| You see | It means | What it needs |
|---|---|---|
| Nothing to report yet | No traces in the last 7 days | Send a trace (step 1) |
| Not enough labeled outcomes | Fewer than 20 labeled runs for that agent in the last 7 days | Report outcomes (step 2) on more runs |
| Baseline forming | Enough labels, but labeled runs on fewer than four different days | Keep sending labeled runs on a few more days |
| A verdict (Holding up, Worth watching, Needs attention) | Judged against the agent’s own history, not a fixed bar | — |
The baseline is the days with labeled runs before the most recent one: Morse needs three of them to compare the latest day against, so a verdict needs labeled runs on at least four different days in the 7-day window.
A success rate has its own, stricter floor than the verdict. The app shows it only once at least 30 runs are judged (have a recorded success or failure) and at least 20% of the agent’s runs are judged. Below that it reads “success rate needs 30 judged runs” (or “needs 20% of runs judged”) instead of a percentage. The verdict floor (20 labeled runs over four days) and the success-rate floor (30 judged runs) are separate checks.
Week-over-week changes appear only when the previous week has its own labeled runs. Past weeks are frozen as written, and history starts on the first Monday after your agents send traces. The Home dashboard follows the same rules: a card without enough samples shows a line naming what it needs, never a guessed number. An empty worklist reads “Nothing needs attention right now”.
The finish screen offers next steps: Inspect your first trace →, Say what success means → (opens your agent’s page, where you agree what success means without code — see What success means for this agent), Browse demo dashboard →, Get alerted when an agent breaks → (Reliability) and Read the docs →.
Finish completes setup even if your first trace has not arrived yet. A later trace appears on your dashboard, and you can revisit earlier setup screens before finishing.
Troubleshooting
- See what the SDK is doing. In Python, add
import logging; logging.basicConfig(level=logging.INFO)at the top of the script. You should seeMorse SDK initialized — sending to https://api.morsehq.dev/…. The TypeScript SDK prints the same line to the console. - “morse_ai not initialized — set MORSE_API_KEY” (or
ValueError: api_key must be providedfrommorse_ai.init()): the variable is not set in this shell. Runecho $MORSE_API_KEY. TypeScript logs a one-time warning and sends nothing. NameErroronosorMORSE_API_KEYin a snippet copied from somewhere else: usemorse_ai.init()with no arguments, which reads the environment variable itself.zsh: no matches found: morse-ai[...]: put the package name in quotes, as inpip install "morse-ai[anthropic]".SyntaxErroronawaitin Node: rename the file to.mjs.No matching distribution found for morse-ai: your Python is older than 3.11 (the SDK requires 3.11 or newer;pip install -v morse-aishowsrequires-python >=3.11).- My script ended and I never called
flush(): see When you need flush(). - 401 from OTLP: wrong or revoked key, or the space in
Bearer%20…is missing. - 404 from OTLP: you used
OTEL_EXPORTER_OTLP_ENDPOINTand the exporter appended/v1/tracesa second time. UseOTEL_EXPORTER_OTLP_TRACES_ENDPOINT. - Trace arrived but Traces looks empty: it may be hidden as low-signal. Click Show them or see Low-signal traces.
- Trace has no cost: the span has no model name or token counts (for example a streamed call or an
OpenAI Agents
Agentwithoutmodel=on an SDK older than 0.7.0; upgrade withpip install -U morse-aiornpm update @morsehq-dev/sdk). - Corporate network: allow outbound HTTPS to
api.morsehq.dev.
Related
- Introduction — what Morse correlates and why.
- Frameworks — your stack’s snippet.
- Traces — where your first trace shows up once it lands.
- Cost findings — the label-free cost findings from your first traces.
- Set up outcomes — every way to label runs.
- Limits — what counts against the Free plan.