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

# Mobile handoff

> How the verify web app hands a verification off to the user's phone.

Reading the cryptographic chip in an identity document requires NFC, which is a phone-only capability. When a user lands on `verify.kayle.id`, the verify app coordinates a handoff to a mobile client — either Kayle's iOS or Android app on the same device or a desktop-to-phone QR pairing.

You don't implement any of this. The mobile apps and the verify app handle it. This page exists so you understand what your user is seeing and why.

## The handoff token

The verify app calls `POST /v1/verify/session/:id/handoff` to mint a one-time `mobile_write_token`:

```json theme={null}
{
  "v": 1,
  "session_id": "vs_...",
  "attempt_id": "va_...",
  "mobile_write_token": "...",
  "expires_at": "2026-05-05T11:05:00Z"
}
```

The token is valid for **5 minutes**. The mobile app receives it via a deep link (same-device handoff) or a QR code (desktop-to-phone), then opens a WebSocket to the API and uses the token in its first message to authenticate.

## Same-device idempotency

Within a 60-second window, repeated handoff requests for the same session reuse the same token. After that window, a new token is issued and a new attempt may be started. The token is consumed on the first successful mobile hello — replays fail.

The first device that authenticates becomes the only device that can finish the attempt: its hashed device ID is recorded against the attempt and subsequent claims from a different device are rejected. This is the basis for the **same-device-only** flag exposed in the public session status — once any attempt has been claimed, the verify app stops offering desktop-to-phone pairing.

## What you should do

For most integrations, nothing. Send the user to `verification_url` and wait for the webhook. If you want to render your own status indicator while the user is mid-flow, poll `GET /v1/verify/session/:id/status` (a public endpoint scoped to the session ID) — it returns the session's current status and a `same_device_only` flag without exposing any document data.
