# One-Time Codes

One-time codes provide passwordless authentication — the user enters their email, receives a six-digit code, and types it back in to authenticate without a password.

Unlike [magic links](/developer/guides/authentication/magic-links.md), the code is entered on the same device that requested it, so authentication completes synchronously. There is no cross-tab polling — the device that requests the code is the device that signs in.

Just want a login form?

If you don't need to build the UI yourself, the [`<allegro-login-form>`](/developer/components/login-form.md) web component handles the entire one-time code flow out of the box — drop one tag on your page and you're done.

## How It Works[​](#how-it-works "Direct link to How It Works")

1. Call `allegro.member.requestLoginCode(email)` — Allegro emails a six-digit code to that address and creates the audience member if they do not already exist
2. The user enters the code into your UI
3. Call `allegro.member.validateLoginCode(code)` to verify it
4. On success, a JWT session is issued and `allegro:authenticated` fires on `window` with `detail.method === 'otp'`

## Requesting a Code[​](#requesting-a-code "Direct link to Requesting a Code")

```js
await allegro.member.requestLoginCode(
    'user@example.com',
    'https://example.com/account', // optional: where to redirect after auth
    { plan: 'monthly' }, // optional: custom data attached to the session
);

```

| Parameter   | Type                      | Required | Description                                                                   |
| ----------- | ------------------------- | -------- | ----------------------------------------------------------------------------- |
| `email`     | `string`                  | Yes      | The user's email address                                                      |
| `returnUrl` | `string`                  | No       | URL to redirect to after the code is validated. Defaults to the current page. |
| `data`      | `Record<string, unknown>` | No       | Custom data to attach to the resulting session                                |

**Response shape:**

```ts
{
    created: boolean; // true if a new audience member was created
    jwt: string;
    status: 'ok';
}

```

Codes expire and resends invalidate older codes

Each code is valid for 30 minutes. Requesting a new code invalidates any previous code for the session — only the most recently emailed code works.

Requests are rate limited

Requesting a code is limited to **3 requests per email per minute**. Beyond that, the request is rejected with a rate-limit error. Surface a "try again shortly" message rather than retrying automatically.

## Validating a Code[​](#validating-a-code "Direct link to Validating a Code")

```js
const { jwt, return_url, audience_member_created } =
    await allegro.member.validateLoginCode('123456');

```

| Parameter | Type     | Required | Description                       |
| --------- | -------- | -------- | --------------------------------- |
| `code`    | `string` | Yes      | The six-digit code the user typed |

**Response shape:**

```ts
{
    message: string;
    jwt: string;
    return_url: string | null; // the returnUrl from the original request, if any
    audience_member_created: boolean;
    sessions_authenticated: string[];
}

```

On success the session JWT is stored automatically and an `allegro:authenticated` event fires. On failure, validation throws an `AllegroApiError` whose `errorCode` is one of:

| `errorCode`   | Meaning                                                             |
| ------------- | ------------------------------------------------------------------- |
| `INVALID_OTP` | The code is wrong, or no valid code exists for this device          |
| `OTP_EXPIRED` | The code has expired — request a new one                            |
| `OTP_LOCKED`  | Too many incorrect attempts — the code is locked, request a new one |

```js
try {
    await allegro.member.validateLoginCode(code);
} catch (error) {
    if (error.errorCode === 'OTP_LOCKED') {
        // Prompt the user to request a fresh code
    } else {
        // Show "that code wasn't right" and let them retry
    }
}

```

## SDK Events[​](#sdk-events "Direct link to SDK Events")

All events are dispatched on `window` as `CustomEvent` instances.

| Event                   | Detail              | When                              |
| ----------------------- | ------------------- | --------------------------------- |
| `allegro:authenticated` | `{ method: 'otp' }` | A code was validated successfully |
| `allegro:jwt-refreshed` | `MemberJwtPayload`  | JWT was refreshed successfully    |
| `allegro:logout`        | *(none)*            | User was logged out               |

```js
window.addEventListener('allegro:authenticated', (event) => {
    if (event.detail.method === 'otp') {
        // User is now authenticated — update your UI
    }
});

```

## Using the allegro-login-form Component[​](#using-the-allegro-login-form-component "Direct link to Using the allegro-login-form Component")

The [`<allegro-login-form>`](/developer/components/login-form.md) web component handles the full one-time code flow automatically — no manual SDK calls needed:

```html
<allegro-login-form publisher-name="Example News"></allegro-login-form>

```

## Related[​](#related "Direct link to Related")

* [Magic Links guide](/developer/guides/authentication/magic-links.md) — passwordless authentication via an emailed link
* [Social Login guide](/developer/guides/authentication/social-login.md) — OAuth with Google, Apple, and Facebook
* [allegro-login-form component](/developer/components/login-form.md) — full UI for the auth flow
