Skip to content

Authentication

All protected routes expect:

Authorization: Bearer <accessToken>
Content-Type: application/json

Tokens are issued by POST /auth/register and POST /auth/login. The JWT module uses expiresIn: '7d' for normal access tokens unless noted otherwise.

Token storage

Store accessToken securely (secret manager, encrypted session, mobile secure storage). The API does not implement refresh tokens in the snippets below; re-login when expired.

Error envelope

Most errors from Nest HttpException paths use:

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR | UNAUTHORIZED | FORBIDDEN | NOT_FOUND | CONFLICT | INTERNAL_ERROR",
    "message": "Human-readable message",
    "details": []
  }
}

code maps from HTTP status (see HttpExceptionFilter in apps/api). details may list validation messages when message was an array.

Login failure is special: invalid credentials return HTTP 200 with:

{
  "success": false,
  "error": { "code": "UNAUTHORIZED", "message": "Invalid credentials" }
}

Check both status and success when handling login responses.


Register

POST /auth/register — Public. Body: email, password, optional name, optional referralCode.

Replace https://api.azscraper.com with your own deployment's base URL if self-hosting.

curl -sS -X POST "https://api.azscraper.com/auth/register" \
  -H "Content-Type: application/json" \
  -d '{"email":"dev@example.com","password":"SecurePassw0rd!","name":"Dev User"}'
const res = await fetch("https://api.azscraper.com/auth/register", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    email: "dev@example.com",
    password: "SecurePassw0rd!",
    name: "Dev User",
  }),
});
const body = await res.json();
if (!res.ok) console.error(body);
else console.log(body.accessToken, body.user);
import httpx

r = httpx.post(
    "https://api.azscraper.com/auth/register",
    json={
        "email": "dev@example.com",
        "password": "SecurePassw0rd!",
        "name": "Dev User",
    },
)
r.raise_for_status()
print(r.json())

Success (example)

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "user": { "id": "67c...f1", "email": "dev@example.com", "name": "Dev User" }
}

Error (duplicate email, HTTP 409)

{
  "success": false,
  "error": {
    "code": "CONFLICT",
    "message": "An account with this email already exists",
    "details": []
  }
}

Login

POST /auth/login — Public.

curl -sS -X POST "https://api.azscraper.com/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email":"dev@example.com","password":"SecurePassw0rd!"}'
const res = await fetch("https://api.azscraper.com/auth/login", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    email: "dev@example.com",
    password: "SecurePassw0rd!",
  }),
});
const body = await res.json();
if (body.success === false) {
  console.error(body.error);
} else {
  console.log(body.accessToken);
}
import httpx

r = httpx.post(
    "https://api.azscraper.com/auth/login",
    json={"email": "dev@example.com", "password": "SecurePassw0rd!"},
)
data = r.json()
if data.get("success") is False:
    print("login failed", data["error"])
else:
    print(data["accessToken"])

Success

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "user": { "id": "67c...f1", "email": "dev@example.com", "name": "Dev User" }
}

Invalid credentials (HTTP 200, check success)

{
  "success": false,
  "error": { "code": "UNAUTHORIZED", "message": "Invalid credentials" }
}

Logout

POST /auth/logout — Returns 204 No Content. No JWT guard on the route (logout is a client-side convention); discard accessToken locally.

curl -sS -X POST "https://api.azscraper.com/auth/logout" \
  -o /dev/null -w "%{http_code}\n"

Handoff (optional)

Used for short-lived exchange flows (e.g. cross-app handoff).

Endpoint Purpose
POST /auth/login-handoff Body: email, password. Returns handoffToken (JWT expiresIn: '5m') plus user.
POST /auth/verify-handoff Body: { "handoffToken": "..." }. Returns a normal accessToken + user.

Invalid handoff yields 401 with the standard error envelope.