Skip to Content
SDKInstallation

Installation

Three steps: create an API key, install the package, and call init() (or just set an env var — init() is optional).

1. Create an API key

Open Settings → API keys (/settings/api-keys in the app), find your environment, and click Generate (or Rotate, if it already has a key), then Generate key. Copy the key when it’s shown — you won’t see the full key again. The setup wizard’s first screen also has a Generate API key button. See Workspace settings for how environments and keys relate. Only owners and admins can see, create or rotate environment keys.

Rotate an environment key

Each environment has exactly one active key. Rotate opens a dialog with two choices:

  • Rotate (24h grace) — creates the new key and lets the old one keep working for 24 hours, so you can swap it into your deployments without dropping data. The old key then shows as “expires” with its time on the page.
  • Expire old key now (compromised) — creates the new key and stops the old one working immediately. Use it when a key leaked. Anything still using the old key fails until you update it.

Either way, the new key is shown once: copy it before you close the dialog.

Personal keys

For your own scripts and laptop, create a personal key instead of sharing the environment key. Open Profile (/profile in the app) and use Personal API keys → Create key:

  • Give it a name (for example laptop), choose Full access or Read-only, and choose when it expires: never, in 30 days, in 90 days or in 1 year. A personal key is scoped to the environment you are working in.
  • The key is shown once. Copy it before you close the dialog.
  • The list shows each key’s prefix, when it was last used and when it expires. You can rename a key and change its access later.
  • Revoke a key from the same list. A confirmation names the key, and anything using it stops working immediately. This cannot be undone. Expired and revoked keys move to a “revoked or expired” group.

Personal keys are the only keys you can revoke yourself. An environment key is replaced by rotating it (see above), which needs an owner or admin.

2. Install

⚠️

Python 3.11 or newer is required (or Node.js 18.19+ / 20.6+ for TypeScript). On Python 3.10 or older, pip install morse-ai stops with “No matching distribution found for morse-ai” and does not say why. Only pip install -v morse-ai shows the real reason: `requires-python

=3.11. Check with python3 —version, and use python3.11` or newer.

Python:

pip install morse-ai

TypeScript:

npm install @morsehq-dev/sdk

Adapters are peer dependencies in TypeScript — install only the SDKs you actually use. In Python, optional extras add framework-specific adapters — see Frameworks for the full list ("morse-ai[anthropic]", "morse-ai[langgraph]", "morse-ai[openai_agents]", or "morse-ai[all]"). Quote the package name whenever it has brackets: zsh, the default shell on macOS, otherwise stops with no matches found.

3. Initialize

The simplest path is zero-code: set MORSE_API_KEY in your environment and don’t call init() at all. Every public SDK function lazily auto-initializes from the env var on first use.

export MORSE_API_KEY=your-morse-key

Or call init() explicitly at startup if you want to pass the key directly, override the endpoint, or configure redaction:

import morse_ai morse_ai.init() # reads MORSE_API_KEY from your environment
import * as morse from "@morsehq-dev/sdk"; morse.init(); // reads MORSE_API_KEY from your environment

Both SDKs make init() optional, and either resolves the key from MORSE_API_KEY when you don’t pass one. They differ when no key is set: Python’s init() raises ValueError, while TypeScript logs a one-time warning and sends nothing (every SDK call becomes a silent no-op).

In Python, init() resolves api_key from the argument first, then MORSE_API_KEY; if neither is set it raises ValueError. It resolves the ingest endpoint from an explicit endpoint arg, then MORSE_ENDPOINT, then the default https://api.morsehq.dev/api/v1/traces/batch.

Which MORSE_ENDPOINT forms are valid. In Python, both the bare host (https://api.morsehq.dev) and the full ingest URL (https://api.morsehq.dev/api/v1/traces/batch) work: the SDK appends /api/v1/traces/batch when it is missing. In TypeScript the value is used as given for the JSON batch calls (record(), track(), logs), so set the full ingest URL there; spans are sent over OTLP to the same host. Most setups leave MORSE_ENDPOINT unset; set it only if you were given a different Morse API host.

Setting MORSE_DISABLED=1 makes init() (and every submission call) a no-op — useful for local dev or CI.

Verify

Run the first snippet from the Quickstart (Python or TypeScript). It makes one real model call through a wrapped client, so the trace shows a model, token counts and a cost. Then check the Traces list on the dashboard for the run.

A run with no model call, no tool calls and a duration of 100 ms or less can be hidden from the default Traces list as low-signal. If you run a bare with morse_ai.run("my-agent"): block as a test, click Show them to see it. It has no cost, so it does not produce a finding.

When you need flush()

Both SDKs send spans in the background in batches, and both try to send what is left when the process ends normally. That is why a short script that never calls flush() often still delivers its traces. Call flush() yourself (morse_ai.flush() in Python, await morse.flush() in TypeScript) when the normal exit path may not run:

  • Serverless and short-lived handlers (Lambda, Cloud Functions, Vercel): the runtime can freeze the process as soon as your handler returns, before the background send happens. Flush before you return.
  • Hard exits: os._exit(), a process killed by SIGKILL or by a container stop that does not let it finish, and in Node process.exit() or an uncaught exception. The exit hook does not run on these.
  • Forked workers: a child process that exits without a normal interpreter shutdown.
  • When you need to know the data is sent now, for example before you read the trace in the app from the same script.

For an ordinary script or service that ends by returning from main (Python) or by the event loop emptying (Node), the SDK sends what is left on exit and flush() is a safe extra. On exit the SDK waits a bounded time, so an unreachable API does not hang your process, and it prints a warning when spans were dropped.

  • Overview — what the SDK does and its non-invasive guarantee.
  • Frameworks — install commands and snippets for LangChain, LangGraph, OpenAI Agents, Anthropic, and Claude Agent SDK.
  • set_context() — attach cost-attribution dimensions to a trace.
Last updated on