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

# Event Triggers

> Understand when each FlexyPe webhook event fires and how the full trigger flow works end to end.

FlexyPe webhooks are triggered at specific points in a user's journey — browsing, checkout, and account creation. This page explains the exact conditions that fire each event and how they relate to each other.

## Quick reference

<table>
  <thead>
    <tr>
      <th style={{ width: '30%' }}>Event</th>
      <th style={{ width: '50%' }}>Fires when</th>
      <th style={{ width: '20%' }}>Timing</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>[`PRODUCT_VIEWED`](/webhooks/2026-05/product-viewed)</td>
      <td>Identified user views a product page</td>
      <td>Immediately</td>
    </tr>

    <tr>
      <td>[`COLLECTION_VIEWED`](/webhooks/2026-05/collection-viewed)</td>
      <td>Identified user views a collection page</td>
      <td>Immediately</td>
    </tr>

    <tr>
      <td>[`ABANDONED_SESSION`](/webhooks/2026-05/asr)</td>
      <td>Identified user browses without starting checkout, then goes inactive</td>
      <td>After 15 min of inactivity</td>
    </tr>

    <tr>
      <td>[`ABANDONED_CHECKOUT`](/webhooks/2026-05/acr)</td>
      <td>User opens a checkout session and does not complete the order</td>
      <td>15 minutes after the checkout session opens</td>
    </tr>

    <tr>
      <td>[`NEW_CUSTOMER`](/webhooks/2026-05/new-customer)</td>
      <td>First-time user logs in via FlexyPass and a new store account is created</td>
      <td>On account creation</td>
    </tr>
  </tbody>
</table>

<Info>FlexyPe must be able to identify the user via FlexyPass for any event to fire. Anonymous visitors do not trigger webhooks.</Info>

***

## End-to-end flow

### Scenario 1 — User browses, does not start checkout

<Steps>
  <Step title="User visits your store">
    FlexyPe identifies the user via the FlexyPe Network. No event fires yet.
  </Step>

  <Step title="User views a product page">
    A [`PRODUCT_VIEWED`](/webhooks/2026-05/product-viewed) event fires immediately.
  </Step>

  <Step title="User views a collection page">
    A [`COLLECTION_VIEWED`](/webhooks/2026-05/collection-viewed) event fires immediately.
  </Step>

  <Step title="User goes inactive — no checkout opened">
    The 15-minute inactivity timer starts from the user's last recorded activity.
  </Step>

  <Step title="15 minutes pass — ASR fires">
    An [`ABANDONED_SESSION`](/webhooks/2026-05/asr) webhook is sent with the user's last known browsing state.

    If your endpoint returns a non-200 response, FlexyPe retries using the same `event_id`. See [Retry logic](/webhooks/overview#retry-logic).
  </Step>
</Steps>

<Warning>
  If the user opens a checkout session at any point, the [`ABANDONED_SESSION`](/webhooks/2026-05/asr) event is **not** triggered — [`ABANDONED_CHECKOUT`](/webhooks/2026-05/acr)
  takes over instead.
</Warning>

***

### Scenario 2 — User starts checkout, abandons mid-flow

<Steps>
  <Step title="User visits your store and browses">
    [`PRODUCT_VIEWED`](/webhooks/2026-05/product-viewed) and [`COLLECTION_VIEWED`](/webhooks/2026-05/collection-viewed) events fire as the user browses (same as Scenario 1).
  </Step>

  <Step title="User opens checkout">
    The checkout session is created. ASR will not fire for this user. The 15-minute ACR timer starts from this moment.
  </Step>

  <Step title="15-minute timer starts">
    The timer starts from the moment the checkout session is opened — not from the user's last activity. If the order is not completed within 15 minutes, ACR fires.
  </Step>

  <Step title="User progresses through checkout steps">
    FlexyPe tracks the user's latest state across steps:

    | State                       | Description                     |
    | --------------------------- | ------------------------------- |
    | `INITIATED`                 | Checkout session opened         |
    | `PHONE_ENTERED`             | User verified OTP               |
    | `ADDRESS_LOADED`            | Address page loaded             |
    | `ADDRESS_SHIPPING_SELECTED` | User selected a shipping method |

    <Note>
      Only the **latest state** at the time of inactivity is sent — not one event per step.
    </Note>
  </Step>

  <Step title="15 minutes pass — ACR fires">
    An [`ABANDONED_CHECKOUT`](/webhooks/2026-05/acr) webhook is sent with the user's last recorded checkout state and a unique `event_id`.
  </Step>

  <Step title="User returns to the same session">
    Once a session is marked abandoned and ACR has fired, returning to that session does not trigger a new event. The session remains abandoned.
  </Step>

  <Step title="User completes the order">
    No FlexyPe webhook fires on order completion. Use [Shopify webhooks](https://shopify.dev/docs/api/admin-rest/webhook) to listen for completed orders.

    To link a Shopify order back to an ACR event, match on session ID — FlexyPe passes the same session ID to Shopify via `note_attributes`:

    ```json theme={null}
    note_attributes: [
      { "name": "FlexyPe Session ID", "value": "<session_id>" }
    ]
    ```

    This value will match `payload.session_id` on the corresponding ACR webhook event.
  </Step>
</Steps>

***

### Scenario 3 — User opens a new checkout session before the previous one times out

<Steps>
  <Step title="User opens checkout">
    A checkout session is created (session A). The 15-minute ACR timer starts from this moment.
  </Step>

  <Step title="User closes and opens checkout again before 15 minutes elapse">
    Before session A's timer expires, the user opens a new checkout session (session B).

    <Warning>
      Session A's timer is immediately cancelled — no [`ABANDONED_CHECKOUT`](/webhooks/2026-05/acr) event will fire for session A. A new 15-minute timer starts from session B's open time.
    </Warning>
  </Step>

  <Step title="15 minutes pass — ACR fires for session B">
    If session B is not completed within 15 minutes of opening, an [`ABANDONED_CHECKOUT`](/webhooks/2026-05/acr) webhook fires for session B with a new `event_id` and `session_id`.
  </Step>
</Steps>

***

## ACR — how the timer and sessions work

The ACR timer starts from session open, not from the user's last activity. A session can only be marked abandoned once. Here is a concrete example:

| Time  | User action                           | Latest state    | Event fired                                                         |
| ----- | ------------------------------------- | --------------- | ------------------------------------------------------------------- |
| 10:00 | Opens checkout (session A)            | `INITIATED`     | —                                                                   |
| 10:05 | Verifies OTP                          | `PHONE_ENTERED` | —                                                                   |
| 10:10 | Opens new checkout (session B)        | `INITIATED`     | Session A timer cancelled — no ACR for session A                    |
| 10:25 | 15 min elapsed since session B opened | `INITIATED`     | [`ABANDONED_CHECKOUT`](/webhooks/2026-05/acr) (`event_id: abc-123`) |
| 10:30 | Returns to session B                  | —               | No new event — session already abandoned                            |

<Note>Retried deliveries reuse the same `event_id`. A new session always produces a new `event_id` and a new `session_id`.</Note>

***

## Deduplication

Retries reuse the same `event_id`. If your endpoint receives the same `event_id` twice, the second delivery is a retry — not a new event. Discard it after recording the first successful processing.

See [Idempotency](/webhooks/overview#idempotency) in the overview for a code example.
