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

# Triggers

> Triggers are how your app tells Insito a survey-worthy moment just happened — and how the dashboard decides whether to show one.

A **trigger** is a short event name your app fires
(`MicroSurvey.trigger("checkout_completed")`) and an Insito admin
attaches to a survey in the dashboard. Splitting them like this means
non-engineers can change what gets asked without shipping a new app
build.

## Anatomy of a trigger

```mermaid theme={null}
sequenceDiagram
  participant App
  participant API
  participant Dash as Dashboard config

  App->>API: POST /v1/sdk/event {event: "checkout_completed", userId}
  API->>API: check project throttle (Redis)
  API->>Dash: find active survey matching trigger + filters
  Dash-->>API: surveyId or null
  API-->>App: {survey: {...}} or {survey: null}
  Note over App: If survey != null → InsitoProvider opens modal
```

Three things have to be true for the modal to appear:

1. A **survey exists** with a matching trigger condition and
   `status: "active"` in your project. Conditions can include SDK
   events (`trigger('checkout_completed')`), screen visit thresholds,
   app open, or after enough app starts — combined with **OR logic** (whichever
   happens first).
2. The current user **passes throttling** (7 days default, configurable per
   survey). Throttling is checked first, before any survey matching.
3. The user **has been identified** in the current session
   (`MicroSurvey.identify` was called).

If any of the three are false, the API returns `survey: null` and the
SDK silently returns to `idle`. No errors thrown, nothing flashes on
screen — the user is none the wiser.

## Naming conventions

Trigger keys are free-form strings, but conventions help when several
people are wiring surveys in the dashboard.

* `lowercase_snake_case` by convention. Keys are stored as authored (allowed
  characters are `A–Z`, `a–z`, `0–9`, and `_`); there's no server-side
  case-folding, so `Checkout` and `checkout` are different keys.
* **Verb\_in\_past\_tense** for completed actions: `signup_completed`,
  `checkout_completed`, `subscription_renewed`.
* **Noun for visits**: `home_screen`, `settings_screen`.
* **Suffix** with `_v2`, `_v3` if you change semantics, so old SDK
  versions on user phones don't accidentally hit the new survey.

## Where to fire triggers

The "right" place to call `MicroSurvey.trigger()` depends on what you
want to measure:

<CardGroup cols={2}>
  <Card title="Post-conversion" icon="cart-shopping">
    The checkout success screen, payment confirmation, "thanks for
    upgrading" page. High-signal moments — NPS lands close to the
    actual experience.
  </Card>

  <Card title="Post-onboarding" icon="user-plus">
    After the welcome flow finishes. "How easy was that?" with a
    1–5 rating. Spot drop-off causes before they churn.
  </Card>

  <Card title="Feature first-use" icon="sparkles">
    When the user finishes their first action with a new feature.
    Pair with [screen tracking](/sdk/react-native/screen-tracking)
    so you can target by path.
  </Card>

  <Card title="Manual feedback" icon="comment-dots">
    A "Send feedback" button in your settings screen. Fire
    `MicroSurvey.trigger("manual_feedback")` from `onPress`.
  </Card>
</CardGroup>

## What NOT to do

<Warning>
  Do not fire triggers on every render. `useEffect` with an empty
  dependency array — or a dedicated user action — is the right
  pattern. The SDK guards against in-flight triggers, but you'll
  burn impressions for nothing if the call fires 10 times per screen.
</Warning>

```tsx theme={null}
// Bad — fires on every re-render
<Text>{MicroSurvey.trigger("home_screen")}</Text>

// Good — fires once on mount
useEffect(() => {
  void MicroSurvey.trigger("home_screen");
}, []);

// Also good — fires on user action
<Button onPress={() => MicroSurvey.trigger("manual_feedback")} />
```

## Server-side evaluation

Triggers evaluate server-side. Implication: you can pause a survey
mid-rollout (set its status to `paused`), change its questions, or move
it to a different trigger key, and users on the old app build will
see the change instantly. No re-release needed.

This also means **deleted surveys are inert**. The SDK fires the
trigger, the server doesn't find a match, the SDK returns to
`idle`. No errors, no warnings, no `console.log` noise.

## Multi-condition triggers (OR logic)

In the survey builder **Trigger** tab you can enable multiple
condition cards at once:

* **Events** — one or more SDK trigger keys (`checkout_completed`, etc.)
* **Screen visits** — one or more screens with an "after N visits" threshold
* **App open** — fires after a minimum number of app starts (cold launches by default; optionally includes foreground resumes)
* **Set delay** — wait N seconds after a condition qualifies before showing

The API evaluates each `event`/`evaluate` call in this order: (1) project-level
throttle, (2) respondent exists, (3) a survey's trigger condition matches,
(4) the survey's response limit isn't reached, (5) audience filters pass,
(6) the per-user show cap (`maxShowsPerUser`) isn't reached. The first survey
that clears all six is returned.

## Limits

* Event trigger keys can be up to 128 characters, must match `^[a-zA-Z0-9_]+$`.
* A single survey can list **multiple event keys** (OR within the survey).
* Each project can still have **only one active survey per top-level
  `trigger_key` slug** (the survey's unique identifier in the database).
  Multi-event surveys match any key in their Events list via
  `trigger_config.triggers.events.keys`.
* Throttling is per-project per-user (Redis), not per individual event key.

## The variable registry

Every event key and screen name is tracked in the variable registry, split into
the [Events](/dashboard/variables) and **Screens** settings tabs. Keys are
auto-discovered as the SDK fires them,
[declared in your SDK config](/sdk/react-native/configure#events-and-screens),
or added in the dashboard. Auto-discovered keys land in an **Auto-discovered**
list and must be **approved** before they appear in the builder's trigger
selects — so you pick from a curated set of known variables instead of retyping
strings (and typos never leak into the picker).

## Audience properties

A trigger controls *when* a survey is evaluated; **audience filters** control
*who* qualifies. They run against a flat bag of properties on the respondent:

* **Device properties** the SDK auto-captures on every `identify()` —
  `platform`, `app_version`, `locale`, `timezone`, `osVersion`, `sessionCount`,
  `daysSinceInstall`, `environment`.
* **User properties** you send via
  [`identify({ properties })`](/sdk/react-native/api-reference#microsurvey-identify-args),
  discovered into the [User Properties registry](/dashboard/user-properties).

Both are evaluated server-side with the same engine, so a trigger fire only
shows a survey when every audience condition also passes.

## Next

<CardGroup cols={2}>
  <Card title="Throttling rules" icon="clock-rotate-left" href="/concepts/throttling">
    How the throttle window works and how to tune it.
  </Card>

  <Card title="App variables" icon="list-check" href="/dashboard/variables">
    The registry of events and screens behind the trigger selects.
  </Card>

  <Card title="User properties" icon="user-tag" href="/dashboard/user-properties">
    Custom user attributes for audience targeting.
  </Card>

  <Card title="Creating a survey" icon="wand-magic-sparkles" href="/dashboard/creating-surveys">
    Walk through the dashboard survey builder.
  </Card>
</CardGroup>
