Skip to main content
Setting a policy is half the job. The other half is pushing a change to the platforms a family has already connected and knowing precisely what happened on each one. This recipe tightens a screen-time limit, enforces it against a connected console, and reads back a per-platform report: what was applied, what was guided, and what the platform simply can’t do. You will chain five endpoints: Every request and response below is verbatim live output, captured 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. The shapes are identical in production: swap the base URL for https://prodapi.phosra.com and add a phosra_live_… key.

Prerequisites

This recipe starts from a child with an active policy and at least one connected platform. If you ran Onboard a multi-child household you already have the child and policy — the two calls below add a connected console (Xbox) so there is something to enforce against. Set the base URL and IDs once:
export PHOSRA_BASE="https://phosra-api-sandbox-production.up.railway.app"
export FAMILY_ID="af03190e-b31c-40c9-9cc4-f282a532326e"   # from the multi-child recipe
export CHILD_ID="acf4ce5d-d452-47b8-a78b-3b4ca86f50be"    # Ava, age 16, active policy

# Connect a console to the family so the enforce step has a target
curl -s -X POST "$PHOSRA_BASE/api/v1/compliance" \
  -H "Content-Type: application/json" \
  -d "{ \"family_id\": \"$FAMILY_ID\", \"platform_id\": \"xbox\", \"credentials\": \"sandbox-demo-token\" }"
{ "id": "bd220718-3c98-42f8-9d7f-31d82a0e88a3", "platform_id": "xbox", "status": "verified" }
Don’t have a family yet? Run Onboard a multi-child household first (three calls), or the End-to-end walkthrough for the full platform-link ceremony. Then come back with your own CHILD_ID.
1

Find the child's active policy

GET /children/{childID}/policies lists a child’s policies. Grab the active one — that is the policy enforcement reads from.
curl -s "$PHOSRA_BASE/api/v1/children/$CHILD_ID/policies"
[
  {
    "id": "a3147895-b752-4d03-846c-35fd8fb4e77c",
    "child_id": "acf4ce5d-d452-47b8-a78b-3b4ca86f50be",
    "name": "Ava's Policy",
    "status": "active",
    "version": 20
  }
]
export POLICY_ID="a3147895-b752-4d03-846c-35fd8fb4e77c"
2

Locate the rule to change

GET /policies/{policyID}/rules lists every rule on the policy. Find the time_daily_limit rule — it is currently 180 minutes — and keep its id.
curl -s "$PHOSRA_BASE/api/v1/policies/$POLICY_ID/rules"
{
  "id": "1a1e8d7d-e2fe-463b-9952-e560e5adf577",
  "policy_id": "a3147895-b752-4d03-846c-35fd8fb4e77c",
  "category": "time_daily_limit",
  "enabled": true,
  "config": { "daily_minutes": 180 }
}
export RULE_ID="1a1e8d7d-e2fe-463b-9952-e560e5adf577"
3

Tighten the limit

PUT /rules/{ruleID} updates a single rule in place. Drop the daily limit from 180 to 120 minutes.
curl -s -X PUT "$PHOSRA_BASE/api/v1/rules/$RULE_ID" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true, "config": { "daily_minutes": 120 } }'
{
  "id": "1a1e8d7d-e2fe-463b-9952-e560e5adf577",
  "category": "time_daily_limit",
  "enabled": true,
  "config": { "daily_minutes": 120 },
  "updated_at": "2026-07-06T08:45:54.003285438Z"
}
The policy is now updated, but nothing has reached the console yet. Editing a rule changes the policy; it does not touch the platform until you enforce.
4

Enforce the policy

