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

# onVerified callback

> End-of-verification callback fired exactly once per player, regardless of PC cafe status

`onVerified` is an optional callback passed to `PlayWaveServer.init`. It fires **exactly once per player** when the verification flow ends, regardless of whether the player is a PC cafe user.

Without this callback, distinguishing "verification finished but the player is not a cafe user" from "verification still in progress" required polling `isPcCafeUser` (since `onPcCafe` only fires for cafe users). `onVerified` closes that gap.

## Signature

```lua theme={null}
onVerified = function(player, isPcCafe, reason)
    -- player:    Player instance
    -- isPcCafe:  boolean — confirmed value (always false on failure paths)
    -- reason:    string — one of PlayWaveServer.VerifyReason values
end
```

* Fires exactly once per `PlayWaveClientReady` event
* `isPcCafe` is always a boolean
* `reason` is one of the values defined in `PlayWaveServer.VerifyReason` (see below)
* Errors thrown inside the callback are caught with `pcall`, logged as warnings, and do not break the verify flow

## When it fires

`onVerified` is invoked from inside the `PlayWaveClientReady` handler — i.e. after the client's `PlayWaveClient` script signals readiness to the server. The exact firing point depends on the branch taken:

| Branch                         | Firing point                                                       | reason                                                                                               | `isPcCafe`                                          |
| ------------------------------ | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| Teleport resume                | After `POST /game/session/check` response, on session registration | `RESUMED` (success) or `RESUME_REJECTED` (any 4xx/5xx/network failure)                               | success → inherited from origin / failure → `false` |
| Fresh OTT verification         | After `POST /game/session/verify` response                         | `VERIFIED` (success) / `REQUEST_FAILED` (HTTP/network/decode error) / `REJECTED` (`is_valid: false`) | success → server response value / failure → `false` |
| No OTT in `LaunchData`         | Immediately, no HTTP call                                          | `NO_OTT`                                                                                             | `false`                                             |
| OTT without `playwave_` prefix | Immediately, no HTTP call                                          | `INVALID_PREFIX`                                                                                     | `false`                                             |

### Firing order with `onPcCafe`

For PC cafe users on the success path (where both fire):

* **Fresh OTT verification**: `onPcCafe` + client `PC_CAFE` event → `onVerified`
* **Teleport resume**: `onVerified` → `onPcCafe` + client `PC_CAFE` event

<Warning>
  The order between the two paths differs — do not depend on it. Use `onVerified` purely as a "verification flow ended" signal, and put PC-cafe-only side effects inside `onPcCafe`.
</Warning>

### When it does NOT fire

* `TeleportInitFailed` session restoration — the player is already verified before the teleport attempt, so re-firing would duplicate for the same player
* `PlayerRemoving` — not a verification event

## Verify reason values

The `reason` argument is one of the values defined on `PlayWaveServer.VerifyReason`:

| Reason            | Meaning                                                                       | `isPcCafe`            |
| ----------------- | ----------------------------------------------------------------------------- | --------------------- |
| `VERIFIED`        | OTT verification succeeded                                                    | server response value |
| `RESUMED`         | Teleport resume passed `/game/session/check`                                  | inherited from origin |
| `RESUME_REJECTED` | Teleport resume failed server-side check (tampered, expired, etc.)            | always `false`        |
| `NO_OTT`          | Player joined without an OTT in `LaunchData`                                  | always `false`        |
| `INVALID_PREFIX`  | OTT did not start with the `playwave_` prefix (treated as an unrelated token) | always `false`        |
| `REQUEST_FAILED`  | verify HTTP / network / decode error                                          | always `false`        |
| `REJECTED`        | verify API returned `is_valid: false`                                         | always `false`        |

## Usage example

```lua theme={null}
local PlayWaveServer = require(script.PlayWaveServer)
local Reason = PlayWaveServer.VerifyReason

PlayWaveServer.init({
    apiKey = apiKey,
    apiUrl = apiUrl,

    onPcCafe = function(player)
        -- PC cafe-only benefits (fires only for cafe users)
    end,

    onVerified = function(player, isPcCafe, reason)
        if reason == Reason.VERIFIED or reason == Reason.RESUMED then
            -- Normal session — branch on isPcCafe
        else
            -- Verification did not produce a normal session
        end
    end,
})
```

## Relationship with `onPcCafe`

| Outcome                                             | `onPcCafe`    | `onVerified`                                |
| --------------------------------------------------- | ------------- | ------------------------------------------- |
| Cafe user, verified                                 | fires         | fires (`VERIFIED`, `isPcCafe=true`)         |
| Non-cafe user, verified                             | does not fire | fires (`VERIFIED`, `isPcCafe=false`)        |
| Resume succeeds, cafe                               | fires         | fires (`RESUMED`, `isPcCafe=true`)          |
| Resume rejected                                     | does not fire | fires (`RESUME_REJECTED`, `isPcCafe=false`) |
| No OTT / prefix mismatch / request error / rejected | does not fire | fires with that reason, `isPcCafe=false`    |

`onVerified` is purely additive. Existing `onPcCafe` behavior is unchanged.

## Notes

<Note>
  Even when `RESUME_REJECTED` indicates the server-side check for a teleport resume failed, the local session is left in place. The next heartbeat will return 404/410 and clean up automatically.
</Note>
