Files
maternal-app/docs/maternal-app-api-spec.md
andupetcu 98e01ebe80 Phase 1 & 2: Authentication and Children Management
Completed Features:
- Full JWT authentication system with refresh tokens
- User registration and login with device fingerprinting
- Child profile CRUD operations with permission-based access
- Family management with roles and permissions
- Database migrations for core auth and family structure
- Comprehensive test coverage (37 unit + E2E tests)

Tech Stack:
- NestJS backend with TypeORM
- PostgreSQL database
- JWT authentication with Passport
- bcrypt password hashing
- Docker Compose for infrastructure

🤖 Generated with Claude Code
2025-09-30 18:40:10 +03:00

18 KiB

API Specification Document - Maternal Organization App

API Architecture Overview

Base Configuration

  • Base URL: https://api.{domain}/api/v1
  • GraphQL Endpoint: https://api.{domain}/graphql
  • WebSocket: wss://api.{domain}/ws
  • API Style: Hybrid (REST for CRUD, GraphQL for complex queries)
  • Versioning: URL path versioning (/api/v1/)
  • Rate Limiting: 100 requests/minute per user
  • Pagination: Cursor-based with consistent structure

Standard Headers

Content-Type: application/json
Accept: application/json
Accept-Language: en-US,en;q=0.9
X-Client-Version: 1.0.0
X-Device-ID: uuid-device-fingerprint
X-Timezone: America/New_York
Authorization: Bearer {access_token}
X-Refresh-Token: {refresh_token}

Device Fingerprinting

{
  "deviceId": "uuid",
  "platform": "ios|android",
  "model": "iPhone14,2",
  "osVersion": "16.5",
  "appVersion": "1.0.0",
  "pushToken": "fcm_or_apns_token"
}

Authentication Endpoints

POST /api/v1/auth/register

Create new user account with family setup.

Request Body:

{
  "email": "user@example.com",
  "password": "SecurePass123!",
  "phone": "+1234567890",
  "name": "Jane Doe",
  "locale": "en-US",
  "timezone": "America/New_York",
  "deviceInfo": {
    "deviceId": "uuid",
    "platform": "ios",
    "model": "iPhone14,2",
    "osVersion": "16.5"
  }
}

Response 201:

{
  "success": true,
  "data": {
    "user": {
      "id": "usr_2n4k8m9p",
      "email": "user@example.com",
      "name": "Jane Doe",
      "locale": "en-US",
      "emailVerified": false
    },
    "family": {
      "id": "fam_7h3j9k2m",
      "shareCode": "ABC123",
      "role": "parent"
    },
    "tokens": {
      "accessToken": "eyJhbGc...",
      "refreshToken": "eyJhbGc...",
      "expiresIn": 3600
    },
    "deviceRegistered": true
  }
}

POST /api/v1/auth/login

Authenticate existing user with device registration.

Request Body:

{
  "email": "user@example.com",
  "password": "SecurePass123!",
  "deviceInfo": {
    "deviceId": "uuid",
    "platform": "ios",
    "model": "iPhone14,2",
    "osVersion": "16.5"
  }
}

Response 200:

{
  "success": true,
  "data": {
    "user": {
      "id": "usr_2n4k8m9p",
      "email": "user@example.com",
      "families": ["fam_7h3j9k2m"]
    },
    "tokens": {
      "accessToken": "eyJhbGc...",
      "refreshToken": "eyJhbGc...",
      "expiresIn": 3600
    },
    "requiresMFA": false,
    "deviceTrusted": true
  }
}

POST /api/v1/auth/refresh

Refresh access token using refresh token.

Request Body:

{
  "refreshToken": "eyJhbGc...",
  "deviceId": "uuid"
}

Response 200:

{
  "success": true,
  "data": {
    "accessToken": "eyJhbGc...",
    "refreshToken": "eyJhbGc...",
    "expiresIn": 3600
  }
}

POST /api/v1/auth/logout

Logout and revoke tokens for specific device.

Request Body:

