# API Contract

*Updated: 2026-04-05*

All endpoints extracted from journey effect definitions, organised by domain. Each endpoint includes the request/response schema as defined in the journey graphs.

Base URL: `/api`
Auth: Bearer token (Better Auth magic link sessions)
Content-Type: `application/json`

---

## Identity Domain

### `POST /api/onboarding/guardian`
Save guardian profile after onboarding completion.

```typescript
// Request
{
  guardianName: string       // required
  dependentName: string      // required
  familyInvites: string[]    // optional — emails/phones to invite
}

// Response 201
{
  profileId: string
  createdAt: string          // ISO 8601
}

// Errors
// 400 invalid_profile
```

### `POST /api/onboarding/survivor`
*(Implied — not yet in journey effects but needed)*

```typescript
// Request
{
  survivorName: string
  persona: "survivor"
}

// Response 201
{
  profileId: string
  createdAt: string
}
```

---

## Wellbeing Domain

### `GET /api/checkins/streak`
Load current streak data for the authenticated user.

```typescript
// Response 200
{
  currentStreak: number      // required — consecutive days
  lastCheckinAt: string      // optional — ISO 8601
}
```

### `POST /api/checkins`
Save a daily check-in.

```typescript
// Request
{
  mood: "great" | "good" | "mixed" | "struggling"  // required
  journalEntry: string       // optional
  streak: number             // required — client-computed, server validates
}

// Response 201
{
  checkinId: string
  streakCount: number        // server-authoritative streak
}

// Errors
// 400 invalid_checkin
// 429 rate_limited — already checked in today
```

### `POST /api/checkin/submit`
*(Home navigation variant — same domain, slightly different shape)*

```typescript
// Request
{
  mood: string               // required
  notes: string              // optional
}

// Response 201
{
  checkinId: string
  submittedAt: string        // ISO 8601
}
```

### `POST /api/checkin/configure`
Configure check-in schedule (guardian flow).

```typescript
// Request
{
  dependentName: string      // required
  method: string             // required — "app" | "sms" | "watch"
  schedule: {                // required
    frequency: string        // "daily" | "twice-daily" | "weekly"
    time: string             // HH:mm
    timezone: string
  }
}

// Response 201
{
  checkinId: string
}
```

### `GET /api/checkins/history`
*(Needed for REQ-003 streak context — not yet in journey effects)*

```typescript
// Query: ?days=7|30
// Response 200
{
  checkins: Array<{
    date: string             // YYYY-MM-DD
    mood: string
    hasJournal: boolean
    journalSnippet?: string  // first 100 chars, only if shared
  }>
  trend: "improving" | "declining" | "stable" | "volatile"
}
```

---

## Safety Domain

### `GET /api/safety-plan`
Load the authenticated user's safety plan.

```typescript
// Response 200
{
  triggers: string[]              // optional — may be empty on first load
  copingStrategies: string[]      // optional
  emergencyContacts: Array<{      // optional
    name: string
    phone: string
    relationship: string
  }>
  lastUpdatedAt: string           // optional — ISO 8601
}
```

### `PUT /api/safety-plan`
Save/update safety plan.

```typescript
// Request
{
  triggers: string[]              // required
  copingStrategies: string[]      // required
  emergencyContacts: Array<{      // required
    name: string
    phone: string
    relationship: string
  }>
}

// Response 200
{
  planId: string
  updatedAt: string               // ISO 8601
}

// Errors
// 400 invalid_plan
```

### `POST /api/crisis-plan/save`
Save initial crisis plan during survivor onboarding.

```typescript
// Request
{
  crisisContacts: Array<{
    name: string
    phone: string
    relationship: string
  }>
  copingStrategies: string[]
  avoidList: string[]
}

// Response 201
{
  planId: string
  savedAt: string
}

// Errors
// 400 invalid_plan
```

### `POST /api/crisis/activate`
Activate crisis mode — sends alerts to all connected angels.

```typescript
// Request
{
  survivorId: string         // required
  crisisType: string         // required — "panic" | "distress" | "danger"
}

// Response 201
{
  crisisId: string
  alertedAngels: Array<{
    angelId: string
    name: string
    alertMethod: "push" | "sms"
    sentAt: string
  }>
}
```

