Skip to main content
Everything Phosra enforces hangs off a child. A child belongs to a family, and carries a policy — the set of rules that get pushed to every connected platform. This guide builds all three in one call, then enforces the result, entirely against the public sandbox. Every response below is verbatim live output, captured while writing this page. No API key.
Sandbox-first. These requests run against https://phosra-api-sandbox-production.up.railway.app — open, seeded, and safe. In production, change the base URL to https://prodapi.phosra.com and add your phosra_live_… key; the request shapes are identical.

Before you start

export PHOSRA_BASE="https://phosra-api-sandbox-production.up.railway.app/api/v1"
1

Create a family, a child, and an active policy — in one call

POST /setup/quick is the fastest path to a working policy. Give it a child’s name, birth date, and a strictness level (recommended, strict, or light). Phosra derives the age group from the birth date and returns a family, a child, an active policy, and a full set of age-appropriate rules — with no follow-up calls.
curl -s -X POST "$PHOSRA_BASE/setup/quick" \
  -H "Content-Type: application/json" \
  -d '{
    "child_name": "Emma",
    "birth_date": "2016-03-15",
    "strictness": "recommended"
  }'
Real response (200), trimmed to the fields you will use:
{
  "family": { "id": "8581b059-4d30-4f98-88a5-c9667610f5ca", "name": "Emma's Family" },
  "child":  { "id": "1a787ff2-bcd5-4b2d-a5fd-2235ba4e6b9a", "name": "Emma",
              "birth_date": "2016-03-15T00:00:00Z" },
  "policy": { "id": "7949ee63-d113-4405-b791-d400e6e1f97d", "status": "active" },
  "age_group": "preteen",
  "max_ratings": { "mpaa": "PG", "esrb": "E10+", "pegi": "7", "csm": "10+", "tvpg": "TV-PG" },
  "rule_summary": {
    "screen_time_minutes": 120,
    "bedtime_hour": 21,
    "web_filter_level": "moderate",
    "content_rating": "PG",
    "total_rules_enabled": 20
  }
}
Phosra picked age_group: "preteen" from the 2016 birth date and mapped it to concrete rating ceilings across five rating systems. Store family.id, child.id, and policy.id.
Change one field to see the age model work. A 2012-09-01 birth date with strictness: "strict" returns age_group: "teen", max_ratings.mpaa: "PG-13", and a lighter web filter — the policy adapts to the child, not to a fixed template.
Request body (application/json)
FieldTypeRequiredDescription
child_namestringyesThe child’s display name; becomes child.name and seeds family.name ("Emma's Family").
birth_datestring (YYYY-MM-DD)yesDrives the derived age_group and the rating ceilings.
strictnessstringnoOne of recommended, strict, light. Defaults to recommended; an unrecognised value falls back to the default rather than erroring.
Response fields (200)
FieldTypeDescription
family.idstring (UUID)The created family. Store it.
child.idstring (UUID)The child handle — pass it to /children/{id}/enforce and the policy endpoints.
policy.idstring (UUID)The generated policy.
policy.statusstringactive — the policy is live immediately, no activation call.
age_groupstringDerived bucket, e.g. preteen, teen.
max_ratingsobjectRating ceilings across mpaa, esrb, pegi, csm, tvpg.
rule_summary.total_rules_enabledintegerCount of enabled rule categories (20 for preteen / recommended).
rulesarrayOne object per enabled rule category with its config (see next step).
Errors
StatusmessageWhen it happens
400child_name is requiredchild_name is missing from the body.
400birth_date is requiredbirth_date is missing from the body.
500internal server errorbirth_date is present but not a parseable YYYY-MM-DD date. Validate the format client-side — the sandbox surfaces this as a 500 rather than a clean 400.
2

Inspect the rules that were generated

The full response includes a rules array — one entry per enabled rule category, each with its own config. For example, the addictive_design_control rule comes back pre-tuned:
{
  "id": "6106fd52-48fa-425b-ba4d-df54fcde2704",
  "policy_id": "7949ee63-d113-4405-b791-d400e6e1f97d",
  "category": "addictive_design_control",
  "enabled": true,
  "config": {
    "disable_streaks": true,
    "disable_autoplay": true,
    "disable_like_counts": true,
    "disable_daily_rewards": true,
    "disable_infinite_scroll": true
  }
}
Every category comes from the canonical rule reference. You can override any of them later with the policy rules API.
Each element of the rules array:
FieldTypeDescription
idstring (UUID)The rule’s id, for targeted updates.
policy_idstring (UUID)The policy this rule belongs to — matches policy.id.
categorystringRule category slug (e.g. addictive_design_control) from the rule reference.
enabledbooleanWhether the rule is active in this policy.
configobjectPer-category settings. Shape depends on category.
3

Enforce the policy across connected platforms

Push the active policy to every platform the family has connected. Enforcement is asynchronous — you get a job back immediately (202 Accepted):
curl -s -X POST "$PHOSRA_BASE/children/1a787ff2-bcd5-4b2d-a5fd-2235ba4e6b9a/enforce"
Real response (202):
{
  "id": "56072389-470e-4f7c-9d42-1f6cea38d43a",
  "child_id": "1a787ff2-bcd5-4b2d-a5fd-2235ba4e6b9a",
  "policy_id": "7949ee63-d113-4405-b791-d400e6e1f97d",
  "trigger_type": "manual",
  "status": "running"
}
Path parameter
FieldTypeRequiredDescription
childIDstring (UUID)yesThe child.id from /setup/quick.
Response fields (202)
FieldTypeDescription
idstring (UUID)The enforcement job id — poll it in the next step.
child_idstring (UUID)Echoes the child being enforced.
policy_idstring (UUID)The active policy being pushed.
trigger_typestringmanual for a direct call.
statusstringrunning at accept time.
Errors
StatusmessageWhen it happens
400invalid child IDchildID is not a well-formed UUID.
404child not foundchildID is a valid UUID but no such child exists.
4

Confirm the job completed

Poll the job by id until status is completed. Enforcement does not fire a webhook on completion — poll the job, don’t wait for a callback.
curl -s "$PHOSRA_BASE/enforcement/jobs/56072389-470e-4f7c-9d42-1f6cea38d43a"
Real response (200):
{
  "id": "56072389-470e-4f7c-9d42-1f6cea38d43a",
  "status": "completed",
  "completed_at": "2026-07-06T03:02:11.029Z"
}
That is the full loop: create → enforce → confirm. For results per platform, read GET /enforcement/jobs/{id}/results — a non-empty manual_steps array means that platform is parent-guided rather than programmatically applied.
Path parameter
FieldTypeRequiredDescription
jobIDstring (UUID)yesThe id returned by /enforce.
Response fields (200)
FieldTypeDescription
idstring (UUID)The job id.
statusstringrunningcompleted (or failed). Poll until it leaves running.
completed_atstring (RFC 3339)When the job finished; absent while still running.
Notes & errors
BehaviourDetail
Unknown jobIDReturns 200 with a null body rather than a 404. Treat null as “no such job” in your client.
No connected platformsGET /enforcement/jobs/{id}/results returns null — there was nowhere to push. Connect a platform first (see Connect a platform).

Adding more children

Call POST /setup/quick once per child (each returns its own child in the same family when you pass the same family context in production), or use the granular endpoints — POST /families, POST /families/{familyID}/children, POST /children/{childID}/policies/generate — when you need finer control. See the Families and Children reference.

Next steps

Connect a platform

Link a family to a platform so enforcement has somewhere to land.

Test in the sandbox

Understand the open sandbox, the signed Trust List, and self-registration.