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

# Start a session

> Mint a session on your server with your secret key.

One session is one person's attempt at one verification. Create it server side. The secret key never leaves your server; the browser only ever sees the `session_id`.

```ts theme={null}
const VY_SK = process.env.VY_SK; // sk_test_… while building, sk_live_… to ship

const res = await fetch("https://trust.verifyyou.com/v3/initialize", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${VY_SK}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    verification_id: "<verification_id>",
    // where they land when done
    redirect_url: "http://localhost:3000/verified",
    // optionally link with your database
    // external_id: user.id,
    // link with a new person or external identifier
    // email: user.email,
  }),
});

if (!res.ok) {
  const { detail } = await res.json();
  // 400 one_identifier_only               send email OR phone, never both
  // 400 identifier_unrecognized           that email/phone isn't a shape we parse
  // 400 invalid_redirect_url              must be absolute, and https off localhost
  // 400 verification_requires_identifier  this check is set to "Provided by you"
  // 401 / 403                             wrong key, or a test-only field on a live key
  // 404 no_matching_verification          that verification_id isn't on your account
  // 409 link_not_active                   this check runs on invites, not on code
  throw new Error(`verifyyou initialize: ${res.status} ${detail}`);
}

// => { session_id: "…", url: "…" }
// use the session_id to run the verification on the frontend;
// a session's ttl is reasonable but not infinite
const { session_id } = await res.json();
```

Attach what ties the run to your records:

| You have            | Send                   | You get back                                                 |
| ------------------- | ---------------------- | ------------------------------------------------------------ |
| An existing account | `external_id`          | Duplicates reported against your id                          |
| A new signup        | `email` or `phone`     | The run is bound to that identity; the person cannot swap it |
| Nothing (anonymous) | just `verification_id` | A session, and a pass with no identity attached to it        |

## Anonymous checks

A verification configured as **Anonymous** collects no email and no phone. The
person never sees an identity step, and three things follow that are easy to
trip over:

* **Send neither `email` nor `phone`.** Neither is rejected, but either one seeds
  the run and quietly turns it into a bound one, which is not what the
  verification is configured to do.
* **`identity` on the confirmation is always `null`.** Do not build a flow that
  expects an address back, and do not ask the person for one to reconcile
  against.
* **There is no token-free lookup.** [`POST /v3/confirmations`](/v3/dev/spec/server-api/lookup)
  matches on a credential, and an anonymous run binds none, so it answers
  `404 no_settled_pass` every time. The `vyt` is the only way to read the
  result, so do not drop it.

<Warning>
  While a run carries no identifier at all, an anonymous verification meters
  **one successful pass per person, for life**, ahead of any limit configured on
  the verification. Sending an `external_id` is enough to take the run out of
  that ceiling and restore the configured limit. If your second test run comes
  back denied, this is why.

  An option to control this is coming: check segmentation will let you group
  the collision gate, so a pass counts within a segment you define instead of
  once for life.
</Warning>

`external_id` is the only handle an anonymous check gives you. It is how you tie
a pass back to your own record, and how duplicates get reported against your id.

Minting server side is also your rate limit and your gate. Nobody can flood your account from a browser, and you decide which requests get a scan. Full parameters and errors: [`POST /v3/initialize`](/v3/dev/spec/server-api/initialize).
