# Save searches and export data

> Preserve repeatable filters and create bounded CSV, JSON, or Parquet export jobs.

Canonical page: https://anectico.com/docs/manage/save-and-export-data/


Save a search when the same investigation filter will be reused inside Anectico. Export data only when a
bounded external copy is necessary for analysis, evidence, or an approved downstream workflow.

## Save a search

From a supported signal view, save the current filters with a question-oriented name. Keep searches
private when they contain an individual's investigation context; share only filters that are useful
to the organization.

The CLI exposes the complete saved-search contract:

```bash
anectico saved-searches list --signal logs
anectico saved-searches create --body '{"name":"Checkout errors","signal":"logs","query":{"service_name":"checkout","level":"error"},"is_shared":true}'
```

Saved searches preserve filters, not a frozen result set. Running one later uses current data and the
current retention window. Dashboard query fields are single-line: when the dashboard opens a saved
search created through another client, runs of tabs, line breaks, or Unicode line separators in its
query are shown and submitted as one ASCII space. Other query characters and structured filters are
preserved. The REST API and CLI continue to store the JSON supplied by their caller.

Every saved-search operation resolves to one project. The CLI sends the active profile's project.
Project-scoped REST and MCP credentials may omit `project_id` or repeat only their exact signed
project; an explicit sibling or unknown project is rejected with the same generic not-found result
before Saved Search is called. Organization-level callers must select one consistent project.

## Export a frozen analytics result

In the dashboard, open an event measurement, a funnel participant selection or a retention
participant selection and choose **Export selection**. Choose **Measurement** (one row containing
the complete frozen result) or **Participants** (one row per contributor in the selected population),
then JSON, CSV or Parquet. **Review export** shows the full selection count and source expiry;
**Create export** submits that exact request. Review describes intent, not a reservation or a
promise that the source will still be available. Current permissions and source checks apply.

After an uncertain response, keep the measurement open and use **Retry confirmation**. Closing and
reopening the dialog retains the same request key; leaving or refreshing the measurement does not.
Check **Exports** for an existing job before starting a new request. The job list labels analytics
measurements and participants separately, shows the full source selection count separately from
file rows, and lets you inspect the frozen result, snapshot and selection references. A recovered
job receipt does not prove that its file is ready. When source details are withheld, the interface
shows them as unavailable, never as an empty population. Dashboard downloads retrieve the protected
file through the authenticated API client and save it without navigating away. The browser buffers
the file; use the CLI for large downloads.


Use `create_analytics_export` through MCP's `execute_internal_action`, or the CLI below,
to export an existing analytics result. Select the explicit project and copy the exact result,
snapshot and selection UUIDs from `get_analytics_result`. No query is rerun under those IDs.

```bash
anectico analytics exports create <result-id> \
  --snapshot-id <snapshot-id> --selection-id <selection-id> \
  --projection participants --format json --idempotency-key checkout-export-v1
```

The first call previews the intent without writing or reserving the source. Repeat the identical
command with `--confirm-token <preview-token>` to enqueue it. The preview does not certify source
availability. Creation requires `mcp:write`, `export:write`, `analytics:read`, `persons:read` and any
inherited source permissions. Use a project API key or native user session; external OAuth grants
cannot commission this background work. The generic signal-export REST routes reject analytics sources.

Choose `measurement` for the complete typed result, including its evidence and coverage caveats, or
`participants` for every contributor in the selected population, including explicitly unresolved
identities. A participant page is never the exported population. JSON preserves the structured result;
CSV and Parquet encode the measurement result as JSON text. All three formats include exact source
references, full selection count, computation time and source expiry. Counts do not prove source
completeness. The worker currently refuses selections above a provisional one-million-contributor
limit; this is not a production-capacity guarantee.

Poll with `anectico export jobs get <job-id>` (`export:read`), then use
`anectico export jobs download <job-id> --file <new-file>` (`export:download`) for the authenticated artifact.
These CLI lifecycle calls also require `projects:read` to verify the selected project.
MCP uses `get_export_job` and `get_export_download` through `execute_read_action`, with the same
`project_id` and `job_id`. Queued, running and completed jobs remain subject to current source,
identity, retention, erasure and export-policy checks. Neither the job nor its artifact extends the
source's lifetime. Source checks that cross the expiry deadline also refuse access, including
metadata reads. Unavailable sources cause refusal; they are never replaced with a fresh query.
Temporary source capacity or availability failures use the job's bounded retry budget; a failed
source check reports `analytics_source_unavailable`, not instructions to change a frozen query.