{
  "deviceId": "uuid",
  "allDevices": false
}

Response 200:

{
  "success": true,
  "message": "Successfully logged out"
}

Family Management Endpoints

POST /api/v1/families/invite

Generate invitation for family member.

Request Body:

{
  "email": "partner@example.com",
  "role": "parent|caregiver|viewer",
  "permissions": {
    "canAddChildren": true,
    "canEditChildren": true,
    "canLogActivities": true,
    "canViewReports": true
  },
  "message": "Join our family on the app!"
}

Response 201:

{
  "success": true,
  "data": {
    "invitationId": "inv_8k3m9n2p",
    "shareCode": "XYZ789",
    "expiresAt": "2024-01-15T00:00:00Z",
    "invitationUrl": "app://join/XYZ789"
  }
}

POST /api/v1/families/join

Join family using share code.

Request Body:

{
  "shareCode": "XYZ789"
}

Response 200:

{
  "success": true,
  "data": {
    "familyId": "fam_7h3j9k2m",
    "familyName": "The Doe Family",
    "role": "parent",
    "members": [
      {
        "id": "usr_2n4k8m9p",
        "name": "Jane Doe",
        "role": "parent"
      }
    ],
    "children": []
  }
}

GET /api/v1/families/{familyId}/members

Get all family members with their permissions.

Response 200:

{
  "success": true,
  "data": {
    "members": [
      {
        "id": "usr_2n4k8m9p",
        "name": "Jane Doe",
        "email": "jane@example.com",
        "role": "parent",
        "permissions": {
          "canAddChildren": true,
          "canEditChildren": true,
          "canLogActivities": true,
          "canViewReports": true
        },
        "joinedAt": "2024-01-01T00:00:00Z",
        "lastActive": "2024-01-10T15:30:00Z"
      }
    ]
  }
}

Children Management Endpoints

POST /api/v1/children

Add a new child to the family.

Request Body:

{
  "familyId": "fam_7h3j9k2m",
  "name": "Emma",
  "birthDate": "2023-06-15",
  "gender": "female",
  "bloodType": "O+",
  "allergies": ["peanuts", "dairy"],
  "medicalConditions": ["eczema"],
  "pediatrician": {
    "name": "Dr. Smith",
    "phone": "+1234567890"
  },
  "photo": "base64_encoded_image"
}

Response 201:

{
  "success": true,
  "data": {
    "id": "chd_9m2k4n8p",
    "familyId": "fam_7h3j9k2m",
    "name": "Emma",
    "birthDate": "2023-06-15",
    "ageInMonths": 7,
    "developmentalStage": "infant",
    "photoUrl": "https://storage.api/photos/chd_9m2k4n8p.jpg"
  }
}

GET /api/v1/children/{childId}

Get child details with calculated metrics.

Response 200:

{
  "success": true,
  "data": {
    "id": "chd_9m2k4n8p",
    "name": "Emma",
    "birthDate": "2023-06-15",
    "ageInMonths": 7,
    "developmentalStage": "infant",
    "currentWeight": {
      "value": 8.2,
      "unit": "kg",
      "percentile": 75,
      "recordedAt": "2024-01-10T10:00:00Z"
    },
    "currentHeight": {
      "value": 68,
      "unit": "cm",
      "percentile": 80,
      "recordedAt": "2024-01-10T10:00:00Z"
    },
    "todaySummary": {
      "feedings": 5,
      "sleepHours": 14.5,
      "diapers": 6,
      "lastFeedingAt": "2024-01-10T14:30:00Z",
      "lastSleepAt": "2024-01-10T13:00:00Z"
    }
  }
}

Activity Tracking Endpoints (REST)

POST /api/v1/activities/feeding

Log a feeding activity.

Request Body:

{
  "childId": "chd_9m2k4n8p",
  "type": "breast|bottle|solid",
  "startTime": "2024-01-10T14:30:00Z",
  "endTime": "2024-01-10T14:45:00Z",
  "details": {
    "breastSide": "left|right|both",
    "amount": 120,
    "unit": "ml|oz",
    "foodType": "formula|breastmilk|puree"
  },
  "notes": "Good feeding session",
  "mood": "happy|fussy|sleepy"
}

