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

# Get a bearer token

> Exchange a customer email and password for a bearer token used to authenticate all subsequent API requests.

# Get a bearer token

Exchanges a customer's email address and password for a short-lived bearer token and a refresh token. Every authenticated API call in the Members Portal uses the `access_token` returned here as a `Bearer` credential. Pass `totp` when the customer has two-factor authentication enabled — omitting it when 2FA is active will return a `two_factor_auth_check` error.

<Note>
  Unlike most Nexudus API endpoints, this request must be encoded as `application/x-www-form-urlencoded`, **not** `application/json`. Sending a JSON
  body will result in an `unsupported_grant_type` error.
</Note>

## Authentication

No authentication required. This is the endpoint that issues credentials.

## Request Body

<ParamField body="grant_type" type="string" required>
  Grant flow to use. Must be `password` for email/password authentication.
</ParamField>

<ParamField body="username" type="string" required>
  The customer's email address.
</ParamField>

<ParamField body="password" type="string" required>
  The customer's password.
</ParamField>

<ParamField body="totp" type="string">
  Time-based One-Time Password for two-factor authentication. Required when the customer has 2FA enabled; omit otherwise.
</ParamField>

## Response

<ResponseField name="access_token" type="string">
  Bearer token to include in the `Authorization` header of all subsequent authenticated requests.
</ResponseField>

<ResponseField name="token_type" type="string">
  Token scheme. Always `bearer`.
</ResponseField>

<ResponseField name="expires_in" type="number">
  Lifetime of the access token in seconds.
</ResponseField>

<ResponseField name="refresh_token" type="string">
  Token used to obtain a new `access_token` after it expires, without requiring the customer to re-enter their password.
</ResponseField>

## Examples

### Successful sign-in

```http theme={null}
POST /api/token
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=jane.doe%40example.com&password=S3cur3P%40ss
```

```json theme={null}
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 86400,
  "refresh_token": "8xLOxBtZp8"
}
```

### Sign-in with two-factor authentication

```http theme={null}
POST /api/token
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=jane.doe%40example.com&password=S3cur3P%40ss&totp=482910
```

```json theme={null}
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 86400,
  "refresh_token": "8xLOxBtZp8"
}
```

## TypeScript Integration

```typescript theme={null}
import { type AxiosResponse } from 'axios'
import qs from 'qs'
import { AuthToken } from '@/states/useAuthContext'

const data = {
  grant_type: 'password',
  username: values.email,
  password: values.password,
  totp: values.totp,
}

const res: AxiosResponse<AuthToken> = await httpClient.post('/api/token', qs.stringify(data), {
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
})

if (res.data.access_token) {
  saveSession({ tokenResponse: res.data, remember: values.rememberMe })
}
```

## Usage in Portal

| Context                  | Source file                          |
| ------------------------ | ------------------------------------ |
| Sign-in page (`/signin`) | `src/views/auth/SignIn/useSignIn.ts` |

## Error Responses

<ResponseField name="400 Bad Request — unsupported_grant_type" type="error">
  The `grant_type` field is missing or the body was not encoded as `application/x-www-form-urlencoded`.
</ResponseField>

<ResponseField name="400 Bad Request — invalid_grant" type="error">
  Credentials are incorrect, the customer is not registered with this location, or the account has been suspended. The `error_description` field
  contains a human-readable reason.
</ResponseField>

<ResponseField name="400 Bad Request — two_factor_auth_check" type="error">
  The customer has 2FA enabled but `totp` was not supplied or the supplied code is invalid. Prompt the customer for their one-time code and retry.
</ResponseField>

<ResponseField name="400 Bad Request — must_reset_password" type="error">
  The customer is required to reset their password before signing in. The `error_description` field contains a password-reset token to pass to the
  reset-password flow.
</ResponseField>

## Related Endpoints

| Method | Endpoint                       | Description                                               |
| ------ | ------------------------------ | --------------------------------------------------------- |
| `POST` | `/api/token`                   | *(this endpoint)* Exchange credentials for a bearer token |
| `GET`  | `/api/public/billing/customer` | Retrieve the authenticated customer's profile             |
| `GET`  | `/api/public/teams/my`         | List teams the authenticated customer belongs to          |
