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

# Authentication: JWT tokens and guest sessions

> Drssed uses JWT Bearer tokens for all requests. Learn how to get a token, refresh it, sign out, and upgrade a guest account to a full account.

Every request to the Drssed API must include a valid JWT Bearer token in the `Authorization` header. Tokens are issued either for guest sessions — which require no credentials and are useful for onboarding or exploration — or for full accounts identified by an email address or username and password. Both account types produce the same token format and behave identically in terms of making API requests.

## Guest sessions

The quickest way to get a token is to create a guest session. Call `POST /auth/guest` with no body and no credentials. The API responds with an `access_token`, its expiry in seconds, and a `refresh_token`.

Guest sessions are rate limited to **5 requests per hour** per IP address.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.drssed.com/auth/guest
  ```

  ```http HTTP theme={null}
  POST /auth/guest HTTP/1.1
  Host: api.drssed.com
  ```
</CodeGroup>

```json Response theme={null}
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHNhbXBsZSByZWZyZXNo"
}
```

<Note>
  Guest accounts have no expiry on the refresh token. However, each user can hold a maximum of 5 active refresh tokens at a time. When a sixth is issued, the oldest is automatically revoked.
</Note>

## Full accounts

To log in with a full account, call `POST /auth/login` with either `email` or `username` alongside `password`. You do not need to supply both `email` and `username` — either one is sufficient.

Full account login is rate limited to **5 requests per minute** per IP address.

<CodeGroup>
  ```bash curl (email) theme={null}
  curl -X POST https://api.drssed.com/auth/login \
    -H "Content-Type: application/json" \
    -d '{
      "email": "you@example.com",
      "password": "your-password"
    }'
  ```

  ```bash curl (username) theme={null}
  curl -X POST https://api.drssed.com/auth/login \
    -H "Content-Type: application/json" \
    -d '{
      "username": "yourhandle",
      "password": "your-password"
    }'
  ```

  ```http HTTP theme={null}
  POST /auth/login HTTP/1.1
  Host: api.drssed.com
  Content-Type: application/json

  {
    "email": "you@example.com",
    "password": "your-password"
  }
  ```
</CodeGroup>

```json Response theme={null}
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHNhbXBsZSByZWZyZXNo"
}
```

## Including the token

Include the access token in every authenticated request using the `Authorization` header with the `Bearer` scheme.

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.drssed.com/users/me/clothing \
    -H "Authorization: Bearer {access_token}"
  ```

  ```http HTTP theme={null}
  GET /users/me/clothing HTTP/1.1
  Host: api.drssed.com
  Authorization: Bearer {access_token}
  ```
</CodeGroup>

## Refreshing tokens

Access tokens expire after **1 hour** (`expires_in: 3600`). To get a new access token without requiring the user to log in again, call `POST /auth/refresh` with both the expired `access_token` and the valid `refresh_token`.

Token refresh is rate limited to **5 requests per minute** per IP address.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.drssed.com/auth/refresh \
    -H "Content-Type: application/json" \
    -d '{
      "access_token": "{expired_access_token}",
      "refresh_token": "{refresh_token}"
    }'
  ```

  ```http HTTP theme={null}
  POST /auth/refresh HTTP/1.1
  Host: api.drssed.com
  Content-Type: application/json

  {
    "access_token": "{expired_access_token}",
    "refresh_token": "{refresh_token}"
  }
  ```
</CodeGroup>

```json Response theme={null}
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": 3600,
  "refresh_token": "bmV3UmVmcmVzaFRva2VuSGVyZQ"
}
```

<Warning>
  The API issues a new `refresh_token` on every refresh call. Replace your stored refresh token with the one returned in the response — the previous one is invalidated.
</Warning>

## Signing out

To invalidate a session, call `POST /auth/signout` with the `refresh_token` in the request body. You must include a valid `Authorization` header. A successful response returns `204 No Content`.

Sign-out is rate limited to **2 requests per minute** per IP address.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.drssed.com/auth/signout \
    -H "Authorization: Bearer {access_token}" \
    -H "Content-Type: application/json" \
    -d '{
      "refresh_token": "{refresh_token}"
    }'
  ```

  ```http HTTP theme={null}
  POST /auth/signout HTTP/1.1
  Host: api.drssed.com
  Authorization: Bearer {access_token}
  Content-Type: application/json

  {
    "refresh_token": "{refresh_token}"
  }
  ```
</CodeGroup>

## Upgrading a guest account

If a user starts as a guest and you want to give them a persistent, credential-based account, call `POST /auth/upgrade` while authenticated as that guest. Provide `email`, `username`, and `password`. The `profile_picture` field is optional.

Upgrading preserves the user's existing wardrobe data — clothing items and outfits created during the guest session remain associated with the account after the upgrade.

Upgrade is rate limited to **5 requests per minute** per IP address.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.drssed.com/auth/upgrade \
    -H "Authorization: Bearer {guest_access_token}" \
    -H "Content-Type: application/json" \
    -d '{
      "email": "you@example.com",
      "username": "yourhandle",
      "password": "your-password",
      "profile_picture": "https://example.com/avatar.png"
    }'
  ```

  ```http HTTP theme={null}
  POST /auth/upgrade HTTP/1.1
  Host: api.drssed.com
  Authorization: Bearer {guest_access_token}
  Content-Type: application/json

  {
    "email": "you@example.com",
    "username": "yourhandle",
    "password": "your-password",
    "profile_picture": "https://example.com/avatar.png"
  }
  ```
</CodeGroup>

```json Response (201 Created) theme={null}
{
  "user_id": "usr_def456",
  "is_guest": false,
  "username": "yourhandle",
  "email": "you@example.com",
  "created_at": "2026-04-30T12:00:00+00:00",
  "updated_at": "2026-04-30T12:01:00+00:00"
}
```

## Error responses

| Status                  | Meaning                                  | Common cause                                                                   |
| ----------------------- | ---------------------------------------- | ------------------------------------------------------------------------------ |
| `401 Unauthorized`      | Missing or invalid access token          | No `Authorization` header, malformed token, or expired token not yet refreshed |
| `403 Forbidden`         | Valid token but insufficient permissions | Attempting to access another user's private resources                          |
| `429 Too Many Requests` | Rate limit exceeded                      | Too many requests to an auth endpoint within the allowed window                |

<Note>
  Rate limits apply per IP address. The limits for each endpoint are: `POST /auth/guest` — 5/hour; `POST /auth/login` — 5/min; `POST /auth/refresh` — 5/min; `POST /auth/signout` — 2/min; `POST /auth/upgrade` — 5/min.
</Note>
