Open the Traces dashboard
Browse traced conversations and run Reflexes over them.
Instrument your app
Install the SDK —npm install @morphllm/morphsdk, or pip install 'morphsdk[otel]' — then initialize once at startup. After that, calls to the instrumented SDKs are traced automatically.
begin opens an interaction you can enrich — set_input, set_property, and with_tool / with_span to nest tool calls — and finish closes it. The event_id it mints is the join key the Reflex labels attach to. The evals map names the Reflexes Morph runs on the turn; Run evals automatically covers which role each one classifies.
Run evals automatically
Passevals and Morph classifies each turn for you — asynchronously, off your request path. Results show up in the Traces dashboard already labeled, and in the export, exactly as if you’d run them by hand.
You say which role each Reflex classifies — user (the user’s message) and/or assistant (the agent’s output). Most safety/intent Reflexes run on user; response-quality ones like leaked-thinking run on assistant.
evals: { user: ["jailbreak"] }. Set a default for every turn by passing evals to morph_tracing / morphTracing; a per-begin value overrides it, and omitting both runs none.
Evals run only on turns you wrap in
begin() — that’s what gives the turn the event_id the label links to. Auto-instrumented LLM calls made outside an interaction are still traced, but won’t be classified.Configuration
Pass these tomorph_tracing / morphTracing. Every field is optional.
Ingest directly
If you already emit OpenTelemetry spans, skip the SDK and POST OTLP/JSON straight to Morph. This is the same endpoint the SDK exports to.
The account is resolved server-side from your API key and stamped onto every span — any client-supplied
morph.account.* attributes are stripped. For zero-data-retention keys (or when you set trace_content: false), prompt/response content is dropped before storage.
cURL
List traced turns
Browse the turns you’ve ingested — the same data behind the Traces dashboard. This is how you read async eval results back in code. Usemorph.traces.list() in the SDK, or GET /v1/reflex/traces directly.
Tracing is async, so
morph.traces.list() reads labels back after they land. For a label synchronously, inside your request, call morph.reflex.predict() instead and read the result directly.reflex_results). Tool-only and content-stripped spans are omitted. Labels appear whether the Reflex ran automatically from the SDK or from the Traces dashboard. Each entry carries a status — pending while the async classification is queued, then completed (or failed) — so poll until the entries you’re waiting on are completed. Note that selected names the winning class even when it’s the benign one (["benign"], ["Not Frustrated"]): to find firing turns, match label against the failure class you care about, not selected being non-empty.
Reflexes overview
Label every traced turn — jailbreaks, loops, frustration, and more.
Classify a backlog
Run Reflexes over past traces with the async batch API.