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

# Authentication

> Sign in with the card key itself: no passwords, no custody.

The browser already holds the card key (unlocked by the passkey, see [The card](/learn/card)). Signing a one-time message with it proves
ownership. The card itself (number, expiry, CVV) is issued after an identity check with Didit, with the holder
name taken from the verified document.

```ts theme={null}
// `account` is a viem LocalAccount for the card key.
// The app gets it from the passkey (apps/web/lib/passkey.ts); in a script you can use
// privateKeyToAccount from "viem/accounts".

const API = "https://api.tozzecard.xyz";

const { message } = await fetch(`${API}/auth/challenge`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ address: account.address }),
}).then((r) => r.json());

const { token, kyc, card } = await fetch(`${API}/auth/verify`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ message, signature: await account.signMessage({ message }) }),
}).then((r) => r.json());
// kyc: "none" | "pending" | "approved" | "declined" | "duplicate"; card is null until "approved"
```

## Activate the card

```ts theme={null}
const auth = { authorization: `Bearer ${token}`, "content-type": "application/json" };
const { url } = await fetch(`${API}/kyc/session`, {
  method: "POST",
  headers: auth,
  body: JSON.stringify({ returnUrl: "https://app.tozzecard.xyz/" }),
}).then((r) => r.json());
location.href = url; // Didit: document + selfie, then back to returnUrl

// Back in the app: poll while pending
const me = await fetch(`${API}/me`, { headers: auth }).then((r) => r.json());
```

One document holds one card: a second card for the same document ends as `duplicate`. Didit tells the API the
result by webhook; the API reads the document from Didit itself, never from the app.

<Note>
  The card number and CVV identify the card in the app only. They are not a payment-network number and nothing
  accepts them as a credential; payments are passkey-signed authorizations.
</Note>

Tokens last 30 days. `POST /auth/signout` ends one early.

## Errors

Errors are JSON `{ error, code }`. A missing or expired token returns `401`; sign in again.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.