Skip to main content

Authentication

DocSnap uses JWT (JSON Web Tokens) for authentication. Access tokens are short-lived (1 hour) and refresh tokens are long-lived (30 days).

Token Architecture

TokenLifetimePurpose
Access Token1 hourAuthenticates API requests via Authorization: Bearer header
Refresh Token30 daysObtains new access tokens without re-entering credentials

Both tokens are HS256 JWTs. The access token payload:

{
"sub": "user-uuid",
"type": "access",
"iat": 1771709471,
"exp": 1771713071
}

POST /v1/auth/register

Create a new user account. A verification email is sent asynchronously.

Request:

{
"email": "driver@acmefreight.com",
"password": "securepass123"
}

Response (201):

{
"user": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "driver@acmefreight.com",
"email_verified": false
},
"message": "Account created. Please check your email to verify your address."
}
note

No tokens are returned at registration. The user must verify their email first.


POST /v1/auth/login

Authenticate with email and password. Optionally includes device trust verification.

Request:

{
"email": "driver@acmefreight.com",
"password": "securepass123",
"device_id": "20C85E1B-3B11-43E3-821C-8C2CC27998BD",
"device_name": "iPhone"
}

device_id and device_name are optional. When provided:

  • First device after email verification is auto-trusted
  • Subsequent new devices require email confirmation (returns 403)
  • Already-trusted devices proceed normally

Response (200):

{
"user": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "driver@acmefreight.com",
"email_verified": true
},
"access_token": "eyJ...",
"refresh_token": "eyJ..."
}

Error Responses:

StatusCodeCondition
401INVALID_CREDENTIALSWrong email or password
403EMAIL_NOT_VERIFIEDEmail not yet verified
403DEVICE_NOT_CONFIRMEDNew device needs email confirmation

POST /v1/auth/refresh

Exchange a refresh token for a new access token.

Request:

{
"refresh_token": "eyJ..."
}

Response (200):

{
"access_token": "eyJ..."
}

POST /v1/auth/verify-email

Verify email address using the token from the verification email.

Request:

{
"token": "raw-token-from-email-link"
}

Response (200):

{
"user": { "id": "uuid", "email": "driver@acmefreight.com", "email_verified": true },
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"message": "Email verified successfully."
}

POST /v1/auth/verification-status

Poll whether an email address has been verified. Used by clients while showing a "check your email" screen.

Request:

{
"email": "driver@acmefreight.com"
}

Response (200) -- verified:

{
"verified": true,
"user": { "id": "uuid", "email": "driver@acmefreight.com", "email_verified": true },
"access_token": "eyJ...",
"refresh_token": "eyJ..."
}

Response (200) -- not yet verified:

{
"verified": false
}

Always returns verified: false for non-existent emails (prevents enumeration).


POST /v1/auth/resend-verification

Resend the verification email. Rate limited: 1/min, 5/hour.

Request:

{
"email": "driver@acmefreight.com"
}

Response (200):

{
"message": "If an account exists with this email, a verification link has been sent."
}

POST /v1/auth/confirm-device

Confirm a new device using the token from the confirmation email.

Request:

{
"token": "raw-token-from-email-link"
}

Response (200):

{
"user": { "id": "uuid", "email": "driver@acmefreight.com", "email_verified": true },
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"message": "Device confirmed successfully."
}

POST /v1/auth/device-status

Poll whether a device has been confirmed.

Request:

{
"email": "driver@acmefreight.com",
"device_id": "20C85E1B-3B11-43E3-821C-8C2CC27998BD"
}

Response (200) -- confirmed:

{
"confirmed": true,
"user": { "id": "uuid", "email": "driver@acmefreight.com", "email_verified": true },
"access_token": "eyJ...",
"refresh_token": "eyJ..."
}

Response (200) -- not yet confirmed:

{
"confirmed": false
}

POST /v1/auth/forgot-password

Send a password reset email. Rate limited: 1/min, 5/hour.

Request:

{
"email": "driver@acmefreight.com"
}

Response (200):

{
"message": "If an account exists with this email, a password reset link has been sent."
}

POST /v1/auth/reset-password

Reset password using the token from the reset email.

Request:

{
"token": "raw-token-from-email-link",
"password": "newSecurePass456"
}

Response (200):

{
"message": "Password has been reset successfully. You can now log in."
}

GET /v1/me

Get the authenticated user's profile.

Headers: Authorization: Bearer <access_token>

Response (200):

{
"user": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "driver@acmefreight.com"
}
}