Keep the same idempotency key for identical retries. An existing job can be recovered without
re-admitting its source, but this does not renew access. A changed source, format, projection or
authenticated actor/session conflicts. When source access cannot be verified, job metadata marks
`analytics_details_withheld: true`; hidden counts and progress mean unavailable information, not
zero events or an empty selection. CLI watch, MCP status and the Exports page label this explicitly.

## Export a bounded log set

On **Signals → Logs**, finish a bounded search, choose **CSV** or **JSON** beside **Export**, and
download that exact settled investigation. Anectico freezes the active project, submitted query,
environment, person IDs, service, severity, absolute time window, and structured field filters in
the durable job. Changing the format creates a distinct export intent; retrying an unchanged intent
reuses its idempotency key.

Open **Signals → Exports** to create broad signal exports and manage all durable CSV, JSON, and
Parquet jobs. Choose the signal, columns, and format. The list shows pending, running,
retrying, completed, failed, canceled, and expired jobs; active jobs can be canceled and failed jobs
can be retried. Closing the page does not cancel a job. Creation requires `export:write` plus the
read scope of every signal the export's rows come from (`logs:read` for a logs export; a
unified-search export carries trace and log rows, so it needs `traces:read` and `logs:read` as well
as `search:read`). Listing and polling require `export:read`. Downloading the result separately
requires the owner/admin-only `export:download` scope.

### Create an export from the CLI

To create CSV, JSON, or Parquet jobs from a terminal, use the CLI:

```bash
anectico export logs --idempotency-key incident-124-logs-v1 --body '{
  "format":"csv",
  "query":"payment timeout",
  "project_id":"11111111-1111-1111-1111-111111111111",
  "service_name":"checkout",
  "level":"error",
  "environment":"production",
  "distinct_ids":["customer-42"],
  "start_time":"2026-07-20T10:00:00Z",
  "end_time":"2026-07-20T11:00:00Z",
  "filters":{"attributes.region":"eu-west-1"},
  "columns":["timestamp","severity","message","service_name","trace_id","span_id","distinct_id","attributes"]
}'
```

The CLI sends the active profile's project on every export lifecycle request. Select it first with
`anectico projects use <project-id-or-slug>` (or configure `ANECTICO_PROJECT`) when the credential itself is
not project-scoped.

Choose CSV for spreadsheet analysis, JSON for structured processing, and Parquet for larger analytic
workflows. Always set the narrowest useful project, time range, filters, and columns. Invalid
formats, malformed project IDs, invalid or reversed timestamps, and missing internal signal routing
are rejected before a job is enqueued.

### Download permissions and access changes

An export is not a standing permission. Retrying a job and downloading its result both re-check, at
the moment of the request, that you still hold the read scopes its rows required and that the
project's current **Sent outside Anectico** decision permits the recorded content in the file. So
revoking someone's access to a signal, or tightening that content-policy cell, also takes away
exports already produced under the older decision. If the current project decision cannot be
confirmed, the download is temporarily unavailable rather than using the historical verdict stored
on the job. The next download attempt is refused, and **a transfer already running is stopped too**,
which is worth stating exactly because the two credential types differ:

- **An API key.** While the file is moving, Anectico re-checks the key against its authoritative
  record every few seconds. Revoking it, freezing it, or removing one of the scopes its rows require
  stops the transfer within seconds. The client is left with a file shorter than the length it was
  promised — a failed download in every HTTP client, which is the intended outcome: once a response
  has begun it cannot be turned back into a refusal, and a truncated file is better than the rest of
  one.
- **A browser sign-in.** There is no separate record to re-check, so the bound is the sign-in's own
  renewal cycle: a transfer that was running when access was withdrawn stops within 15 minutes rather
  than within seconds. Revoke the API key if you need the shorter bound.

Downloading is additionally limited to whoever created the job, or to an
organization owner or admin; another credential in the same project sees the same not-found response
as for an unknown job.

