Document 04 · Client-used interfaces

API reference

REST calls made by reachable app code, including request fields, expected status, and the response shape the client consumes.

Scope: call sites in the running client. The configured but unused /v1/resources, /v1/trait-definitions, and /v1/auth/mfa/verify routes are not listed.

1. Conventions

ItemClient behavior
Base URLEXPO_PUBLIC_API_BASE_URL, trailing slash removed. Default paths come from src/config/apiEndpoints.ts; most may be replaced by an endpoint-specific environment URL.
AuthenticationAuthorization: Bearer <access_token> and, when available, X-Account-Id: <account.id>.
FormatAccept: application/json; JSON writes add Content-Type: application/json. Empty DELETE/POST requests deliberately omit content type.
Success envelopeUsually { "data": ... }. Lists may also include page or meta.
ErrorsProblem body fields consumed: code, detail or title, optional entitlement. Retry-After becomes retryAfterSeconds.
Session expiryHTTP 401 or codes invalid_token, missing_bearer_token, token_expired, user_not_found clear the local session.
IdempotencyChild creation, onboarding, scan creation, play-session creation, milestone writes, saved items, article reads and consent writes use a generated idempotency key where implemented.
CacheAuthenticated repository calls use cache: no-store.

2. Authentication and account

Method and pathAuthRequestExpected / consumed response
POST /v1/auth/registerNo{email, password, given_name?, family_name?, mfa_code?, recaptcha_token?, onboarding?}201 · {data: AuthSession}
POST /v1/auth/loginNoSame email-auth body200 · {data: AuthSession}
POST /v1/auth/googleNo{id_token}200 existing / 201 created · session envelope
POST /v1/auth/appleNo{identity_token, authorization_code?, given_name?, family_name?}200 existing / 201 created · session envelope
GET /v1/meYes200 · {data: {user, accounts[]}}
DELETE /v1/meYes200 or 204. A 404 is treated as already deleted.
POST [configured logout URL]YesNo bodyBest-effort; response is ignored. The call is skipped unless EXPO_PUBLIC_AUTH_LOGOUT_URL is explicitly set.
AuthSession {
  access_token: string
  user: User
  account?: Account
  // optional token/session metadata returned by server
}

3. Children and onboarding

Method and pathRequestExpected / consumed response
GET /v1/children200 · {data: ChildRecord[]}
POST /v1/childrenChildCreateInput + Idempotency-Key201 · child envelope. 402 entitlement is surfaced to the paywall.
PATCH /v1/children/{childId}ChildUpdateInput; no If-Match200 · child envelope
DELETE /v1/children/{childId}202 · {data: JobRecord}. Child is removed from UI immediately.
GET /v1/jobs/{jobId}200 · job envelope; client follows poll_after_ms for up to 60 seconds.
POST /v1/onboardingRegisterOnboardingPayload + idempotency key200 or 201 · {data: OnboardingResult}
POST /v1/onboarding (anonymous)Legacy onboarding snapshot, no authAny 2xx is accepted; used by “Continue for now.”
ChildCreateInput {
  given_name: string
  preferred_name?: string
  birthdate: string       // YYYY-MM-DD
  sex?: string | null
  locale?: string
  timezone?: string
}

RegisterOnboardingPayload {
  child: ChildCreateInput
  assessment: four-area milestone answers
  interests: InterestWrite[]
  traits?: TraitWrite[]
  // onboarding metadata carried by the typed request
}

4. Child interests and traits

Method and pathRequestExpected response
GET /v1/interest-tags?age_months=N&curated=falseOptional auth; curated=false only when requestedAny OK · {data: InterestTag[]}; failure returns empty list.
PUT /v1/children/{childId}/interests{interests: InterestWrite[]}200 · interest records
POST /v1/children/{childId}/interestsInterestWrite201 · one interest record
PATCH /v1/children/{childId}/interests/{id}InterestUpdate200 · one interest record
DELETE /v1/children/{childId}/interests/{id}204
PUT /v1/children/{childId}/traits{traits: TraitWrite[]}200 · trait records
POST /v1/children/{childId}/traitsTraitWrite201 · one trait record
PATCH /v1/children/{childId}/traits/{id}TraitUpdate200 · one trait record
DELETE /v1/children/{childId}/traits/{id}204

