Skip to main content
Real households are not one child. This recipe onboards one family with two kids of different ages and gives each an age-appropriate policy that Phosra generates from their birth date — a 9-year-old and a 16-year-old end up with genuinely different rules, screen-time, and rating ceilings, from the same three lines of code. You will chain five endpoints: Every request and response below is verbatim live output, captured by running these exact calls against https://phosra-api-sandbox-production.up.railway.app. No API key, nothing to install.
Sandbox-first. These calls run against the open sandbox with no credential — nothing you create touches a real family. The shapes are identical in production: swap the base URL for https://prodapi.phosra.com and authenticate with a phosra_live_… key.

Before you start

Set the base URL once so every step is copy-paste:
export PHOSRA_BASE="https://phosra-api-sandbox-production.up.railway.app"
1

Onboard the family with the first child

POST /setup/quick onboards a family, the first child, and an age-appropriate starter policy in one call. Give it the younger child — Leo, age 9.
curl -s -X POST "$PHOSRA_BASE/api/v1/setup/quick" \
  -H "Content-Type: application/json" \
  -d '{
    "child_name": "Leo",
    "birth_date": "2016-09-03",
    "family_name": "The Okafor Family",
    "strictness": "recommended"
  }'
The response returns the family, the child, a fully-seeded policy, and a rule_summary — Leo, at 9, lands in the child age group with a 90-minute daily limit and a PG rating ceiling. Truncated to what you carry forward:
{
  "family": {
    "id": "af03190e-b31c-40c9-9cc4-f282a532326e",
    "name": "The Okafor Family",
    "created_at": "2026-07-06T08:45:32.502290561Z"
  },
  "child": {
    "id": "f87273d3-bbb7-43b7-9929-f4a0f2d59373",
    "name": "Leo",
    "birth_date": "2016-09-03T00:00:00Z"
  },
  "policy": { "id": "6d81fd69-2395-48f5-aac9-447e3cc118bb", "status": "active" },
  "age_group": "child",
  "max_ratings": { "csm": "7+", "esrb": "E", "mpaa": "PG", "pegi": "7", "tvpg": "TV-Y7" },
  "rule_summary": {
    "screen_time_minutes": 90,
    "bedtime_hour": 20,
    "web_filter_level": "strict",
    "content_rating": "PG",
    "total_rules_enabled": 20
  }
}
Keep family.id (the next child is added to it) and child.id (Leo, for the ratings check at the end):
export FAMILY_ID="af03190e-b31c-40c9-9cc4-f282a532326e"
export LEO_ID="f87273d3-bbb7-43b7-9929-f4a0f2d59373"

Reference: quickstart · setup/quick

The one-call onboarding path, with the full request and response documented.
2

Add the second child

The teenager is added to the same family with POST /families/{familyID}/children. Only a name and birth date are required — Ava, age 16.
curl -s -X POST "$PHOSRA_BASE/api/v1/families/$FAMILY_ID/children" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Ava", "birth_date": "2010-02-20" }'
{
  "id": "acf4ce5d-d452-47b8-a78b-3b4ca86f50be",
  "family_id": "af03190e-b31c-40c9-9cc4-f282a532326e",
  "name": "Ava",
  "birth_date": "2010-02-20T00:00:00Z",
  "created_at": "2026-07-06T08:45:32.832408747Z"
}
export AVA_ID="acf4ce5d-d452-47b8-a78b-3b4ca86f50be"
Adding a child does not create a policy — unlike setup/quick, which bundles one. That is the next two steps: create a draft policy, then fill it from Ava’s age.
3

Create a draft policy for the teen

POST /children/{childID}/policies creates an empty draft policy. It has no rules yet — you fill it in the next step.
curl -s -X POST "$PHOSRA_BASE/api/v1/children/$AVA_ID/policies" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Ava'\''s Policy" }'
{
  "id": "a3147895-b752-4d03-846c-35fd8fb4e77c",
  "child_id": "acf4ce5d-d452-47b8-a78b-3b4ca86f50be",
  "name": "Ava's Policy",
  "status": "draft",
  "version": 0,
  "created_at": "2026-07-06T08:45:33.024493168Z"
}
export AVA_POLICY_ID="a3147895-b752-4d03-846c-35fd8fb4e77c"
4

Generate rules from Ava's age

