---
title: "Login"
method: POST
path: "/api/v1/auth/login"
tags: ["auth"]
---

# Login

`POST /api/v1/auth/login`

## Request

- Base URL: `https://api.badgeia.com`
- URL: `https://api.badgeia.com/api/v1/auth/login`
- Auth: none declared

Login with email and password.

Sets HttpOnly session + refresh cookies AND returns the access token
in the JSON body for non-cookie clients (Badge SDK, curl). See module
docstring for the cookie attribute set.

Lockout is enforced via Redis (PR-F #2 / HIGH #8, per account since
#1414). Failed attempts increment a counter keyed on the normalised email
(any address) and two keyed on `source_ip` (any account).
`AUTH_LOCKOUT_THRESHOLD` (5) on the account or `AUTH_LOCKOUT_IP_THRESHOLD`
(50) on the address within 15 minutes, or `AUTH_LOCKOUT_IP_HOURLY_THRESHOLD`
(150) on the address within an hour, returns 429, with the same body
every time. Successful login, or a completed password reset, clears the
account counter only.

Fail-closed: if Redis is unavailable, return 503 — never silently
allow login. Unlike the general rate limiter (which fails open by
design — it's a guard rail), this IS the auth correctness boundary.

## Request body

- LoginRequest
  - `email` string, email, required
  - `password` string, required

## Response `200`

Successful Response

- TokenResponse
  - `access_token` string, required
  - `token_type` string
  - `user` UserRead, required
    - `email` string, email, required
    - `name` string, nullable
    - `avatar_url` string, nullable
    - `id` string, uuid, required
    - `username` string, nullable
    - `auth_provider` string, required
    - `plan` string, required
    - `has_billing_account` boolean
    - `plan_interval` string, nullable
    - `email_verified` boolean
    - `trial_ends_at` string, date-time, nullable
    - `is_active` boolean, required
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `notify_run_complete` boolean, required
    - `notify_score_change` boolean, required
    - `notify_rank_change` boolean, required
    - `notify_weekly_digest` boolean, required
    - `notify_lifecycle_email` boolean
    - `notify_regression` boolean
    - `pmf_survey_last_shown` string, date-time, nullable
    - `marketplace_payout_email` string, nullable
    - `is_admin` boolean
    - `deletion_requested_at` string, date-time, nullable
    - `deletion_due_at` string, date-time, nullable
    - `arena_run_count` integer
    - `monthly_run_count` integer
    - `agent_count` integer
    - `team_count` integer

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/badgeia/apis/badge.md) · [All operations](https://skmtc.dev/badgeia/apis/badge/llms.txt) · [OpenAPI document](https://skmtc.dev/badgeia/apis/badge/revisions/7e7f7fee8b77?raw)