5. App read models and care queue

Method and pathQuery/bodyExpected / consumed response
GET /v1/children/{childId}/home200 · {data: HomeScreenData}
GET /v1/children/{childId}/portrait200 · {data: PortraitScreenData}. No include=report is sent.
GET /v1/children/{childId}/sessionslimit, optional completed, since, cursor200 · {data: PlaySessionRecord[], page?: {has_more,next_cursor}}. Full history loops at 100/page, max 20 pages.
GET /v1/children/{childId}/care-queue200 · {data: CareQueueItem[], meta}
POST /v1/children/{childId}/care-queue/{itemId}/actions{action, resolution?, snooze_until?}200 · updated care queue item

6. Activities, scans, and play sessions

Method and pathRequestExpected / consumed response
GET /v1/activitiesage_months, limit=50, optional setting; optional auth; locale headers/queryOK · activity list/read model. Signed-in callers require remote results; signed-out paths may use bundled content.
POST /v1/children/{childId}/scansSee SceneScanInput below; idempotency header and body key201 · {data:{scan: ScanRecord}}. 402 is allowed to reach paywall handling.
GET /v1/scans/{scanId}200 · scan envelope; polled until completed/failed/expired/rejected, max 90 seconds.
GET /v1/scans/{scanId}/detections200 · detection array
GET /v1/scans/{scanId}/suggestions200 · activity suggestions
POST /v1/children/{childId}/sessions{activity_id, client_event_id, context, suggestion_id?} + idempotency key200 or 201 · play session
PATCH /v1/sessions/{sessionId}{completed:true, ended_at}200 · completed play session
PUT /v1/sessions/{sessionId}/feedback{rating?, difficulty_feedback?, enjoyment?, child_engaged?, comment?}200 · data must be present; concrete payload is not otherwise consumed.
SceneScanInput {
  content_type: "image/jpeg" | "image/png"
  bytes: number
  image_base64: string
  idempotency_key: string
  room_hint?: string
  user_prompt?: string[]
  user_tags?: string[]
  regenerate_of_suggestion_id?: string
  regenerate_mode?: "similar_activity"
  preview_mock?: true          // localhost preview only
}

Play session context is from_scan | from_library | from_milestone | from_search | from_class | ad_hoc. Feedback maps caregiver chips to too_easy|just_right|too_hard and loved|liked|disliked.

7. Milestones

Method and pathRequestExpected response
GET /v1/children/{childId}/milestoneslimit, optional cursor200 · paged milestone observations; repository follows pages.
PUT /v1/children/{childId}/milestones/{milestoneId}Milestone observation write + idempotency key200 · observation
PUT /v1/children/{childId}/milestones/by-code/{code}Milestone observation write + idempotency key200 · observation
DELETE /v1/children/{childId}/milestones/by-code/{code}204

8. Saved items and article reads

Method and pathRequestExpected / consumed response
GET /v1/children/{childId}/saved-itemsobject_type, limit200 · saved item list
POST /v1/children/{childId}/saved-items{object_type, object_ref, note?} + idempotency key200 or 201 · saved item
DELETE /v1/saved-items/{savedItemId}204
GET /v1/children/{childId}/article-readslimit, optional cursor200 · article read list/page
PUT /v1/children/{childId}/article-reads/{articleId}Read/completion state + idempotency key200 · article read

The Resources catalog itself comes from the home.resources read model for signed-in users; the configured standalone resources route has no reachable caller.

9. Tracker