Response 201:

{
  "success": true,
  "data": {
    "id": "act_3k9n2m4p",
    "childId": "chd_9m2k4n8p",
    "type": "feeding",
    "timestamp": "2024-01-10T14:30:00Z",
    "duration": 15,
    "createdBy": "usr_2n4k8m9p",
    "syncedToFamily": true
  }
}

POST /api/v1/activities/sleep

Log sleep activity.

Request Body:

{
  "childId": "chd_9m2k4n8p",
  "startTime": "2024-01-10T13:00:00Z",
  "endTime": "2024-01-10T14:30:00Z",
  "type": "nap|night",
  "location": "crib|stroller|car|bed",
  "quality": "good|restless|interrupted",
  "notes": "Went down easily"
}

POST /api/v1/activities/diaper

Log diaper change.

Request Body:

{
  "childId": "chd_9m2k4n8p",
  "timestamp": "2024-01-10T15:00:00Z",
  "type": "wet|dirty|both",
  "consistency": "normal|loose|hard",
  "color": "normal|green|yellow",
  "hasRash": false,
  "notes": "Applied diaper cream"
}

GET /api/v1/activities

Get activities with cursor pagination.

Query Parameters:

  • childId: Filter by child (required)
  • type: Filter by activity type
  • startDate: ISO date string
  • endDate: ISO date string
  • cursor: Pagination cursor
  • limit: Items per page (default: 20, max: 100)

Response 200:

{
  "success": true,
  "data": {
    "activities": [
      {
        "id": "act_3k9n2m4p",
        "childId": "chd_9m2k4n8p",
        "type": "feeding",
        "timestamp": "2024-01-10T14:30:00Z",
        "details": {},
        "createdBy": "usr_2n4k8m9p"
      }
    ],
    "cursor": {
      "next": "eyJpZCI6ImFjdF8za...",
      "hasMore": true,
      "total": 150
    }
  }
}

AI Assistant Endpoints

POST /api/v1/ai/chat

Send message to AI assistant.

Request Body:

{
  "message": "Why won't my baby sleep?",
  "childId": "chd_9m2k4n8p",
  "conversationId": "conv_8n2k4m9p",
  "context": {
    "includeRecentActivities": true,
    "includeSleepPatterns": true,
    "includeDevelopmentalInfo": true
  },
  "locale": "en-US"
}

Response 200:

{
  "success": true,
  "data": {
    "conversationId": "conv_8n2k4m9p",
    "messageId": "msg_7k3n9m2p",
    "response": "Based on Emma's recent sleep patterns...",
    "suggestions": [
      "Try starting bedtime routine 15 minutes earlier",
      "Check room temperature (ideal: 68-72°F)"
    ],
    "relatedArticles": [
      {
        "title": "7-Month Sleep Regression",
        "url": "/resources/sleep-regression-7-months"
      }
    ],
    "confidenceScore": 0.92
  }
}

POST /api/v1/ai/voice

Process voice command.

Request Body (multipart/form-data):

audio: [audio file - wav/mp3]
childId: chd_9m2k4n8p
locale: en-US

Response 200:

{
  "success": true,
  "data": {
    "transcription": "Baby fed 4 ounces at 3pm",
    "interpretation": {
      "action": "log_feeding",
      "childId": "chd_9m2k4n8p",
      "parameters": {
        "amount": 4,
        "unit": "oz",
        "time": "15:00"
      }
    },
    "executed": true,
    "activityId": "act_9k2m4n3p"
  }
}

Analytics & Insights Endpoints

GET /api/v1/insights/{childId}/patterns

Get AI-detected patterns for a child.

Response 200:

{
  "success": true,
  "data": {
    "sleepPatterns": {
      "averageNightSleep": 10.5,
      "averageDaySleep": 3.5,
      "optimalBedtime": "19:30",
      "wakeWindows": [90, 120, 150, 180],
      "trend": "improving",
      "insights": [
        "Emma sleeps 45 minutes longer when put down before 7:30 PM",
        "Morning wake time is consistently between 6:30-7:00 AM"
      ]
    },
    "feedingPatterns": {
      "averageIntake": 28,
      "feedingIntervals": [2.5, 3, 3, 4],
      "preferredSide": "left",
      "insights": [
        "Feeding intervals increasing - may be ready for longer stretches"
      ]
    }
  }
}

GET /api/v1/insights/{childId}/predictions

Get predictive suggestions.

Response 200:

{
  "success": true,
  "data": {
    "nextNapTime": {
      "predicted": "2024-01-10T16:30:00Z",
      "confidence": 0.85,
      "wakeWindow": 120
    },
    "nextFeedingTime": {
      "predicted": "2024-01-10T17:30:00Z",
      "confidence": 0.78
    },
    "growthSpurt": {
      "likelihood": 0.72,
      "expectedIn": "5-7 days",
      "symptoms": ["increased feeding", "fussiness", "disrupted sleep"]
    }
  }
}

GraphQL Queries for Complex Data

GraphQL Endpoint

POST https://api.{domain}/graphql

Family Dashboard Query

query FamilyDashboard($familyId: ID!, $date: Date!) {
  family(id: $familyId) {
    id
    name
    children {
      id
      name
      age
      todaySummary(date: $date) {
        feedings {
          count
          totalAmount
          lastAt
        }
        sleep {
          totalHours
          naps
          nightSleep
          currentStatus
        }
        diapers {
          count
          lastAt
        }
      }
      upcomingEvents {
        type
        scheduledFor
        description
      }
      aiInsights {
        message
        priority
        actionable
      }
    }
    members {
      id
      name
      lastActive
      recentActivities(limit: 5) {
        type
        childName
        timestamp
      }
    }
  }
}

Weekly Report Query

query WeeklyReport($childId: ID!, $startDate: Date!, $endDate: Date!) {
  child(id: $childId) {
    weeklyReport(startDate: $startDate, endDate: $endDate) {
      sleep {
        dailyAverages {
          date
          nightHours
          napHours
          totalHours
        }
        patterns {
          consistentBedtime
          averageWakeTime
          longestStretch
        }
      }
      feeding {
        dailyTotals {
          date
          numberOfFeedings
          totalVolume
        }
        trends {
          direction
          averageInterval
        }
      }
      growth {
        measurements {
          date
          weight
          height
          percentiles
        }
      }
      milestones {
        achieved {
          name
          achievedAt
        }
        upcoming {
          name
          expectedBy
        }
      }
    }
  }
}

WebSocket Events

Connection

// Client connects with auth
ws.connect('wss://api.{domain}/ws', {
  headers: {
    'Authorization': 'Bearer {access_token}',
    'X-Device-ID': 'uuid'
  }
});

Family Room Events

// Join family room
ws.emit('join-family', { familyId: 'fam_7h3j9k2m' });

// Activity logged by family member
ws.on('activity-logged', {
  activityId: 'act_3k9n2m4p',
  childId: 'chd_9m2k4n8p',
  type: 'feeding',
  loggedBy: 'usr_2n4k8m9p',
  timestamp: '2024-01-10T14:30:00Z'
});

// Real-time timer sync
ws.on('timer-started', {
  timerId: 'tmr_8k3m9n2p',
  type: 'feeding',
  childId: 'chd_9m2k4n8p',
  startedBy: 'usr_2n4k8m9p',
  startTime: '2024-01-10T14:30:00Z'
});

// AI insight generated
ws.on('insight-available', {
  childId: 'chd_9m2k4n8p',
  type: 'pattern_detected',
  message: 'New sleep pattern identified',
  priority: 'medium'
});

Error Responses

Standard Error Format

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input data",
    "details": {
      "field": "email",
      "reason": "Invalid email format"
    },
    "timestamp": "2024-01-10T15:30:00Z",
    "traceId": "trace_8k3m9n2p"
  }
}