POST /children/{childID}/enforce pushes the child’s current active policy to every platform the family has connected. It returns an enforcement job that runs asynchronously.
curl -s -X POST "$PHOSRA_BASE/api/v1/children/$CHILD_ID/enforce" \
  -H "Content-Type: application/json" -d '{}'
{
  "id": "67c763ff-efc9-4e27-99c7-fffe0fbac020",
  "child_id": "acf4ce5d-d452-47b8-a78b-3b4ca86f50be",
  "policy_id": "a3147895-b752-4d03-846c-35fd8fb4e77c",
  "trigger_type": "manual",
  "status": "running",
  "started_at": "2026-07-06T08:45:54.171964Z"
}
export JOB_ID="67c763ff-efc9-4e27-99c7-fffe0fbac020"
5

Poll the job to completion

GET /enforcement/jobs/{jobID} returns the job’s status. In the sandbox it finishes in well under a second; poll until status is completed.
curl -s "$PHOSRA_BASE/api/v1/enforcement/jobs/$JOB_ID"
{
  "id": "67c763ff-efc9-4e27-99c7-fffe0fbac020",
  "status": "completed",
  "started_at": "2026-07-06T08:45:54.171964Z",
  "completed_at": "2026-07-06T08:45:54.315948Z"
}
6

Read the per-platform report

GET /enforcement/jobs/{jobID}/results returns one result per connected platform. This is where you learn exactly what landed: Xbox applied 5 rules, guided several more, and flagged the categories a console can’t enforce.
curl -s "$PHOSRA_BASE/api/v1/enforcement/jobs/$JOB_ID/results"
[
  {
    "platform_id": "xbox",
    "status": "completed",
    "rules_applied": 5,
    "rules_skipped": 0,
    "rules_failed": 0,
    "details": {
      "profile_name": "Ava's Xbox Profile",
      "enforcement_mode": "manual_attested",
      "time_daily_limit": { "status": "guided", "limit_display": "2h 0m", "limit_minutes": 120 },
      "content_rating":   { "status": "guided", "max_rating": "PG-13", "blocked_above": ["R", "NC-17"] },
      "notification_curfew": { "status": "applied", "start": "22:00", "end": "06:00" },
      "monitoring_activity": { "status": "applied", "handler": "phosra_analytics" },
      "unenforceable_categories": [
        "addictive_design_control", "algo_feed_control", "privacy_profile_visibility",
        "targeted_ad_block", "web_filter_level", "web_safesearch"
      ]
    }
  }
]
The new 120-minute limit is right there in time_daily_limit.limit_minutes — your rule change made it to the platform. Read the three statuses this way:
StatusMeaning
appliedPhosra enforced the rule directly on the platform (e.g. curfew, monitoring).
guidedThe platform accepts the setting but enforces it itself; Phosra configured and attested it (e.g. the console’s own screen-time and rating gate).
unenforceable_categoriesRules this platform has no surface for — a console can’t run a web-content filter. They are reported, never silently dropped.
A game console owns its own screen-time and content-rating controls. Phosra sets them and records a signed attestation (enforcement_mode: manual_attested) rather than pretending it flipped a switch it doesn’t own. A DNS filter or MDM profile, by contrast, returns applied for the categories it enforces directly. The result report never conflates the two — you always know who is holding the line.

The whole flow at a glance

#StepCallLive result
1Find active policyGET /children/{id}/policiespolicy a3147895…, active
2Locate the ruleGET /policies/{id}/rulestime_daily_limit = 180 min
3Tighten itPUT /rules/{id}120 min
4EnforcePOST /children/{id}/enforcejob 67c763ff…, running
5PollGET /enforcement/jobs/{id}completed in ~0.14s
6Read resultsGET /enforcement/jobs/{id}/resultsXbox: 5 applied, 120-min limit guided

Next steps

Onboard a multi-child household

Where the child and policy in this recipe came from — two kids, two age-tuned policies.

Enforcement, explained

What applied, guided, and unenforceable mean per platform, and how attestation works.

Policies & rules

The full rule catalog and config shapes behind time_daily_limit and friends.

Retry a failed job

When a platform is offline mid-enforce, re-run just the failed results.