> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vexlo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Understand function usage

> Follow observed relationships to retained executions, captured inputs and a concrete test fixture.

Use function metadata to find recorded callers and callees, then inspect executions that
explain those relationships. This is runtime evidence for understanding code and planning a
change. It does not enumerate every possible dependency.

This procedure uses HTTP throughout. You need a server with recorded data, a read token, curl
and Python 3 for parsing JSON. The worked example examines `price_order`: a checkout function
that totals order lines, applies a loyalty discount and calculates tax.

Use a service and function recorded by your own application, or run the optional checkout
example below. Quickstart records a different, smaller operation; it does not create the
`price_order` function used in this guide's responses.

## Record the example (optional)

Use the Python environment and running server/relay from [Quickstart](https://docs.vexlo.ai/quickstart.md).
Set the queue directory in this terminal, replacing the path with the relay's absolute path:

```sh theme={"system"}
export VEXLO_SPOOL_DIR="/absolute/path/to/vexlo-spool"
```

The SDK and relay must access that same directory. The example uses fixed local data;
it does not send payments or change inventory.

Open the [example sources](https://docs.vexlo.ai/examples/checkout.md#pricing-example) and save `order_service.py`
and `test_pricing.py` in the application directory containing `.venv`.

Record the checkout with the source fingerprint supplied as a declared argument tag:

```sh theme={"system"}
export APP_REVISION=$(.venv/bin/python -c \
  'import hashlib; print("sha256:" + hashlib.sha256(open("order_service.py", "rb").read()).hexdigest())')
VEXLO_ENABLED=1 VEXLO_CAPTURE_VALUES=1 VEXLO_VALUE_SAMPLE_RATE=1 \
  VEXLO_CAPTURE_ERROR_MESSAGE=1 .venv/bin/python -c 'from order_service import main; main()'
```

Find `order-1042` in service `checkout-graph-example`. Confirm that its `app_revision` matches
`APP_REVISION`; repeated runs can produce several matching orders. The graph contains fourteen
calls, including pricing and a handled payment retry. For the HTTP workflow below, use that
service and copy the exact function name from its catalog.

## Locate a function

The UI's **Code** tab shows the selected function's identity and counts of observed callers
and callees. It does not display source code or browse relationship lists. Use the HTTP routes
below for those lists; the equivalent agent tools are linked at the end of this page.

Set the server and credential explicitly. Set `VEXLO_SERVICE` to the service you are investigating:

```sh theme={"system"}
export VEXLO_ENDPOINT=http://127.0.0.1:8080
export VEXLO_TOKEN=your-read-token
export VEXLO_SERVICE=checkout-graph-example
```

List the service's observed functions:

```sh theme={"system"}
curl --silent --show-error --fail-with-body --get "$VEXLO_ENDPOINT/v1/functions" \
  --data-urlencode "service=$VEXLO_SERVICE" \
  -H "Authorization: Bearer $VEXLO_TOKEN" > functions.json
```

Inspect `items` in `functions.json` and copy the exact function name. Continue with
`next_cursor` if the function is not on the first page. A service and full function name
identify the function for detail and example requests. The optional catalog `name` filter
is exact, not a substring search, and it is applied to the fetched page: an empty filtered
page with a continuation cursor does not establish that the function was never observed.

All requests inherit the token's access scope. If it covers several teams, use `team` to narrow
the catalog, detail and examples requests consistently; apply the same environment filter when
investigating one deployment. Identically named functions can otherwise be aggregated across
the accessible teams. Narrowing filters do not grant access beyond the token.

## Read observed callers and callees

For the pricing function returned by the example's catalog:

```sh theme={"system"}
export VEXLO_FUNCTION=order_service.price_order
curl --silent --show-error --fail-with-body --get "$VEXLO_ENDPOINT/v1/functions/detail" \
  --data-urlencode "service=$VEXLO_SERVICE" --data-urlencode "name=$VEXLO_FUNCTION" \
  -H "Authorization: Bearer $VEXLO_TOKEN" > function.json
```

The recorded relationship fields were:

```json theme={"system"}
{
  "name": "order_service.price_order",
  "observed_count": 1,
  "error_count": 0,
  "observed_callers": [
    {
      "name": "order_service.checkout",
      "observed_count": 1,
      "first_seen_at": "2026-11-08T18:42:45.575Z",
      "last_seen_at": "2026-11-08T18:42:45.575Z"
    }
  ],
  "observed_callees": [
    {
      "name": "order_service.price_lines",
      "observed_count": 1,
      "first_seen_at": "2026-11-08T18:42:45.575Z",
      "last_seen_at": "2026-11-08T18:42:45.575Z"
    },
    {
      "name": "order_service.discount_for",
      "observed_count": 1,
      "first_seen_at": "2026-11-08T18:42:45.575Z",
      "last_seen_at": "2026-11-08T18:42:45.575Z"
    },
    {
      "name": "order_service.tax_for",
      "observed_count": 1,
      "first_seen_at": "2026-11-08T18:42:45.575Z",
      "last_seen_at": "2026-11-08T18:42:45.575Z"
    }
  ]
}
```

`checkout` is an observed caller. The observed callees are `price_lines`, `discount_for` and
`tax_for`. The surrounding trace shows `price_lines` calling `line_total` twice, once per item,
and each line calculation calling `unit_price`. The detail response lists direct recorded
relationships, not every descendant.
These are recorded-parent relationships: an uninstrumented intermediary does not create its
own node or make this a complete source-level call graph.

Counts and first/last-seen times describe the indexed observations, not the total number of
possible paths through the application. Function `observed_count` counts recorded steps;
`error_count` counts failed attempts. Use these facts to choose executions to inspect before
changing an input assumption or a caller's behavior.

## Inspect real executions

Retrieve one retained example:

```sh theme={"system"}
curl --silent --show-error --fail-with-body --get "$VEXLO_ENDPOINT/v1/functions/examples" \
  --data-urlencode "service=$VEXLO_SERVICE" --data-urlencode "name=$VEXLO_FUNCTION" \
  --data-urlencode limit=1 -H "Authorization: Bearer $VEXLO_TOKEN" > examples.json
```

An example identifies `trace_id`, `path`, `attempt` and status, with input/output previews.
Inspect `examples` before continuing. If it is nonempty, take the example's trace and call:

```sh theme={"system"}
export VEXLO_TRACE_ID=$(python3 -c \
  'import json; print(json.load(open("examples.json"))["examples"][0]["trace_id"])')
export VEXLO_CALL_PATH=$(python3 -c \
  'import json; print(json.load(open("examples.json"))["examples"][0]["path"])')

curl --silent --show-error --fail-with-body \
  "$VEXLO_ENDPOINT/v1/traces/$VEXLO_TRACE_ID" \
  -H "Authorization: Bearer $VEXLO_TOKEN" > trace.json
```

The example belongs to the complete checkout graph, including customer lookup, inventory and
payment. Reading the surrounding trace establishes where pricing ran and what consumed its
result. Follow `next_cursor` if your trace has more call pages; the small example fits one page.

The examples endpoint returns at most five examples and has no continuation cursor. Environment
filters narrow retained example rows; they do not search a complete execution history for fresh
examples in that environment. For broader evidence, search traces by the function and a time
range, then inspect the matching recordings.

## Establish the application revision

Before comparing the recording with source code, establish which application revision produced
it. The example application passes `APP_REVISION` as an entry-point argument and includes
`app_revision` in `tag_keys`, so the SDK records it as a trace tag. That is an application
convention, not an automatic Vexlo field.
For this single-file example, the value is a SHA-256 fingerprint of the exact application
source file used for the recording.

Read the tag from the selected trace:

```sh theme={"system"}
python3 -c 'import json; print(json.load(open("trace.json"))["tags"]["app_revision"])'
```

```text theme={"system"}
sha256:97eb5c94da10869db1673c2826ab5a3951cc247fc39a73c0815ca8c59ab8203a
```

Use the corresponding source snapshot when examining the pricing rules. In an application with
many files, a deployment's source commit or build identifier can supply your revision convention.
If a recording has no revision tag, obtain provenance from deployment records before relating
it to a checkout. `sdk_version` identifies the SDK, not the application. A stack's file and line
number alone cannot identify the application revision.

## Retrieve an input and use it in a test

Retrieve the pricing call's input and output:

```sh theme={"system"}
curl --silent --show-error --fail-with-body --get \
  "$VEXLO_ENDPOINT/v1/traces/$VEXLO_TRACE_ID/values" \
  --data-urlencode "path=$VEXLO_CALL_PATH" --data-urlencode slot=input \
  --data-urlencode slot=output -H "Authorization: Bearer $VEXLO_TOKEN" > values.json
```

For this call, both values have `encoding: "json"`, readable `value` fields and
`truncated: false`. The input contains item identifiers and quantities, not catalog prices.
Inspect the two `unit_price` children in the same trace and retrieve their input/output values
to reconstruct the prices observed for those items. Combining that mapping with the pricing
input yielded this fixture and expected result:

```json theme={"system"}
{
  "fixture": {
    "inputs": {
      "customer": {
        "customer_id": "customer-17",
        "loyalty_member": true
      },
      "lines": [
        {
          "quantity": 2,
          "sku": "notebook"
        },
        {
          "quantity": 3,
          "sku": "pen"
        }
      ]
    },
    "catalog_prices": {
      "notebook": 1200,
      "pen": 300
    }
  },
  "expected": {
    "discount_cents": 330,
    "subtotal_cents": 3300,
    "tax_cents": 238,
    "total_cents": 3208
  }
}
```

The fixture establishes a concrete input case: a loyalty customer with two order lines totaling
3300 cents. At the identified application revision, pricing applies a 330-cent discount before
calculating 238 cents of tax, producing 3208 cents. A test can preserve that order of operations
rather than checking only that the price is positive.

To test this case, set the catalog to the observed prices, call `price_order` with the
recorded arguments and assert the recorded result:

```python theme={"system"}
order_service.CATALOG_PRICES = catalog_prices
assert order_service.price_order(**inputs) == expected
```

For the supplied example, run the extracted `test_pricing.py` in the same directory as
`order_service.py`, `trace.json` and `values.json`:

```sh theme={"system"}
.venv/bin/python test_pricing.py
```

The script checks that the source fingerprint matches the trace, reads the pricing values
saved above and fetches both `unit_price` calls using `VEXLO_ENDPOINT` and `VEXLO_TOKEN`.
It requires a complete graph and one attempt per selected call, then reconstructs the catalog
and runs the assertion. Missing, truncated or unavailable values stop the test instead of
becoming an incomplete fixture.

This test covers the recorded pricing input and the two observed catalog prices. It does not
replay checkout or reproduce payment, inventory or customer service state. Reconstruct those
fixtures separately when testing functions that depend on them.

Use a captured input only after checking its representation and meaning. Truncation, withheld
fields, type-only representations and missing external state can prevent it from becoming a
valid test input. After changing code, record comparable executions at the new revision and
inspect their graphs and values. Vexlo's recordings supply evidence; they do not automatically
compare revisions or prove a change correct.

## Interpret missing evidence

A known function relationship can remain after all supporting trace examples expire. The
function index and trace records have separate retention lifetimes. When resolving a retained
example, the server skips it if its source trace or attempt no longer exists. In that case,
function detail can still show an observed caller while `examples` is empty.

An empty examples list does not invalidate an indexed relationship or establish that the
function became unused. Values can also expire while a trace and its previews remain readable.
Use [Retention](https://docs.vexlo.ai/deployment/retention.md) for the lifetime boundaries.

Check service, team and environment scope, [recording coverage](https://docs.vexlo.ai/reference/http-search.md), and
[Troubleshooting](https://docs.vexlo.ai/deployment/troubleshooting.md) before concluding a function has no recorded
usage. Even a complete retained set describes only instrumented executions that reached this
server. For agent access to the same workflow, use `vexlo_list_functions`, `vexlo_get_function`,
`vexlo_get_function_examples`, `vexlo_get_trace` and `vexlo_get_values`, documented in
[MCP tools](https://docs.vexlo.ai/reference/mcp.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.