Skip to content

Authentication API

import { Badge } from ‘@astrojs/starlight/components’;

Authenticate a user and receive a JWT.

Auth: None

Request:

{
"email": "doctor@clinicflow.lat",
"password": "secure-password-here"
}

Response (200):

{
"token": "eyJhbGciOiJFUzI1NiIs...",
"user": {
"id": "uuid",
"clinicId": "uuid",
"email": "doctor@clinicflow.lat",
"fullName": "Dr. García",
"role": "admin",
"subscriptionPlan": "pro",
"subscriptionExpiresAt": "2026-07-01T00:00:00Z"
}
}

Errors:

Status Code Meaning
400 VALIDATION_ERROR Email or password missing
401 INVALID_CREDENTIALS Wrong email or password
403 USER_INACTIVE User account deactivated
403 CLINIC_INACTIVE Clinic account deactivated

Notes:

  • Password is compared with bcrypt (cost factor 12)
  • JWT payload includes userId, clinicId, email, role, subscriptionPlan, subscriptionExpiresAt
  • Default expiry: 8 hours
{
"sub": "user-uuid",
"clinic_id": "clinic-uuid",
"email": "doctor@clinicflow.lat",
"app_role": "admin",
"role": "authenticated",
"plan": "pro",
"exp": 1719700000,
"iat": 1719671200
}
  • Algorithm: ES256 (P-256 asymmetric)
  • Expiry: 8 hours from issuance
  • Live validation: RLS policies check users.is_active and clinics.is_active on every query

Self-service clinic registration. Requires a valid invite code.

Auth: None

Request:

{
"clinicName": "Clínica Dental San Miguel",
"adminName": "Dr. García",
"email": "doctor@example.com",
"password": "secure-password-1234",
"inviteCode": "ABC123DEF456"
}

Response (201):

{
"token": "eyJhbGciOiJFUzI1NiIs...",
"clinic": { "id": "uuid", "name": "Clínica Dental San Miguel", "plan": "trial" },
"user": { "id": "uuid", "email": "doctor@example.com", "role": "admin" }
}

Errors:

Status Message
400 Validation error (missing fields, invalid email, etc.)
400 “Invite code is invalid, expired, or already used”
409 “Email already registered”

Notes:

  • Invite codes are atomically claimed to prevent double-registration race conditions
  • Admin password is hashed with bcrypt (cost factor 12)
  • Clinic slug: {name-lowercase-dashes}-{6-char-uuid-suffix}

Get the current authenticated user’s profile.

Auth: Required

Response (200):

{
"id": "uuid",
"email": "admin@clinica.com",
"full_name": "Admin Name",
"role": "admin",
"clinic_id": "uuid",
"clinic": {
"name": "Clínica San Salvador",
"slug": "clinica-san-salvador-abc123",
"subscription_plan": "pro",
"subscription_expires_at": "2025-12-31T00:00:00Z",
"is_active": true
}
}

Errors:

Status Message
401 “Authentication token required”
401 “Token expired”
401 “Invalid token”
402 “Subscription expired” (from requireActiveSubscription)