Skip to Content
SDKRecord keys and versions

Record keys and versions

Two things make a run easy to follow up on later: which business record it acted on (a support ticket, an order, a pull request) and which version of your agent ran. Record keys carry the first. Version fields carry the second. Both are optional, additive, and cost one line each.

⚠️

Requires SDK 0.7.0 (not yet released; 0.6.0 does not have this). On 0.6.0, run() fails with run() got an unexpected keyword argument 'agent_version'.

SDK support for everything on this page ships in SDK 0.7.0, which is not released yet. You can send the same data today over REST and OTLP; each section below shows the wire form next to the SDK call.

Record keys

A record key names the business record a run acted on. It is a pair (key_type, key_value), for example ("zendesk_ticket", "4812"). A run can carry several.

Once a run carries a key, you can send the outcome of that record later (a ticket was reopened, an order was refunded, a pull request was merged) without knowing the run’s trace id. Morse finds every run that carries the key and labels it.

For pull requests connected through the GitHub connector, use the key type github_pr with the value owner/repo#number; a bare number never matches.

Rules, enforced when the run arrives:

  • key_type is lower-cased and must match ^[a-z0-9][a-z0-9_.-]{0,63}$.
  • key_value is turned into a string and trimmed, and must be 1–256 characters. Integers are accepted, so an unquoted ticket id from a webhook template works.
  • At most 20 keys are kept per run.
  • An invalid pair is dropped. The run is still accepted, so a bad key never costs you a trace.
⚠️

Key values are opaque identifiers. Use a ticket number, order id or PR number, not an email address, name or anything else that identifies a person. Keys are stored and searchable as plain text.

Python

import morse_ai morse_ai.init() with morse_ai.run("support-agent", record_keys={"zendesk_ticket": ticket.id}) as r: answer = handle(ticket) replied = bool(answer) # derive the outcome from a check; never hard-code success=True r.set_outcome(success=replied, outcome="replied" if replied else "no_reply")

You often learn the id after the run has started. Add it from anywhere inside the run:

morse_ai.link_record("zendesk_ticket", ticket.id)

If you already use set_context(), any key that starts with record_key. is treated as a record key. It also stays in your user_metadata, so your Segments keep working:

morse_ai.set_context(**{"record_key.zendesk_ticket": ticket.id})

TypeScript

import * as morse from "@morsehq-dev/sdk"; await morse.runTraced( { agentName: "support-agent", recordKeys: { zendesk_ticket: ticket.id } }, async () => { // ... morse.linkRecord("order", order.id); // add another key mid-run morse.setContext({ "record_key.github_pr": pr.number }); // same prefix convention as Python }, );

REST and OTLP

Available today, without the SDK.

RouteHow to send a record key
REST trace payload"record_keys": {"zendesk_ticket": "4812"} on the trace
REST user_metadata"user_metadata": {"record_key.zendesk_ticket": "4812"}
OTLP spanspan attribute morse.record_key.zendesk_ticket on any span of the run

OTLP record-key attributes are consumed by Morse and are not copied into the span’s metadata. All three routes can be combined on one run; the keys are merged.

Agent version

agent_version is your own label for the deployed agent: a release name, a build number, a semantic version. Up to 128 characters.

morse_ai.init(agent_version="2026.10.3") # default for every run with morse_ai.run("support-agent", agent_version="2026.10.3-canary") as r: # this run only ...
morse.init({ agentVersion: "2026.10.3" }); await morse.runTraced( { agentName: "support-agent", agentVersion: "2026.10.3-canary" }, // this run only async () => {}, );

Over the wire: "agent_version" on the REST trace payload, or the span attribute morse.agent_version on any span over OTLP (the first value wins; the SDKs send it on the root span).

Prompt references

Say which prompt a run used. The version is optional, so teams that keep prompts in code do not have to invent version numbers. Your agent_version or git revision identifies the change instead.

morse_ai.set_prompt("triage", version=7) morse_ai.set_prompt("triage") # prompts kept in code: no version needed
morse.setPrompt("triage", 7); morse.setPrompt("triage");

Called inside an open span, the reference is pinned to that span; otherwise it belongs to the run. At most 20 references are kept per run. A version that is not a whole number of 1 or more is dropped and the name is kept.

Over the wire: "prompts": [{"name": "triage", "version": 7}] on the REST trace payload, or the span attributes morse.prompt.name and morse.prompt.version over OTLP (one reference per span). A reference that includes a version is linked to the matching prompt template. One without a version is recorded on the run and shows up as triage in its configuration.

Git revision

The SDKs look for the revision that is running, in this order, and use the first one that is set:

  1. MORSE_GIT_REVISION
  2. GITHUB_SHA
  3. CI_COMMIT_SHA
  4. RAILWAY_GIT_COMMIT_SHA
  5. VERCEL_GIT_COMMIT_SHA
  6. RENDER_GIT_COMMIT
  7. SOURCE_VERSION
  8. git rev-parse HEAD, when a git binary and checkout are present. In TypeScript this only runs where Node’s child_process is available. It never throws.

It is sent as the git.revision resource attribute, over REST and OTLP alike. Morse trims it and keeps the first 40 characters.

In a container

A production image usually has no .git directory and no CI variables, so none of the lookups above find anything. Pass the commit in at build time:

ARG GIT_SHA ENV MORSE_GIT_REVISION=$GIT_SHA
docker build --build-arg GIT_SHA="$(git rev-parse HEAD)" -t my-agent .

If your platform already sets one of the variables in the list at runtime (Railway, Vercel and Render do), you do not need this.

Service version

If your process sets service.version in OTEL_RESOURCE_ATTRIBUTES, both SDKs send it with no extra code, and OTLP exporters send it as a standard resource attribute. Morse keeps up to 128 characters.

export OTEL_RESOURCE_ATTRIBUTES="service.name=support-agent,service.version=2.4.1"

Which version field do I set?

FieldAnswersSet it withWhen to use it
Git revisionWhich commit is running?Nothing, usually. MORSE_GIT_REVISION in a container.Always. It is free and exact.
agent_versionWhich release of the agent did you declare?init(agent_version=) or run(agent_version=)You name releases, canaries or experiments your own way.
Prompt versionWhich prompt text did this run use?set_prompt(name, version)You keep prompts in a registry with numbered versions.
service.versionWhich version of the service is this process?OTEL_RESOURCE_ATTRIBUTESYour service already reports one through OpenTelemetry.

You do not need all four. If you set only one, set the git revision: it needs no code and cannot drift from what is deployed.

Where it shows up

Attribution panel

Open any run from Traces. The Attribution panel lists the run’s record keys, its agent version, git revision and service version, the models, prompt references and tool descriptions it used, and the history of its outcome label: when it changed, what it changed to, and where the label came from. A field that was never sent reads Not captured and links back to this page.

Capture health

Open an agent. The Capture health section answers whether this is wired up across all of the agent’s runs, not just the one you tested. It shows, for the selected window:

  • the share of runs with any record key, and the count per key type;
  • the share of keyed runs that received a record outcome;
  • the share of runs with a git revision, and with a configuration fingerprint;
  • record-outcome events by status: applied, pending, expired, no effect.

Each percentage comes with its counts, for example “412 of 1,030 runs”. A low keyed share usually means one code path forgot to call link_record. A growing pending count usually means events are arriving with a key_type or value that no run carries.

  • Record outcomes by key: send what happened to a record, and Morse labels every run that carries its key.
  • set_context(): the record_key. prefix convention builds on it.
  • OTLP: the span attributes above, for OpenTelemetry exporters.
Last updated on