# Frequency capping

> How show-once frequency capping works in StepsKit. Tours marked show-once won't play twice for the same identified visitor; anonymous visitors fall back to a session-scoped check.

*Source: https://stepskit.com/docs/concepts/frequency-capping*

Frequency capping decides how often a visitor sees a given tour. The
default is "show every time": each visit to a page that matches the
tour's targeting plays the tour. You can change this on a per-tour basis
in the dashboard.

## Show once

Set a tour to **Show once per visitor** in the dashboard's
[visibility settings](/app). With this on:

- The first time the visitor reaches a page that matches the targeting
  rules, the tour plays.
- Once they either complete or dismiss it, they won't see it again.
- "Dismiss" includes closing the tour mid-way, navigating away, and
  closing the tab while a tour is open.

## How it's enforced

When the StepsKit embed fetches active tours, the server filters out
"show once" tours that this visitor has already completed or dismissed.
That filter is keyed on the visitor's `id`, which is why
[visitor identification](/docs/concepts/visitor-identification.md) matters.

The check is fail-open: if the events lookup fails (rare), the tour is
returned and played rather than withheld. We'd rather show a tour twice
than swallow a real impression.

## Anonymous visitors

If `data-user-id` (or `id` in `setUserAttributes`) isn't set, the
visitor is anonymous. "Show once" then falls back to a session-scoped
check using browser storage:

- Works within the current browser session.
- Resets when the visitor clears site data or uses another device or
  browser.

For most marketing-site use cases this is fine. For an authenticated
product, always pass `id`.

## Identifying the user before the first fetch

The cleanest pattern is to render `data-user-id` directly into the
script tag server-side, so the very first tour fetch carries the
correct visitor:

```html
<script
  src="https://cdn.stepskit.com/stepskit.latest.js"
  data-api-key="YOUR_API_KEY"
  data-user-id="user_123"
  async
></script>
```

If you can only identify client-side, use
`setUserAttributes({ id }, { autoRefresh: true })`. StepsKit's
init/refresh coordination guarantees a "show once" tour won't play
against a stale anonymous fetch — see
[Visitor identification](/docs/concepts/visitor-identification.md#the-init-refresh-race-condition).
