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

> ## Agent Instructions
> Every Lath operation is POST https://platform.trylath.com/<operation name with dots replaced by slashes>, with a JSON body and `Authorization: Bearer <key>`. `email.send` is POST /email/send.
> Branch on `error.code`, never on `error.message`. Every refusal also carries `error.fix`, which names the next step.
> Send an `Idempotency-Key` header on any operation that is not retry-safe, so a retry cannot run it twice.
> A `lath_test_` key emails only the account's own members and sends no SMS; a `lath_live_` key reaches real recipients and is billed.
> The OpenAPI document, generated from the same registry as the routes, is at https://platform.trylath.com/openapi.json.

# Sign in your first user

> Two calls: send a code to an address, then exchange the code for a session. The user does not have to exist first — verifying the code is what creates them.

<Steps>
  <Step title="Send the code">
    `auth.signin.start` takes a `method` and an `identifier` and returns a `challengeId`. The methods are `email_code`, `email_link` and `sms_code`.

    It never reveals whether the address is already a user. That is deliberate: an endpoint that answers differently for a known address is an account-enumeration oracle, and this one cannot be used as one.

    Its permission is `auth:public`, so your frontend can call it with a publishable key. No secret key in a browser.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://platform.trylath.com/auth/signin/start \
        -H "Authorization: Bearer lath_pk_..." \
        -H "Content-Type: application/json" \
        -d '{"method":"email_code","identifier":"you@example.com"}'
      ```

      ```ts TypeScript theme={null}
      import { createLath } from "@trylath/sdk";

      const lath = createLath({ key: process.env.LATH_PUBLISHABLE_KEY });

      const { result } = await lath.auth.signin.start({ method: "email_code", identifier: "you@example.com" });
      ```

      ```bash CLI theme={null}
      lath auth signin start --method email_code --identifier you@example.com
      ```
    </CodeGroup>
  </Step>

  <Step title="Exchange it for a session">
    `auth.signin.verify` takes the `challengeId` and the six digits. On a first sign-in it creates the user and a verified identity, then issues a session: a short-lived access token and a rotating refresh token.

    There is no separate sign-up call, and that is the point — an account is a side effect of proving an address, so there is no window in which a user exists with an address nobody has proved.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://platform.trylath.com/auth/signin/verify \
        -H "Authorization: Bearer lath_pk_..." \
        -H "Content-Type: application/json" \
        -d '{"challengeId":"...","secret":"123456"}'
      ```

      ```ts TypeScript theme={null}
      import { createLath } from "@trylath/sdk";

      const lath = createLath({ key: process.env.LATH_PUBLISHABLE_KEY });

      const { result } = await lath.auth.signin.verify({ challengeId: "...", secret: "123456" });
      ```

      ```bash CLI theme={null}
      lath auth signin verify --challengeId ... --secret 123456
      ```
    </CodeGroup>
  </Step>

  <Step title="Handle the second factor">
    When the user has a second factor, or the environment requires one, `verify` returns `mfa.mfaToken` **instead of** a session. Read for the session before assuming you have one — a client that reads `session.accessToken` unconditionally crashes on exactly the accounts that took security seriously.

    Finish with `auth.mfa.verify`, which takes the token and the digits from the authenticator app, or a recovery code.
  </Step>

  <Step title="Check the token on your own API">
    `verifyAccessToken` in `@trylath/sdk` validates a token against your environment's published keys, so your backend does not call Lath on every request. `requireUser` does it for a standard `Request`.

    See the SDK guide for the framework-shaped versions.

    ```ts theme={null}
    import { requireUser } from "@trylath/sdk";

    const user = await requireUser(req, { environmentId: process.env.LATH_ENV_ID! });
    ```
  </Step>
</Steps>
