Skip to Content
Getting StartedQuickstart

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
Quickstart in three clips
  1. 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
    Open setup in Morse → (opens in a new tab)
    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.

  2. 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
    Open Traces in Morse → (opens in a new tab)
    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.

  3. 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
    Open Settings › Outcomes in Morse → (opens in a new tab)
    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 --version or node --version. On an older Python (macOS still ships 3.9 as python3 on some machines), pip install morse-ai stops with “No matching distribution found for morse-ai” and does not name the real cause. Only pip install -v shows it: `requires-python

    =3.11. Use python3.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

  1. 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.
  2. Map success — tell Morse what a successful run means, and report outcomes from your code.
  3. 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

  1. 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.
  2. Open the verification email and click the link.
  3. 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 quotes

    On Windows PowerShell, export does not exist. Use $env:MORSE_API_KEY = '<paste-your-morse-key>' (this session only), or setx MORSE_API_KEY "<paste-your-morse-key>" and open a new terminal. If npm stops with “running scripts is disabled on this system”, PowerShell’s execution policy is blocking npm.ps1. Run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once, or call npm.cmd instead.

  • 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-ai
import 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 exits

TypeScript / Node (save as connection-test.mjs; the .mjs extension matters because top-level await needs an ES module):

npm install @morsehq-dev/sdk
import * 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 exits

A 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.py

Using 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 exits
node agent.mjs

OpenAI 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 %20 in the environment variable. Other headers such as X-API-Key are 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 body

See 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-agent or otlp-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:

CheckWhat it needs
Repeated tool calls / retry loopsTool-call spans from new traffic (older traces are not backfilled)
Prompt caching estimateAn Anthropic agent with at least 20 calls sharing a long prompt prefix
Larger-than-usual contextAt least 5 earlier traces for the agent (14-day baseline)
Outcome coverageShows 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.

  1. 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.
  2. Report outcomes from your code (the strongest label; nothing overrides it): r.set_outcome(...) in Python, handle.setOutcome(...) in TypeScript, or morse.outcome.success on an OTLP span, as in step 1. Report success only when your code can actually check it. A hard-coded success=True marks 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.
  3. 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= or link_record() in Python and linkRecord() in TypeScript (SDK 0.7.0 and later), or over REST or OTLP.
  4. 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 seeIt meansWhat it needs
Nothing to report yetNo traces in the last 7 daysSend a trace (step 1)
Not enough labeled outcomesFewer than 20 labeled runs for that agent in the last 7 daysReport outcomes (step 2) on more runs
Baseline formingEnough labels, but labeled runs on fewer than four different daysKeep 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 see Morse 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 provided from morse_ai.init()): the variable is not set in this shell. Run echo $MORSE_API_KEY. TypeScript logs a one-time warning and sends nothing.
  • NameError on os or MORSE_API_KEY in a snippet copied from somewhere else: use morse_ai.init() with no arguments, which reads the environment variable itself.
  • zsh: no matches found: morse-ai[...]: put the package name in quotes, as in pip install "morse-ai[anthropic]".
  • SyntaxError on await in 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-ai shows requires-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_ENDPOINT and the exporter appended /v1/traces a second time. Use OTEL_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 Agent without model= on an SDK older than 0.7.0; upgrade with pip install -U morse-ai or npm update @morsehq-dev/sdk).
  • Corporate network: allow outbound HTTPS to api.morsehq.dev.
Last updated on