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

# AI visibility and traffic

> Separate captured answer evidence from first-party traffic, verified agent behavior, and business outcomes.

AI visibility is useful only when you can distinguish a generated answer from separately observed demand and outcomes. Epode keeps those evidence classes adjacent without pretending they are causally linked.

```text theme={null}
captured answer · cited source · observed traffic · verified behavior · business outcome
```

## Start with market truth

The AEO workspace asks a company owner or admin for five public inputs:

* brand name and primary domain;
* target audience;
* topics where the product should be discovered or evaluated;
* competitors buyers and answer engines compare with the product.

Epode uses this company-authored profile to create a durable query portfolio. It does not import customer prompts, private conversations, or inferred personal profiles.

## Cover the buyer journey

The generated portfolio deliberately spans three stages:

| Stage      | What it measures                                                                           | Example shape                                  |
| ---------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------- |
| Discovery  | Literal brand presence before a buyer has selected vendors; generated prompts are unseeded | “What are the best … options for …?”           |
| Evaluation | Literal presence and cited evidence in general-provider prompts, which are unseeded        | “Which … providers are most trusted, and why?” |
| Decision   | Captured answers when the brand is already named, reported separately as seeded            | “Should … use this brand for …?”               |

Named competitor comparisons are also seeded and supplement those stages instead of becoming the whole portfolio. Custom prompts must explicitly declare whether they name the monitored brand; Epode does not infer that classification on create or update. Prompt identities and revisions remain stable so later observations preserve the classification and text that actually ran across ordinary profile updates.

The headline metric is **unseeded literal brand presence**: completed unseeded answers containing the configured brand name divided by completed unseeded answers. Seeded literal presence has its own numerator, denominator, and rate, and discovery, evaluation, and decision each expose their own completed populations. A zero denominator is reported as `null`, never `0%`. The compatibility aggregate that mixes seeded and unseeded prompts is deprecated and must not be presented as organic discovery.

Literal name presence does not establish recommendation, sentiment, ranking, preference, or share of voice. A partial run uses only completed observations in its denominators. Truncated answers remain flagged and counted in evidence coverage, and an answer without a confirmed-completed search call remains explicitly distinguishable from one with completed search evidence.

## Keep the evidence ladder visible

| Layer                  | Evidence class     | What it can prove                                                                          |
| ---------------------- | ------------------ | ------------------------------------------------------------------------------------------ |
| Answer visibility      | Synthetic evidence | What a named engine and model returned for a versioned query at a recorded time and locale |
| Crawlers and referrals | Observed traffic   | What first-party CDN, server, or analytics data saw arrive                                 |
| Agent behavior         | Protocol evidence  | What a confirmed MCP or other instrumented agent interaction did                           |
| Business outcomes      | Company evidence   | What the product, CRM, commerce system, or analytics stack recorded afterward              |

Epode does not roll these layers into one unexplained score. An AEO metric must say what happened, how Epode knows, and what remains unobserved. Product-wide interaction and outcome totals remain explicitly unattributed until referral or session evidence links them to AEO.

## Run and inspect monitoring

An owner or admin selects active queries, a provider, and a locale. Epode creates a durable run before provider work starts. Workers preserve completed prompts and may safely recover interrupted work only when the durable ledger proves that provider dispatch did not begin. An ambiguous or post-dispatch failure is terminal unknown and is never automatically replayed, because another request could duplicate provider work and spend.

Each observation records the provider, requested model alias and provider-resolved model, captured answer, URL citations, literal brand and configured-competitor presence, whether the answer cites the monitored domain, latency, token usage, and completion or failure state. Run detail exposes the sorted set of resolved models so comparisons do not mistake a mutable alias for a fixed snapshot. OpenAI Responses with web search is the production execution path. Search-call output items are counted as attempted, completed only when their status is exactly `completed`, or unknown when status is missing or unrecognized; `webSearchUsed` is true only when at least one call is confirmed completed. Returned citations are captured evidence, but provider configuration, a failed call, or an unknown status does not prove that a specific answer completed search. Ollama is available for real local-model validation and is visibly labelled as non-web-grounded evidence.