Method and pathRequestExpected / consumed response
GET /v1/children/{childId}/tracker/entries?limit=100200 · {data: ApiTrackerEntry[]}; merged into the local store.
POST /v1/children/{childId}/tracker/sync{entries: ApiTrackerEntryInput[]}, maximum 200 per request200 · result rows with client_event_id, outcome: accepted|duplicate|rejected, and optional server entry.
PATCH /v1/tracker/entries/{entryId}One ApiTrackerEntryInput200 · updated entry
DELETE /v1/tracker/entries/{entryId}No body/content type204 or 404; both clear the local tombstone.
ApiTrackerEntryInput {
  kind: "feeding" | "sleep" | "diaper" | "measurement" | "health" | "pump"
  occurred_at: ISODateTime
  ended_at?: ISODateTime
  duration_seconds?: number
  note?: string
  client_event_id?: string
  feeding?: {method?, amount_ml?, food_items?, meal_type?, reacted_to?, mood_after?}
  sleep?: {kind?, quality?, location?, wake_count?, mood_before?, mood_after?, slept_through?}
  diaper?: {kind?, consistency?, color?, has_rash?, in_potty?}
  measurement?: {kind?, value?, unit?: "kg"|"cm", source?: "parent"}
  health?: {kind?, description?, temperature_c?}
  pump?: {left_ml?, right_ml?, left_seconds?, right_seconds?, total_ml?}
}

Optional input values are omitted rather than sent as null. Response rows add id, child_id, created_at, and may return nullable optional values.

10. Notifications and devices

Method and pathRequestExpected response
GET /v1/notificationscursor?, limit, unread_only?200 · {data: AppNotification[], meta/page}
POST /v1/notifications/mark-viewedExactly one of {ids: string[]} or {before: ISODateTime}200
PATCH /v1/notifications/{id}{viewed:true} or {dismissed:true}200 · updated notification
GET /v1/notifications/preferences200 · notification preferences
PUT /v1/notifications/preferencesComplete preference update used by the screen200 · updated preferences
POST /v1/devicesDevice upsert with platform, push token and client metadata200 · {data: Device}; id stored locally.
DELETE /v1/devices/{deviceId}204 or 404

11. Entitlements, config, consent, and support

Method and pathRequestExpected / consumed response
GET /v1/account/entitlements200 · {data: Entitlements}. Failure retains free fallback.
GET /v1/configOptional bearerOK · {data:{flags, limits}}; used for payment switch and protocol limits.
POST /v1/consents{consent_type, granted, method:"in_app", policy_version} + idempotency key200 or 201. Photo consent writes three consent types in parallel, best-effort.
POST /v1/support/requests{type, message, child_id?, current_url?, client_context?}202; response body is not consumed.
Entitlements {
  plan: string
  tier?: {code, label, rank}
  is_active: boolean
  source: string
  expires_at?: ISODateTime | null
  in_trial?: boolean
  trial_ends_at?: ISODateTime | null
  features?: Record<string, boolean>
  limits?: Record<string, number>  // -1 means unlimited
  active_promotions?: unknown[]
  payments_enabled?: boolean
}

12. Non-REST interfaces

InterfaceCurrent useCurrent constraint
RevenueCat react-native-purchasesConfigure by account id, read offerings, purchase one of three product ids, restore purchases, log out SDK.The environment reader omits both RevenueCat public key names, so configuration receives no key and this service returns unavailable in the current snapshot.
Expo notificationsRequest permission, obtain push token, register/unregister device.Unavailable paths fail without crashing.
Apple / Google auth SDKsObtain identity token, then exchange through the REST auth endpoints.Platform/client-id configuration controls availability.
Camera/image APIsCapture/select, resize and encode room photos; select/crop avatar photos.Scan requires explicit consent; avatar remains local.

13. Typical authenticated call

sequenceDiagram
  participant UI as Screen
  participant R as Repository
  participant A as authorizedFetch
  participant API as API
  UI->>R: typed input
  R->>A: URL, method, expected status
  A->>API: Bearer + X-Account-Id + JSON
  API-->>A: data envelope or problem
  alt expected status
    A-->>R: Response
    R-->>UI: mapped typed data
  else error
    A-->>UI: AuthApiError
  end