?team=, the status fields, handles, errors, pagination) is on
conventions.
Every response body on this page was captured from a server seeded with the spec’s conformance
trace: 17 steps, root_status: error, one three-attempt observed_retry step, on team fixture
with environment prod. The one exception is the populated /related body at the end, captured
on team demo of the same server, which holds three delivery-service traces that share one
order_id tag. $READ_TOKEN holds a read token whose team-set includes both teams.
GET /v1/traces
Lists traces, newestlast_seen_at first, as summary rows without steps.
environment is omitted, never null, on a trace whose relay daemon sent no
X-Vexlo-Environment.
duration_ms is omitted until the root attempt’s completion record arrives. The last four
fields are the coverage block flattened into the row.
A tag_value search without tag_key adds matched_tag_keys to each row:
has_error and root_status answer different questions. The conformance trace has four error
steps and an error root, so has_error=true&root_status=ok matches nothing:
Looking up ids
?trace_id= is the cheap re-check for ids you already hold, such as rows still showing
completeness: running. It returns list rows, never steps. The response echoes limit, raised
to the number of ids named when ?limit= is smaller, and its next_cursor is null: every
named id resolves in one page. An id outside the token’s team-set, or one that does not exist,
is silently absent from items:
422 too_many_trace_ids.
GET /v1/traces/{id}
Returns one trace’s shape: every step with its attempts, timing, status, and handles with previews, and no value bytes. This route has no widening parameter; bytes come from the values route below.
The route accepts no
?team=: the id names one trace, and an id outside the token’s team-set is
404 trace_not_found. A trace whose trace-open has not arrived answers 404 too and is absent
from every listing, ?trace_id= included: until that record lands the server holds a landing
place for out-of-order records, not an observed request. Read a 404 as “no trace with that id
is visible to you”, never as “the request never ran”.
path, parent_path, and depth for rebuilding the tree, name and
parent_name in module.qualname form, attempt_grouping (single, declared, or
observed_retry, derived by the server), duration_us, and one entry per attempt in attempts.
error_class appears only on an error attempt, and an in_flight attempt carries neither it, a
duration_us, nor a slots key
(conventions). slots holds one handle per slot the
attempt recorded; PickBestFromCandidates failed three times, so it has three attempts and no
output slot on any of them.
A step’s duration_us is the sum of its completed attempts’ duration_us: the attempts of one
step run one after another, so the sum is what the request paid for that step.
PickBestFromCandidates reads 2450, its 850, 810, and 790 added up. The field is absent, never
0, on a step whose every attempt is still in flight, and on a step with both completed and
in-flight attempts it is a lower bound rather than a final figure.
format_version is the version of the record format this trace’s emitter wrote. It is a trace-level
field, omitted from the list rows above, which carry no attempts.
The root step’s error and input read stored. The ok steps below it, such as
ParseAssignRequest, read pending on both slots although the trace is complete and their
values are served by the route below.
Step paging and sorting
limit caps the steps in one response, cursor resumes where the previous one ended, and
steps_sort=duration_desc reorders them. next_cursor is a string exactly when steps past this
page remain, in either sort, and null when nothing sorts after the steps the response carries.
The 17-step trace at limit=3 returns a cursor:
duration_desc ranks on the step’s own duration_us, so the retried
PickBestFromCandidates is ranked on the 2450 microseconds its three attempts add up to and sits
ninth, between SelectEligibleDasher at 3020 and ResolveDispatchParams at 2000. The five
slowest, with 12 rows still behind the cursor:
path sorts before
its children’s, so a parent that returns mid-walk lands behind the cursor: the walk ends with
next_cursor: null and without that parent, the root step included. Under duration_desc it
lands further forward still, a parent being at least as slow as the children it waited on.
step_count is what says whether a walk saw everything. It counts the steps recorded at the
moment of each response, not the ones returned, so compare the distinct path values collected
so far against the newest step_count seen, and walk again from no cursor while the two
disagree.
Traces are capped at 10,000 steps; a trace that hit the cap reads "truncated": true in its
coverage block.
GET /v1/traces/{id}/values
Returns the captured bytes of named steps. It is one of the two routes that serve a value; the other isGET /v1/functions/examples?include=values,
which serves the example calls of one function.
value is a string in every case. With encoding: json it holds JSON text to parse; with
text the exact UTF-8 string; with base64 the raw bytes encoded. bytes_count is the length of
the underlying bytes and truncated says whether they were cut at the 16 KiB slot bound.
A retried step returns one entry per attempt for each requested slot; the entries carry no
attempt index. PickBestFromCandidates at AACAAAIAAA raised the same error
three times:
values and listed in unknown_paths.
A path whose only attempts are in flight is left out of values too and is not listed in
unknown_paths: the path is known and the step ran, and there is nothing captured to return.
unknown_paths stays reserved for a path matching no step on the trace.
Here AAAA (ParseAssignRequest, an ok step) is served with "state": "pending" and its
value present, and the unknown ZZZZ is echoed in the server’s normalized spelling:
value. A slot that never formed (redacted, too_large,
not_captured), or that resolves to expired at read time, appears with path, slot, and
state only. More than 8 distinct paths is rejected:
GET /v1/traces/{id}/related
Lists the traces in the same team that share a tag with this one within a time window. Sharing a tag means both traces carry the same key and the same value, and nothing beyond that: it never means one called the other. The route takes no?team= and is always scoped to the trace’s own
team.
items is empty, and searched_tag_keys
still names the keys the search used. An empty result therefore stays distinguishable from a
service that sends no tags or spells a key differently.
Team demo on the same server holds three delivery-service traces for order o-5007, one
each from its assign, quote, and track operations, all tagging order_id. Anchored on one
of the three, the route returns the other two:
trace_id, service, environment (when the daemon sent one), shared_tags
(the pairs both traces carry), started_at, and root_status once the root attempt’s
completion record has arrived.
Both rows name the anchor’s own service, because all three runs are that one service: a shared
tag relates requests and says nothing about how many services they crossed.
window is measured back from the time of the request, not from the anchor trace’s own
started_at, and a value above the maximum is applied as 7 days:
