Parent: Aligner Tracker Api


System Diagram


flowchart TB

subgraph client [Mobile App]

MobileApp[React Native App]

end

  

subgraph aws [AWS Cloud]

APIGateway[API Gateway]

Cognito[Cognito User Pool]

S3[S3 Bucket]

RDS[(RDS PostgreSQL)]

end

  

subgraph backend [Backend Service]

FastAPI[FastAPI Application]

end

  

MobileApp -->|JWT Token| APIGateway

APIGateway --> Cognito

APIGateway --> FastAPI

FastAPI --> RDS

FastAPI --> S3

Authentication Flow


sequenceDiagram

participant App as Mobile App

participant GW as API Gateway

participant Cognito as Cognito

participant API as FastAPI

  

App->>GW: Request + JWT Bearer Token

GW->>Cognito: Validate JWT

Cognito-->>GW: Token Valid

GW->>API: Forward Request

API->>API: Extract sub from JWT

API->>API: Verify sub matches path param

API-->>App: Response

Endpoint Types

PrefixAuth MethodUse Case
/auth/*Cognito JWTUser-facing endpoints (mobile app)
/internal/*API Gateway API KeyService-to-service (e.g., from Jarvis)
/public/*HubSpot Signature v3HubSpot webhooks

Key Design Decisions

  • API Gateway validates JWTs - The FastAPI app does NOT verify JWT signatures; it trusts API Gateway has already validated them. It only extracts the sub claim.

  • Path-based authorization - Endpoints like /auth/users/{cognito_sub}/... require the JWT’s sub to match the path parameter.

  • Alembic for migrations - Database schema is managed via Alembic, auto-run on container startup.

Environment Variables

# AWS
AWS_REGION=eu-west-2
COGNITO_USER_POOL_ID=eu-west-2_XXXXXX
USER_IMAGES_BUCKET=your-s3-bucket-name
 
# DB
DATABASE_URL=xxx
 
# Jarvis
JARVIS_API_URL=xxx
JARVIS_API_KEY=xxx
 
# OneSignal
ONESIGNAL_APP_ID=xxx
ONESIGNAL_REST_API_KEY=xxx
 
# HubSpot
HUBSPOT_CLIENT_SECRET=xxx
 
# Logging configuration
LOG_LEVEL=INFO
LOG_FORMAT=json
LOG_REDACT_SENSITIVE=true

External Service Integration

Jarvis API

Used to submit dentist change requests:

POST {JARVIS_API_URL}/api/smilewhite_app/customer/change-dentist/{email}/
Headers: Authorization: Api-Key {JARVIS_API_KEY}
Request: {"assigned_dentist": <dentist_id>}

Responses:

  • 200: Success, returns {"ticket": {"id": "..."}}
  • 409: Dentist change already pending

OneSignal

Used to send scheduled push notifications for progress change reminders:

Send Scheduled Notification:

POST https://api.onesignal.com/notifications
Headers: Authorization: Key {ONESIGNAL_REST_API_KEY}
Request: {
  "app_id": "<app_id>",
  "include_aliases": {"external_id": ["<cognito_sub>"]},
  "target_channel": "push",
  "headings": {"en": "Progress Reminder"},
  "contents": {"en": "<message>"},
  "send_after": "<ISO8601 datetime>",
  "data": {"notification_id": "<id>", "type": "progress"}
}

Cancel Notification:

DELETE https://api.onesignal.com/notifications/{onesignal_id}?app_id={app_id}
Headers: Authorization: Key {ONESIGNAL_REST_API_KEY}

Users are targeted via external_id which maps to their cognito_sub_identifier.

CI/CD Pipeline


flowchart LR

Push[Push to main] --> Quality[Quality Job]

Quality --> |Tests Pass| Build[Build Docker Image]

Build --> ECR[Push to ECR]

ECR --> Deploy[Deploy to Dev]

Deploy --> Infra[Smile-White/Infrastructure]

  • Trigger: Push to main branch

  • Quality checks: Alembic migration check, pytest, health check

  • Deploy: Uses reusable workflow from Smile-White/Infrastructure repo