This is the load-bearing call. POST /policies/{policyID}/generate-from-age looks up the child on the policy, computes their age, and fills the policy with an age-appropriate rule set at the strictness you pass. We use strict for the teen.
curl -s -X POST "$PHOSRA_BASE/api/v1/policies/$AVA_POLICY_ID/generate-from-age" \
  -H "Content-Type: application/json" \
  -d '{ "strictness": "strict" }'
The call returns the generated rules. Ava’s set is tuned to 16, not 9 — a longer daily limit, a PG-13 ceiling, a lighter web filter, contacts-only DMs, a later curfew, and no age gate:
[
  { "category": "time_daily_limit",  "config": { "daily_minutes": 180 } },
  { "category": "content_rating",    "config": { "max_ratings": {
      "csm": "13+", "esrb": "T", "mpaa": "PG-13", "pegi": "12", "tvpg": "TV-14" } } },
  { "category": "web_filter_level",  "config": { "level": "light" } },
  { "category": "dm_restriction",    "config": { "mode": "contacts_only" } },
  { "category": "notification_curfew", "config": { "start": "22:00", "end": "06:00" } },
  { "category": "age_gate",          "enabled": false }
]
Compare with Leo’s seeded policy from step 1 — same family, same code, different child:
RuleLeo (age 9, recommended)Ava (age 16, strict)
Daily screen time90 min180 min
Content-rating ceilingPG / CSM 7+PG-13 / CSM 13+
Web filterstrictlight
DMsfriends_onlycontacts_only
Curfew20:00–07:0022:00–06:00
Age gateon (min 13)off
Total rules enabled2018
FieldTypeNotes
strictnessenumrecommended (default), strict, or relaxed. Shifts limits and thresholds; the rating ceiling is driven by age, not strictness.
generate-from-age replaces the policy’s rules — it is a regenerate, not a merge. A policy with no resolvable child returns 400. To hand-edit an individual rule afterward, see Change a rule and enforce it.
5

Activate the policy

A generated policy is still a draft. POST /policies/{policyID}/activate makes it the child’s live policy and stamps a version.
curl -s -X POST "$PHOSRA_BASE/api/v1/policies/$AVA_POLICY_ID/activate" \
  -H "Content-Type: application/json" -d '{}'
{ "id": "a3147895-b752-4d03-846c-35fd8fb4e77c", "status": "active", "version": 20 }
Both children now have an active, age-tuned policy. Leo’s went live at step 1; Ava’s is live now.
6

Confirm each child's rating ceiling

Isolation across children is not just “two policies exist” — it is “each policy matches that child’s age.” GET /children/{childID}/age-ratings returns the rating ceiling Phosra computes from a child’s birth date. Run it for both and the numbers line up with the policies you just generated.
curl -s "$PHOSRA_BASE/api/v1/children/$LEO_ID/age-ratings"
curl -s "$PHOSRA_BASE/api/v1/children/$AVA_ID/age-ratings"
// Leo
{ "age": 9,  "ratings": { "csm": { "code": "7+" },  "mpaa": { "code": "PG" } } }
// Ava
{ "age": 16, "ratings": { "csm": { "code": "13+" }, "mpaa": { "code": "PG-13" } } }
Leo’s ceiling is PG / CSM 7+; Ava’s is PG-13 / CSM 13+ — exactly the content_rating rule each policy carries. One family, two children, two correct ceilings.

The whole flow at a glance

Every row is one call you just ran, in order:
#StepCallLive result
1Onboard family + LeoPOST /setup/quickfamily af03190e…, Leo (9), 20-rule PG policy
2Add AvaPOST /families/{id}/childrenchild acf4ce5d…, age 16
3Draft Ava’s policyPOST /children/{id}/policiespolicy a3147895…, draft
4Generate from agePOST /policies/{id}/generate-from-age18 rules, PG-13, 180-min limit
5ActivatePOST /policies/{id}/activateactive, version 20
6Confirm ceilingsGET /children/{id}/age-ratingsLeo PG · Ava PG-13

Next steps

Change a rule and enforce it

Tighten one of these rules and push it to a connected platform — then read back what landed.

End-to-end walkthrough

Link two platforms to this family, unlink one, and prove per-service isolation.

Families & kids

The full family, child, and policy resource model behind these calls.

Strictness levels

How recommended, strict, and relaxed shift limits — and why the rating ceiling follows age.