### Retry the same export intent

Every create requires a stable idempotency key. Repeating the same request with the same key returns
the original job; reusing the key for different input fails instead of creating a second export.
The dashboard generates this key automatically. CLI and MCP callers must supply one explicitly.
Use 1–128 ASCII letters, digits, `.`, `_`, `:`, or `-`; Anectico rejects surrounding whitespace,
control bytes, and Unicode lookalikes rather than normalizing them into an ambiguous durable key.

Export uses the absolute time window that produced the currently displayed results. Refresh the
search to advance a relative range such as **Last hour**; while a changed or refreshed search is
loading or has failed, export stays unavailable to avoid mixing retained rows with a newer scope.
Pause live tail and wait for the bounded search results before exporting.

### Recorded content and project policy

**An export contains no more than you could read yourself.** Whether you hold `agents:content:read`
is recorded when the job is created, and the rows are assembled against that answer — so a trace,
log or metric export created by a credential without the content scope carries the same metadata you
would see through the corresponding API and none of the prompts, completions, tool arguments or
retrieved documents, wherever they were recorded: span attributes, log attributes, or a metric
exemplar's attributes. Each affected row in the file carries an `anectico.content.withheld` attribute
naming exactly which keys were removed, so a missing body is never mistaken for a call that had
none. That inclusion answer is taken at creation and used when the file is built. It describes what
is physically in the finished file; it is not a permanent download grant. Revoking the scope later
leaves the file unchanged but refuses retry and download until the caller again holds the recorded
source scopes.

**Your project's content policy decides the same question for everyone.** Your permissions answer
who is asking; the content policy answers whether recorded content may leave the project through a
bulk export at all. The two are combined, and the stricter one wins: a project that does not permit
model transcripts to be exported produces exports without them even for an organization owner
holding every scope there is, and a permissive policy never hands content to a credential that could
not read it anyway.

That answer is taken when the export **runs**, not when it is created, so tightening the policy also
applies to jobs already queued. It is then asked again on retry and download: an older file that was
built with content is held back while the current cell denies it, and becomes available unchanged if
the cell later permits it. Changes take up to 30 seconds to apply everywhere. Read the policy with
`GET /api/v1/projects/{projectId}/content-policy` and replace it with `PUT` on the same path; reading
needs `governance:read` and changing needs `governance:write`.

### Inspect withheld content

When a policy withholds content, the job says so. `anectico export jobs get <job-id>` and
`GET /api/v1/export/jobs/{id}` return:

- `content_policy_reason` — empty while the job has not run yet, then one of `allowed` (the policy
  permits everything an export can carry), `policy` (your project's policy does not permit it),
  `no_policy` (no single policy could govern this export — see below) or `resolver_error` (the
  policy could not be read, so content was withheld rather than released on an unverified answer).
  An empty value is not the same as `allowed`: a queued export has not been decided yet.
- `content_policy_default_posture` — `true` when the answer came from the standard posture rather
  than from a grid you set, which happens for a project whose policy has not been written yet. The
  content still travels; the flag is what tells you it travelled on a posture nobody chose, so a
  file produced in that window is never mistaken for one you had approved.
- `content_policy_withheld_classes` — which classes of content are absent from the file
  (`model_transcript`, `tool_arguments`). It describes the file rather than the single rule that
  triggered the refusal: an export withholds recorded content as one group, so denying either class
  removes both.

Those fields describe the decision used to build the stored file. A later download can still be
refused under the current decision even when the job says `content_policy_reason: allowed`; that is
the expected sign that the policy changed after generation, not a contradiction in the file's
disclosure record.

Rows in the file are still self-describing, and they say WHICH of the two answers withheld the
content, because the two are fixed by different things. A row whose content was removed because the
requesting credential was not in the sensitive-content tier carries `anectico.content.withheld`; a
row whose content was removed because your project's content policy refuses that class carries
`anectico.content.policy_withheld`, and no credential changes that. Either field names exactly the
keys that were removed. Where both apply to one key, the policy is what is reported.

A new project uses the standard content-policy posture until its policy is initialized (within
15 minutes). Exports built under that posture report `content_policy_default_posture: true`;
this does not mean someone explicitly approved that policy.

**An export that covers your whole organization rather than one project is the exception.** A content
policy belongs to a project, and an organization-wide export can carry rows from projects whose policies
disagree, so there is no single grid to apply and nothing is assumed on their behalf: the export
carries no recorded content and reports `no_policy`. Scope the export to a project to get its
content.

### Project and retention boundaries

The project scope is captured when the job is created and cannot be widened while it runs.
Project-scoped credentials remain pinned to their own project; a conflicting `project_id` is rejected.
Organization-level users must select one project in their organization. Anectico verifies that selection
before creating, listing, reading, canceling, retrying, or downloading a job; customer export
operations never fall back to an organization-wide result.

The authenticated plan entitlement is also captured when the job is created. A queued export
therefore cannot bypass the retention window by asking the background worker for older timestamps,
or by changing fields inside `query`. Anectico applies the organization's current published retention
state when the worker executes the job; when that state is unavailable, it falls back to the
captured plan entitlement. Exporting does not make expired source telemetry readable again.

### File formats and completeness

Logs-page CSV and JSON files use the fixed fields `timestamp`, `severity`, `message`, `service_name`,
`trace_id`, `span_id`, `distinct_id`, and `attributes`. Structured maps and arrays such as
`attributes` remain structured in JSON and are encoded as deterministic JSON inside the CSV cell.
String CSV cells that could be interpreted as spreadsheet formulas are emitted as text, while
normal CSV quoting, newlines, and UTF-8 are preserved. Logs-page downloads use
`anectico-logs-<project-slug>-<UTC timestamp>.<format>` with the selected `.csv` or `.json` extension.

Exports page through the complete result set instead of silently stopping after 1,000 rows per
signal. The `search` export combines traces and logs; metrics use the explicit `metrics` signal so
metric-specific filters cannot be silently broadened. CSV distinguishes an explicitly present `null` value (written as `null`) from a missing
field (an empty cell). JSON preserves explicit null values. Large jobs are streamed through bounded
temporary files, and jobs that exceed the service's row or file safety ceilings fail without
publishing a partial download.

## Monitor and download jobs

```bash
anectico export jobs list --limit 50
anectico export jobs list --limit 50 --cursor <next-cursor>
anectico export jobs get <job-id>
anectico export jobs download <job-id> --file ./export.json
anectico export jobs download-url <job-id>
anectico export jobs cancel <job-id> --yes
anectico export jobs retry <job-id>
```

Each command remains confined to the active project. A job ID from a sibling project produces the
same not-found response as an unknown job and cannot be inspected, mutated, or downloaded.
Use the exact lowercase hyphenated UUID returned when the job was created; alternate UUID spellings
such as uppercase, compact, braced, or URN forms are not accepted.

Failed jobs expose a stable `error_code` and a safe, actionable `error` message. Clients can use
`query_failed`, `format_failed`, or `upload_failed` for consistent handling without parsing message
text; `export_failed` is the safe fallback for an unclassified failure. Provider responses, storage
object keys, request IDs, and other internal diagnostics are not included in the job response;
operators can find those details in structured service logs.

### Download expiry and local-file safety

Completed results remain downloadable for 24 hours and then move to `expired`; Anectico deletes the
object and refuses further downloads. The file is served by Anectico itself rather than by a storage
link, so it can be authorized while it transfers and resumed with a byte range. Dashboard downloads
are served as attachments for CSV, JSON, and Parquet with the deterministic filename
`anectico-export-<job-id>.<format>`, so downloading preserves the current dashboard page and focus.
`anectico` refuses any redirect from an authenticated request when it leaves that request's initial
configured endpoint origin. For exports, this covers both the metadata request made by `download`
or `download-url` and every redirect while fetching the content. `download` also accepts the returned
address only when it has exactly the configured API origin, so the credential is sent only to that
endpoint. It streams to a private temporary file and atomically publishes a new destination;
it never overwrites an existing file or symlink. Treat an exported file as customer telemetry:
store it in an approved location, restrict access, preserve only as long as needed, and delete local
copies after use. Export does not extend the retention of the source data inside Anectico.

- [Search logs, traces, and metrics](/docs/investigate/search-telemetry)
- [Plans, limits, and retention](/docs/reference/limits)
