> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.etera.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.etera.dev/_mcp/server.

# Authentication

## Overview

ETERA uses **JWT Bearer tokens** for API authentication. Tokens are obtained through one of several authentication methods, then passed in the `Authorization` header on every request.

```bash
Authorization: Bearer YOUR_JWT_TOKEN
```

## Choose Your Auth Method

#### Phone OTP

Best for **mobile apps**. 3-step flow using SMS verification.

#### Send OTP

**`cURL`**

```bash title="cURL"
curl -X POST "https://api.etera.dev/auth/api/auth/phone-number/send-otp" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "589595029",
    "countryCode": "+971"
  }'
```

**`TypeScript`**

```typescript title="TypeScript"
const res = await fetch(
  "https://api.etera.dev/auth/api/auth/phone-number/send-otp",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      phoneNumber: "589595029",
      countryCode: "+971",
    }),
  }
);
```

#### Verify OTP

**`cURL`**

```bash title="cURL"
curl -X POST "https://api.etera.dev/auth/api/auth/phone-number/verify" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "589595029",
    "countryCode": "+971",
    "code": "123456"
  }'
```

**`TypeScript`**

```typescript title="TypeScript"
const res = await fetch(
  "https://api.etera.dev/auth/api/auth/phone-number/verify",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      phoneNumber: "589595029",
      countryCode: "+971",
      code: "123456",
    }),
  }
);

const { token, user } = await res.json();
```

#### Update Profile (new users)

```bash
curl -X PATCH "https://api.etera.dev/auth/api/auth/me" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "firstName": "John", "lastName": "Doe", "language": "en" }'
```

> **Info**
>
> Phone numbers use the number without country code in `phoneNumber` and the calling code with `+` prefix in `countryCode`. Example: UAE `+971 58 959 5029` becomes `phoneNumber: "589595029"`, `countryCode: "+971"`.

#### Email OTP

Best for **web apps**. Same 3-step flow using email verification.

#### Send OTP

```bash
curl -X POST "https://api.etera.dev/auth/api/auth/email/send-otp" \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@example.com" }'
```

#### Verify OTP

```bash
curl -X POST "https://api.etera.dev/auth/api/auth/email/verify-otp" \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@example.com", "code": "123456" }'
```

#### Update Profile (new users)

```bash
curl -X PATCH "https://api.etera.dev/auth/api/auth/me" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "firstName": "John", "lastName": "Doe", "language": "en" }'
```

#### Google OAuth

**Mobile:** Pass the Google ID token directly.

```typescript
const res = await fetch(
  "https://api.etera.dev/auth/api/auth/mobile/google",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ id_token: "GOOGLE_ID_TOKEN" }),
  }
);
const { token, user } = await res.json();
```

**Web:** Use the redirect flow.

#### Redirect to Google

Send the user to `GET /auth/api/auth/sign-in/social/google`

#### Handle callback

Google redirects back to `GET /auth/api/auth/callback/google` with the JWT token

#### Apple OAuth

Best for **iOS apps**. Pass the Apple identity token directly.

```typescript
const res = await fetch(
  "https://api.etera.dev/auth/api/auth/mobile/apple",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ id_token: "APPLE_IDENTITY_TOKEN" }),
  }
);
const { token, user } = await res.json();
```

---

## Using Your Token

Once authenticated, include the JWT in all API requests:

**`cURL`**

```bash title="cURL"
curl "https://api.etera.dev/restaurant/search/recommendations/" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
```

**`TypeScript`**

```typescript title="TypeScript"
const headers = {
  Authorization: `Bearer ${token}`,
  "Content-Type": "application/json",
};

const restaurants = await fetch(
  "https://api.etera.dev/restaurant/search/recommendations/",
  { headers }
).then((r) => r.json());
```

## Get Current User

Retrieve the authenticated user's profile:

```bash
curl "https://api.etera.dev/auth/api/auth/me" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

---

## Security Schemes

#### Bearer Auth (JWT)

Used by most client-facing endpoints. Obtained through any of the authentication flows above.

| Service      | Uses Bearer Auth |
| ------------ | ---------------- |
| Auth         |                  |
| Account      |                  |
| Context      |                  |
| Notification |                  |

#### Admin Secret (API Key)

Used for admin-only operations. Passed as a header value.

| Service | Uses Admin Secret |
| ------- | ----------------- |
| Context |                   |
| AI      |                   |

---

## Rate Limits

| Scope                     | Limit                               |
| ------------------------- | ----------------------------------- |
|  Phone/Email OTP          | 5 requests/hour per number or email |
|  Authentication endpoints | 20 requests/minute per IP           |
|  Profile updates          | 10 requests/minute per user         |
|  General API              | 100 requests/minute per IP          |

> **Warning**
>
> JWT tokens are stateless — store them securely on the client. Never expose tokens in URLs, localStorage on shared devices, or version control.

## Next Steps

#### [Environments](/guides/environments)

Switch between dev, staging, and production

#### [Error Handling](/guides/error-handling)

Handle authentication errors gracefully

#### [Auth API Reference](/platform)

Full endpoint list for the Auth service