> For the complete documentation index, see [llms.txt](https://docs.tnid.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tnid.com/authentication/user-authentication.md).

# User Authentication

Request a one-time code by phone number or email, then exchange it at /auth/token for a user access token.

Learn how to get a TNID user access token to authenticate into the `/user` GraphQL API.

Users of TNID (as opposed to Companies) authenticate with a one-time code (OTP) delivered to their telephone number or email address.

{% hint style="warning" %}
Access tokens are valid for 7 days and refresh tokens for 30 days (see [Refresh Token](/authentication/refresh-token.md)). There are no passwords to rotate; if a token is compromised, stop using it and request a new OTP.
{% endhint %}

### Generate Your Access Token

The OTP endpoint is available in each environment:

| Environment           | OTP endpoint                                           |
| --------------------- | ------------------------------------------------------ |
| Staging               | `https://api.staging.v2.tnid.com/auth/create-user-otp` |
| Zero (pre-production) | `https://api.zero.v2.tnid.com/auth/create-user-otp`    |

The path is also accepted spelled `/auth/create_user_otp`.

**Step 1.** Make a request to `/auth/create-user-otp` with the `telephone_number` or `email` of an existing user. To create a new user, include the optional profile fields:

```json
{
   "telephone_number": "13024343433",
   "first_name": "John",
   "last_name": "Doe",
   "email": "email@address.com"
}
```

The response tells you when the code expires and how many attempts remain:

```json
{
  "expires_at": "2026-01-15T09:00:01.000000Z",
  "next_otp_at": "2026-01-15T09:00:00",
  "otp_status": "OTP sent",
  "remaining_otp_attempts": 5
}
```

**Step 2.** Make a request to `/auth/token` with the same `telephone_number` (or `email`) and the received `otp_code`:

```json
{
   "telephone_number": "13024343433",
   "otp_code": "698125"
}
```

The response contains an `access_token` and a `refresh_token`. Use the `access_token` as `Authorization: Bearer <access_token>` to interact with the GraphQL API at `/user`.

## Example Code

{% tabs %}
{% tab title="Python" %}

```python
import requests

BASE = "https://api.staging.v2.tnid.com"
HEADERS = {"Content-Type": "application/x-www-form-urlencoded"}

# Step one: request an OTP for an existing user. The user receives the code by SMS (or email if you pass "email").
def request_otp(user_phone_number):
    response = requests.post(f"{BASE}/auth/create-user-otp", headers=HEADERS,
                             data={"telephone_number": user_phone_number})
    if response.status_code != 200:
        raise Exception(f"Failed to request OTP: {response.status_code} {response.text}")
    print(f"OTP requested: {response.text}")

# Step two: exchange the OTP for tokens.
def get_bearer_token(user_phone_number, otp):
    response = requests.post(f"{BASE}/auth/token", headers=HEADERS,
                             data={"telephone_number": user_phone_number, "otp_code": otp})
    if response.status_code != 200:
        raise Exception(f"Failed to retrieve token: {response.status_code} {response.text}")
    return response.json().get("access_token")

# Creating a new user: same endpoint, with profile fields. Then request the OTP and token as above.
def create_new_user(phone_number, first_name, last_name, email):
    response = requests.post(f"{BASE}/auth/create-user-otp", headers=HEADERS,
                             data={"telephone_number": phone_number, "first_name": first_name,
                                   "last_name": last_name, "email": email})
    if response.status_code != 200:
        raise Exception(f"Failed to create new user: {response.status_code} {response.text}")
    print(f"User created: {response.text}")

phone = "14075554530"
request_otp(phone)
otp = input("Enter the OTP you received: ")
print("Bearer Token:", get_bearer_token(phone, otp))
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -X POST https://api.staging.v2.tnid.com/auth/create-user-otp \
  -H "Content-Type: application/json" \
  -d '{"telephone_number":"14075554530"}'

curl -X POST https://api.staging.v2.tnid.com/auth/token \
  -H "Content-Type: application/json" \
  -d '{"telephone_number":"14075554530","otp_code":"698125"}'
```

{% endtab %}
{% endtabs %}