The OAT path captures guest consumer surfaces separately from API evidence. A manual first check creates one `consumer_guest` lane per answer engine and one OAT Test Run per engine and prompt. Before each external call, Epode commits a dispatch intent; an accepted Test Run is then correlated by its stored OAT run ID. OAT submits results to the service-authenticated internal ingest endpoint, which validates the stored engine, surface, prompt, and market and makes exact batch replays no-ops. Scenario IDs are deployment configuration owned by the capture-runtime seam, not inferred from engine names. A lane can dispatch only when that engine has a configured OAT scenario. Missing configuration produces an explicit terminal lane failure and does not block the rest of the run group.

Application-level run and provider-call caps are defense in depth, not a substitute for provider billing controls. Production deployments should use a dedicated provider project and configure provider-side hard spend limits and billing alerts in addition to Epode's caps.

Raw AEO retention uses the minimum of the configured `AEO_RAW_RETENTION_DAYS` operator horizon and the tenant's shortest environment `retention_days`, whichever are set. Every normally created product has a Default environment with `retention_days` defaulting to 30 days, so raw AEO purging is effectively on at 30 days unless the tenant raises that commitment; keep-forever requires both horizons to be unset, which does not occur for a normally created product. A run group is deleted atomically only after every member is terminal and the newest member completion is past the cutoff for that effective horizon. This policy affects only raw AEO runs. Existing retention behavior for audit, schedule, prompt, HTTP, interaction, consent, and context data remains unchanged.

Permanent series rows survive raw-data retention. They exclude seeded prompts and count only completed, unseeded observations with a recorded answer. `usableCount` is that eligible observation count on every metric row, never an alias for the metric denominator. For consumer-guest captures, `completed` and `completed_degraded` are usable, but `hiIntegrityCount` includes only `completed` outcomes with no challenge; the absence of a challenge alone does not make a degraded capture hi-integrity. API observations have no capture outcome and retain their existing eligibility. Captcha, rate-limit, authentication, extraction, and provider-error outcomes remain stored in retained raw evidence but are excluded from permanent series. A challenged capture is therefore a gap, not a dip: it does not add to `usableCount` or inflate the `visibility` denominator. Denominator units are metric-specific: `visibility` divides responses mentioning the tracked brand by responses mentioning at least one tracked brand, while `share_of_voice` uses presence-based mentions across all tracked brands, dividing the tracked brand's distinct appearances by distinct tracked-brand appearances summed across responses. Each tracked brand contributes at most once per response, though one response can contribute several different tracked brands, so a share-of-voice denominator may exceed `usableCount`; a visibility denominator may not. `share_of_voice` is permanently presence-based. Any future occurrence-based measure requires a new metric key rather than redefining historical rows. When competitor lists differ across runs, rows in one bucket can have different denominators: each tracked brand's denominator covers only runs where that identity was tracked. Tracked-brand identities are trimmed, Unicode-casefolded, and whitespace-collapsed when written. Readers return the stored identity verbatim. Editing a competitor name starts a new permanent series identity and never retroactively merges history.

The series market is the run locale verbatim. Nullable `marketCountry` and `marketLanguage` capture fields do not define series identity.

An archived custom prompt and its idempotency payload are deleted only after the product retention boundary and only when no retained observation references the prompt. AEO runs are not currently bound to one selected environment.

The workspace response returns at most the 64 most recently created retained runs. That bounded cap covers a full year of weekly comparisons, including a 90-day rotation, without returning an unbounded history; older retained runs remain addressable by run ID.

CDN, server, analytics, CRM, and commerce connectors remain separate collection layers. Until one is connected, the dashboard reports that state directly rather than showing synthetic coverage as observed traffic.

## Turn terminal evidence into internal review work

Completed and partial runs can produce rule-versioned marketer recommendations. These are deterministic interpretations of retained run and observation snapshots, not generated strategy and not copied answer text. Each recommendation reports its numerator and denominator, sample and prompt-portfolio coverage, seeded or unseeded stage context, every matching observation ID, the immutable prompt snapshot, literal signal and search/truncation qualifiers, and citations grouped under the observation that returned them. A zero denominator remains unknown.

Version 1 has seven deliberately narrow recommendation kinds:

| Kind                                   | Safe interpretation                                                                                                                                                                                                                                                                                   |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `validate_evidence`                    | Search was not confirmed completed, a literal signal is null, the observation failed, or truncation makes a negative unsafe. Stronger recommendations are suppressed for that observation.                                                                                                            |
| `investigate_brand_absence`            | A grounded, untruncated answer did not literally contain the brand. Unseeded discovery evidence is reported separately from seeded consideration evidence. This is not sentiment, rank, preference, or a recommendation outcome.                                                                      |
| `investigate_first_party_citation_gap` | A grounded, untruncated answer did not literally cite the snapshotted first-party domain. It is a prompt to investigate, not an instruction to create or change content.                                                                                                                              |
| `review_competitor_presence`           | A configured competitor name was literally present. Presence does not prove sentiment, pressure, preference, rank, or recommendation.                                                                                                                                                                 |
| `repeat_baseline`                      | A completed run was fully grounded and had no stronger literal gap. Partial, local, no-search, and otherwise insufficient runs cannot receive it.                                                                                                                                                     |
| `absent_on_engine`                     | On a run from a reportable answer engine, a grounded, untruncated answer literally did not mention the tracked brand while it literally named a tracked competitor. This is a co-occurrence in one answer, not causation, rank, or attribution of the absence to the engine.                          |
| `third_party_citer`                    | At least three distinct losing prompts (unseeded, completed, answered, mentioning a tracked brand but not the tracked one) literally named the same competitor across the run. The evidence is those losing prompts' observation IDs. Recurrence is a pattern to investigate, not an influence claim. |

The two engine-scoped kinds fire only for runs on reportable answer engines and cite the exact observation IDs they were derived from. A citation is a lineage pointer, never a claim that the observation caused, ranked, or was attributed to anything.

An owner or admin can persist one internal action for a source run, kind, and rule version. The create API accepts only an idempotency key, source run ID, kind, and rule version; the server re-derives the signal and exact evidence IDs. Members can read actions, while only owners and admins can move them through `open`, `in_progress`, `completed`, and `dismissed`. Terminal tasks can only return to `open` through an explicit reopen transition. The authenticated user—not a request field—is written to the append-only transition history.

Action state records whether an internal review task ended. `completed` does not mean content was created or published and does not claim that visibility improved. Version 1 has no CMS credentials, destination URL, draft or body field, webhook, issue creation, content-write operation, publish endpoint, or outbound action call.

Actions store workflow identifiers and normalized observation links only. Titles, descriptions, coverage, contexts, prompt snapshots, literal signals, and citations are re-derived when an action is read and checked against the stored observation IDs; a mismatch is surfaced as an integrity error. Raw AEO retention uses the shorter configured operator or product-environment horizon; keep-forever applies only when both are unset. `expiresAt` uses that same effective horizon, so it is null only when neither horizon is configured. Deleting an expired source run atomically removes its actions, evidence links, and history, so an open task can disappear at expiration and never extends raw-evidence retention.

An owner or admin can expand a stored action's cited observations into a markdown brief. An expanded brief is a draft for a human: it is sanitized on write, stored append-only against the action, and never published. Expanding performs no CMS write, no destination transport, and no outbound call other than the model request itself, and it deliberately leaves the action's own state and revision untouched. Each expansion is idempotent by its key: the same key replays the stored attempt without a second call or charge, and a failed generation is stored as a retryable failed row. Expanding is the one action operation that does not re-derive the recommendation, so an action whose source evidence has drifted can still be expanded and returns 200 while reading that same action returns an integrity conflict; the brief expands evidence the action already cited and never asserts current state. Both the expand and the prompt-generation endpoints charge a trial session only on accepted output — a failed or fallback result costs nothing.

Prompt generation returns 10–20 proposals from the configured model when it is available, or a deterministic template derived from the profile otherwise. A generated prompt set is a draft: nothing is applied automatically, and the user edits and saves prompts through the existing custom-prompt CRUD. Generation is idempotent under a replayed key, which returns the stored accepted result without a second model call or charge; the profile's market fields (`marketCountry`, `marketLanguage`) act only as configuration defaults for that request, while the run locale remains the authoritative observation and continues to define series identity.

## Why the existing core matters

Many AEO tools stop at sampled answers. Epode's agent telemetry, permissioned context, decision records, and outcome events can provide separate lower-funnel evidence. Until referral or session linkage exists, those product-wide events are not attributed to an AEO observation. Shopify can be one outcome connector, but the model also supports SaaS, financial services, marketplaces, media, and other products through CRM, product analytics, and first-party infrastructure data.