Error Codes

Code HTTP Status Description
UNAUTHORIZED 401 Invalid or expired token
FORBIDDEN 403 Insufficient permissions
NOT_FOUND 404 Resource not found
VALIDATION_ERROR 400 Invalid input data
RATE_LIMITED 429 Too many requests
FAMILY_FULL 400 Family member limit reached
CHILD_LIMIT 400 Free tier child limit reached
AI_QUOTA_EXCEEDED 429 AI query limit reached
SYNC_CONFLICT 409 Data sync conflict
SERVER_ERROR 500 Internal server error

Rate Limit Headers

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1704903000
Retry-After: 3600

Localization

Supported Locales

  • en-US - English (United States)
  • es-ES - Spanish (Spain)
  • es-MX - Spanish (Mexico)
  • fr-FR - French (France)
  • fr-CA - French (Canada)
  • pt-BR - Portuguese (Brazil)
  • zh-CN - Chinese (Simplified)

Locale-Specific Responses

All error messages, AI responses, and notifications are returned in the user's selected locale based on the Accept-Language header or user profile setting.

Date/Time Formatting

Dates are returned in ISO 8601 format but should be displayed according to user's locale:

  • US: MM/DD/YYYY, 12-hour clock
  • EU: DD/MM/YYYY, 24-hour clock
  • Time zones always included in responses

Measurement Units

{
  "measurement": {
    "value": 8.2,
    "unit": "kg",
    "display": "8.2 kg",
    "imperial": {
      "value": 18.08,
      "unit": "lbs",
      "display": "18 lbs 1 oz"
    }
  }
}

Security Considerations

Authentication Flow

  1. User provides credentials + device fingerprint
  2. Server validates and issues access token (1 hour) + refresh token (30 days)
  3. Device fingerprint stored and validated on each request
  4. Refresh token rotation on use
  5. All tokens revoked on suspicious activity

Data Encryption

  • All API communication over HTTPS/TLS 1.3
  • Sensitive fields encrypted at rest (AES-256)
  • PII data anonymized in logs
  • No sensitive data in URL parameters

COPPA/GDPR Compliance

  • Parental consent required for child profiles
  • Data minimization enforced
  • Right to deletion implemented
  • Data portability via export endpoints
  • Audit logs for all data access

Request Signing (Optional Enhanced Security)

X-Signature: HMAC-SHA256(request-body + timestamp + nonce)
X-Timestamp: 1704903000
X-Nonce: unique-request-id

Testing Endpoints

Health Check

GET /api/v1/health

Response 200:

{
  "status": "healthy",
  "version": "1.0.0",
  "timestamp": "2024-01-10T15:30:00Z",
  "services": {
    "database": "connected",
    "redis": "connected",
    "ai": "operational"
  }
}

Test Notifications

POST /api/v1/test/notification

Request Body:

{
  "type": "test",
  "deviceId": "uuid"
}

SDK Usage Examples

JavaScript/TypeScript

import { MaternalAPI } from '@maternal/sdk';

const api = new MaternalAPI({
  baseUrl: 'https://api.maternal.app',
  version: 'v1'
});

// Authentication
const { tokens, user } = await api.auth.login({
  email: 'user@example.com',
  password: 'password',
  deviceInfo: getDeviceInfo()
});

// Set tokens for subsequent requests
api.setTokens(tokens);

// Log activity
const activity = await api.activities.logFeeding({
  childId: 'chd_9m2k4n8p',
  type: 'bottle',
  amount: 120,
  unit: 'ml'
});

// Subscribe to real-time updates
api.websocket.on('activity-logged', (data) => {
  console.log('New activity:', data);
});

React Native

import { useMaternalAPI } from '@maternal/react-native';

function FeedingScreen() {
  const { logFeeding, isLoading } = useMaternalAPI();
  
  const handleLogFeeding = async () => {
    await logFeeding({
      childId: currentChild.id,
      type: 'breast',
      duration: 15
    });
  };
}