### `POST /api/crisis/resolve`
Mark a crisis as resolved (after post-crisis recovery timeline).

```typescript
// Request
{
  crisisId: string           // required
}

// Response 200
{
  resolvedAt: string         // ISO 8601
}
```

---

## Circle Domain

### `POST /api/invitations/send`
Send angel invitation(s) from a user.

```typescript
// Request
{
  invitees: Array<{
    name: string
    contactMethod: "email" | "sms" | "link"
    contactValue: string     // email or phone
  }>
}

// Response 201
{
  sentCount: number
}
```

### `POST /api/invitations/accept`
Accept an angel invitation.

```typescript
// Request
{
  notificationLevel: "all" | "crisis-only" | "summary"  // required
  pushEnabled: boolean       // required
}

// Response 201
{
  circleId: string
  angelId: string
}

// Errors
// 400 invalid_invitation
// 404 invitation_not_found
// 409 already_accepted
```

### `POST /api/invitations/decline`
Decline an angel invitation.

```typescript
// Request
{
  invitationId: string       // required
}

// Response 200
{
  acknowledged: boolean
}

// Errors
// 404 invitation_not_found
```

---

## Consent Domain

### `POST /api/consent/save`
Save consent/sharing rules.

```typescript
// Request
{
  selectedMetrics: Array<"mood" | "streak" | "journal" | "crisis" | "plan">
  perPersonRules: Record<string, {  // angelId → rules
    metrics: string[]
    visibility: "hidden" | "summary" | "detailed"
  }>
}

// Response 201
{
  consentId: string
  savedAt: string
}

// Errors
// 400 invalid_consent
// 409 consent_conflict — concurrent edit detected
```

---

## Summary: All Endpoints

| Method | Endpoint | Domain | Journey Source |
|--------|----------|--------|---------------|
| POST | `/api/onboarding/guardian` | Identity | guardian-onboarding |
| GET | `/api/checkins/streak` | Wellbeing | daily-checkin |
| POST | `/api/checkins` | Wellbeing | daily-checkin |
| POST | `/api/checkin/submit` | Wellbeing | home-navigation |
| POST | `/api/checkin/configure` | Wellbeing | guardian-onboarding |
| GET | `/api/checkins/history` | Wellbeing | *(REQ-003, planned)* |
| GET | `/api/safety-plan` | Safety | safety-plan |
| PUT | `/api/safety-plan` | Safety | safety-plan |
| POST | `/api/crisis-plan/save` | Safety | survivor-onboarding |
| POST | `/api/crisis/activate` | Safety | crisis-flow |
| POST | `/api/crisis/resolve` | Safety | crisis-flow |
| POST | `/api/invitations/send` | Circle | survivor-onboarding |
| POST | `/api/invitations/accept` | Circle | angel-invitation |
| POST | `/api/invitations/decline` | Circle | angel-invitation |
| POST | `/api/consent/save` | Consent | consent-wizard |

**15 endpoints total** — all derived from journey graph effects. No speculative endpoints.

---

## Fake API Layer (Demo Mode)

For the demo prototype, each endpoint returns static JSON. Implementation:

```typescript
// api/mock.ts
const mockResponses: Record<string, unknown> = {
  'GET /api/checkins/streak': {
    currentStreak: 7,
    lastCheckinAt: '2026-04-04T09:15:00Z',
  },
  'GET /api/safety-plan': {
    triggers: ['Loud arguments', 'Being alone at night', 'News about violence'],
    copingStrategies: ['Cold water on wrists', 'Call Mum', 'Go outside', 'Box breathing'],
    emergencyContacts: [
      { name: 'Mum', phone: '07700 900123', relationship: 'Mother' },
      { name: 'Dr. Chen', phone: '020 7946 0958', relationship: 'Therapist' },
    ],
    lastUpdatedAt: '2026-03-28T14:30:00Z',
  },
  // ... etc
};
```

This keeps the demo fully functional without any backend infrastructure. When the real API is built, swap the mock layer for real fetch calls — zero component changes needed.
