{"components":{"schemas":{"APNsPushPayload":{"description":"Silent push notification sent to iOS devices when a child's policy is updated. Delivered via APNs HTTP/2 with push-type=background and priority=5.","example":{"aps":{"content-available":1},"phosra":{"event":"policy.updated","version":3}},"properties":{"aps":{"properties":{"content-available":{"description":"Always 1 -- marks as silent/background push","enum":[1],"type":"integer"}},"type":"object"},"phosra":{"properties":{"event":{"description":"Event type -- currently always policy.updated","enum":["policy.updated"],"type":"string"},"version":{"description":"New policy version number. Compare with cached version to decide whether to call GET /device/policy.","type":"integer"}},"type":"object"}},"type":"object"},"AgeRatings":{"properties":{"age":{"type":"integer"},"ratings":{"additionalProperties":{"$ref":"#/components/schemas/Rating"},"type":"object"}},"type":"object"},"AndroidCategoryFrameworkMapping":{"description":"Maps a Phosra rule category to the Android framework and API class needed to enforce it","properties":{"api_class":{"description":"Specific API class or method (e.g. setPackagesSuspended, queryUsageStats, onAccessibilityEvent)","type":"string"},"framework":{"description":"Android framework: DevicePolicyManager, UsageStatsManager, VpnService, AccessibilityService, NotificationListenerService, PackageManager, ContentResolver, AlarmManager, NotificationManager, or none","type":"string"},"min_sdk":{"description":"Minimum Android SDK version required (e.g. 26 for Android 8.0, 28 for Android 9.0)","type":"integer"},"notes":{"description":"Implementation hints for the Android app","type":"string"}},"type":"object"},"AndroidPlatformMappings":{"properties":{"age_ratings":{"additionalProperties":{"type":"string"},"type":"object"},"always_allowed":{"items":{"type":"string"},"type":"array"},"app_categories":{"additionalProperties":{"properties":{"package_names":{"items":{"type":"string"},"type":"array"},"play_store_category":{"type":"string"}},"type":"object"},"type":"object"},"category_frameworks":{"additionalProperties":{"$ref":"#/components/schemas/AndroidCategoryFrameworkMapping"},"description":"Phosra rule category -\u003e Android framework mapping for on-device enforcement","type":"object"},"system_apps":{"additionalProperties":{"type":"string"},"type":"object"}},"type":"object"},"ApplePlatformMappings":{"properties":{"age_ratings":{"additionalProperties":{"type":"string"},"type":"object"},"always_allowed":{"items":{"type":"string"},"type":"array"},"app_categories":{"additionalProperties":{"properties":{"app_store_category":{"type":"string"},"bundle_ids":{"items":{"type":"string"},"type":"array"}},"type":"object"},"type":"object"},"category_frameworks":{"additionalProperties":{"$ref":"#/components/schemas/CategoryFrameworkMapping"},"description":"Phosra rule category -\u003e Apple framework mapping for on-device enforcement","type":"object"},"system_apps":{"additionalProperties":{"type":"string"},"type":"object"}},"type":"object"},"AvailableSource":{"properties":{"api_tier":{"enum":["managed","guided"],"type":"string"},"auth_type":{"type":"string"},"description":{"type":"string"},"display_name":{"type":"string"},"slug":{"type":"string"},"website":{"type":"string"}},"type":"object"},"CategoryEnforcementResult":{"properties":{"category":{"description":"Phosra rule category (e.g. content_rating, time_daily_limit)","type":"string"},"detail":{"description":"Optional error message or implementation note","type":"string"},"framework":{"description":"Apple framework used (ManagedSettings, DeviceActivity, FamilyControls, none)","type":"string"},"status":{"description":"Enforcement outcome on this device","enum":["enforced","partial","failed","unsupported"],"type":"string"}},"type":"object"},"CategoryFrameworkMapping":{"description":"Maps a Phosra rule category to the Apple framework and API class needed to enforce it","properties":{"api_class":{"description":"Specific API class (e.g. ManagedSettingsStore.webContent, DeviceActivitySchedule)","type":"string"},"framework":{"description":"Apple framework: ManagedSettings, DeviceActivity, FamilyControls, or none","type":"string"},"min_os":{"description":"Minimum iOS version required (e.g. 16.0)","type":"string"},"notes":{"description":"Implementation hints for the iOS app","type":"string"}},"type":"object"},"Child":{"properties":{"avatar_url":{"type":"string"},"birth_date":{"format":"date","type":"string"},"created_at":{"format":"date-time","type":"string"},"family_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"}},"type":"object"},"ChildPolicy":{"properties":{"child_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"priority":{"type":"integer"},"status":{"enum":["active","paused","draft"],"type":"string"},"version":{"type":"integer"}},"type":"object"},"CompiledPolicy":{"properties":{"age_group":{"type":"string"},"child_age":{"type":"integer"},"child_id":{"format":"uuid","type":"string"},"content_filter":{"properties":{"age_rating":{"type":"string"},"allowed_apps":{"items":{"type":"string"},"type":"array"},"allowlist_mode":{"type":"boolean"},"blocked_apps":{"items":{"type":"string"},"type":"array"},"max_ratings":{"additionalProperties":{"type":"string"},"type":"object"}},"type":"object"},"generated_at":{"format":"date-time","type":"string"},"notifications":{"properties":{"curfew_end":{"type":"string"},"curfew_start":{"type":"string"},"usage_timer_minutes":{"type":"integer"}},"type":"object"},"policy_id":{"format":"uuid","type":"string"},"privacy":{"properties":{"account_creation_approval":{"type":"boolean"},"data_sharing_restricted":{"type":"boolean"},"location_sharing_enabled":{"type":"boolean"},"profile_visibility":{"type":"string"}},"type":"object"},"purchases":{"properties":{"block_iap":{"type":"boolean"},"require_approval":{"type":"boolean"},"spending_cap_usd":{"type":"number"}},"type":"object"},"screen_time":{"properties":{"always_allowed_apps":{"items":{"type":"string"},"type":"array"},"daily_limit_minutes":{"type":"integer"},"downtime_windows":{"items":{"properties":{"days_of_week":{"items":{"type":"string"},"type":"array"},"end_time":{"type":"string"},"start_time":{"type":"string"}},"type":"object"},"type":"array"},"per_app_limits":{"items":{"properties":{"bundle_id":{"type":"string"},"daily_minutes":{"type":"integer"}},"type":"object"},"type":"array"},"schedule":{"properties":{"weekday":{"properties":{"end":{"type":"string"},"start":{"type":"string"}},"type":"object"},"weekend":{"properties":{"end":{"type":"string"},"start":{"type":"string"}},"type":"object"}},"type":"object"}},"type":"object"},"social":{"properties":{"chat_mode":{"type":"string"},"dm_restriction":{"type":"string"},"multiplayer_mode":{"type":"string"}},"type":"object"},"status":{"type":"string"},"version":{"type":"integer"},"web_filter":{"properties":{"allowed_domains":{"items":{"type":"string"},"type":"array"},"blocked_categories":{"items":{"type":"string"},"type":"array"},"blocked_domains":{"items":{"type":"string"},"type":"array"},"level":{"type":"string"},"safe_search":{"type":"boolean"}},"type":"object"}},"type":"object"},"ComplianceLink":{"properties":{"external_id":{"type":"string"},"family_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"last_enforcement_at":{"format":"date-time","nullable":true,"type":"string"},"last_enforcement_status":{"type":"string"},"platform_id":{"type":"string"},"status":{"enum":["verified","unverified","error"],"type":"string"},"verified_at":{"format":"date-time","type":"string"}},"type":"object"},"ContentDescriptor":{"properties":{"category":{"type":"string"},"code":{"type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"system_id":{"type":"string"}},"type":"object"},"DeviceRegistration":{"properties":{"apns_token":{"nullable":true,"type":"string"},"app_version":{"type":"string"},"capabilities":{"description":"Apple frameworks the device supports (e.g. FamilyControls, ManagedSettings, DeviceActivity)","items":{"type":"string"},"type":"array"},"child_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"device_model":{"type":"string"},"device_name":{"type":"string"},"enforcement_summary":{"description":"Per-category enforcement results from the device's last enforcement_status report","type":"object"},"family_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"last_policy_version":{"type":"integer"},"last_seen_at":{"format":"date-time","nullable":true,"type":"string"},"os_version":{"type":"string"},"platform_id":{"type":"string"},"status":{"enum":["active","inactive","revoked"],"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"type":"object"},"DeviceReportRequest":{"properties":{"payload":{"description":"Report data (varies by report_type). For enforcement_status see EnforcementStatusPayload.","type":"object"},"report_type":{"enum":["screen_time","app_usage","web_activity","blocked_attempt","enforcement_status"],"type":"string"},"reported_at":{"format":"date-time","type":"string"}},"required":["report_type","payload","reported_at"],"type":"object"},"EnforcementJob":{"properties":{"child_id":{"format":"uuid","type":"string"},"completed_at":{"format":"date-time","nullable":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"policy_id":{"format":"uuid","type":"string"},"started_at":{"format":"date-time","nullable":true,"type":"string"},"status":{"enum":["pending","running","completed","failed","partial"],"type":"string"},"trigger_type":{"enum":["manual","auto","webhook"],"type":"string"}},"type":"object"},"EnforcementResult":{"properties":{"completed_at":{"format":"date-time","nullable":true,"type":"string"},"compliance_link_id":{"format":"uuid","type":"string"},"details":{"type":"object"},"enforcement_job_id":{"format":"uuid","type":"string"},"error_message":{"nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"platform_id":{"type":"string"},"rules_applied":{"type":"integer"},"rules_failed":{"type":"integer"},"rules_skipped":{"type":"integer"},"started_at":{"format":"date-time","nullable":true,"type":"string"},"status":{"enum":["pending","running","completed","failed","partial"],"type":"string"}},"type":"object"},"EnforcementStatusPayload":{"description":"Payload for report_type: enforcement_status -- per-category enforcement results","properties":{"policy_version":{"description":"The policy version these results correspond to","type":"integer"},"results":{"items":{"$ref":"#/components/schemas/CategoryEnforcementResult"},"type":"array"}},"type":"object"},"Error":{"properties":{"code":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}},"type":"object"},"Family":{"properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"}},"type":"object"},"FamilyMember":{"properties":{"family_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"joined_at":{"format":"date-time","type":"string"},"role":{"enum":["owner","parent","guardian"],"type":"string"},"user_id":{"format":"uuid","type":"string"}},"type":"object"},"FamilyOverview":{"properties":{"children":{"items":{"properties":{"active_policies":{"type":"integer"},"child":{"$ref":"#/components/schemas/Child"},"enforcement_status":{"type":"string"},"last_enforcement_at":{"format":"date-time","nullable":true,"type":"string"}},"type":"object"},"type":"array"},"enforcement_health":{"enum":["healthy","warning","error"],"type":"string"},"recent_enforcements":{"items":{"$ref":"#/components/schemas/EnforcementJob"},"type":"array"},"total_platforms":{"type":"integer"}},"type":"object"},"GuidedStep":{"properties":{"deep_link":{"type":"string"},"description":{"type":"string"},"image_url":{"type":"string"},"step_number":{"type":"integer"},"title":{"type":"string"}},"type":"object"},"Platform":{"properties":{"auth_type":{"enum":["api_key","oauth2","manual"],"type":"string"},"category":{"enum":["dns","streaming","gaming","device","browser"],"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"enforcement_mode":{"description":"How Phosra enforces rules on this platform. `dns` = live DNS-provider API write (NextDNS/CleanBrowsing/Control D); `device` = on-device enforcement, requires Phosra app installed; `api_write` = live third-party write API; `manual_attested` = no programmatic API — Phosra generates setup steps and records a signed parent attestation. Never render \"Enforced\" for this value; `coming_soon` = adapter not yet built.\n","enum":["dns","device","api_write","manual_attested","coming_soon"],"type":"string"},"icon_url":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"rule_support":{"additionalProperties":{"enum":["dns","device","guided","unsupported"],"type":"string"},"description":"Per-category enforcement support across all 123 OCSS rule categories. `dns` = DNS provider can filter this category; `device` = on-device enforcement handles it; `guided` = manual_attested platform, parent applies with Phosra guidance; `unsupported` = not addressable on this platform.\n","type":"object"},"tier":{"enum":["compliant","provisional","pending"],"type":"string"}},"type":"object"},"PolicyRule":{"properties":{"category":{"$ref":"#/components/schemas/RuleCategory"},"config":{"type":"object"},"created_at":{"format":"date-time","type":"string"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"policy_id":{"format":"uuid","type":"string"}},"type":"object"},"ProviderConnection":{"properties":{"connected_at":{"format":"date-time","type":"string"},"family_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"last_sync_at":{"format":"date-time","type":"string"},"last_sync_status":{"type":"string"},"provider_id":{"type":"string"},"status":{"enum":["connected","disconnected","error"],"type":"string"}},"type":"object"},"QuickSetupRequest":{"properties":{"birth_date":{"description":"Child's birth date in YYYY-MM-DD format","format":"date","type":"string"},"child_name":{"description":"The child's display name","type":"string"},"family_id":{"description":"Optional existing family UUID to add the child to","format":"uuid","type":"string"},"family_name":{"description":"Optional name for a new family (defaults to '{child_name}'s Family')","type":"string"},"strictness":{"description":"Strictness preset (default: recommended)","enum":["recommended","strict","relaxed"],"type":"string"}},"required":["child_name","birth_date"],"type":"object"},"QuickSetupResponse":{"properties":{"age_group":{"type":"string"},"child":{"$ref":"#/components/schemas/Child"},"family":{"$ref":"#/components/schemas/Family"},"max_ratings":{"additionalProperties":{"type":"string"},"type":"object"},"policy":{"$ref":"#/components/schemas/ChildPolicy"},"rule_summary":{"$ref":"#/components/schemas/RuleSummary"},"rules":{"items":{"$ref":"#/components/schemas/PolicyRule"},"type":"array"}},"type":"object"},"Rating":{"properties":{"code":{"type":"string"},"description":{"type":"string"},"display_order":{"type":"integer"},"id":{"format":"uuid","type":"string"},"min_age":{"type":"integer"},"name":{"type":"string"},"restrictive_idx":{"type":"integer"},"system_id":{"type":"string"}},"type":"object"},"RatingEquivalence":{"properties":{"id":{"format":"uuid","type":"string"},"rating_a":{"format":"uuid","type":"string"},"rating_b":{"format":"uuid","type":"string"},"strength":{"description":"0-1, how equivalent the ratings are","type":"number"}},"type":"object"},"RatingSystem":{"properties":{"country":{"type":"string"},"description":{"type":"string"},"id":{"type":"string"},"media_type":{"type":"string"},"name":{"type":"string"}},"type":"object"},"RegisterDeviceRequest":{"properties":{"apns_token":{"type":"string"},"app_version":{"type":"string"},"capabilities":{"description":"Apple frameworks the device supports (e.g. FamilyControls, ManagedSettings, DeviceActivity)","items":{"type":"string"},"type":"array"},"device_model":{"type":"string"},"device_name":{"type":"string"},"os_version":{"type":"string"}},"required":["device_name","device_model","os_version","app_version"],"type":"object"},"RegisterDeviceResponse":{"properties":{"api_key":{"description":"One-time device API key (store in Keychain)","type":"string"},"device":{"$ref":"#/components/schemas/DeviceRegistration"}},"type":"object"},"RuleCategory":{"description":"One of the 123 OCSS rule categories (accepted and signed by the census; capability varies — see the Rule Categories reference).","enum":["rating_age_gate","age_gate","adult_site_av_required","app_store_age_attestation","os_age_signal_ingest","age_signal_broadcast","hard_id_verification_escalation","social_media_min_age","ai_chatbot_age_assertion","teen_minimum_age_16_gate","content_rating","violence_threshold","sexual_content_threshold","profanity_threshold","commercial_pressure_threshold","substance_content_threshold","recognized_seal_allowlist","social_chat_control","web_filter_level","privacy_rating_tier_gate","privacy_rating_score_threshold","privacy_rating_dimension_gate","privacy_seal_required","unrated_privacy_default","ai_chatbot_tier_gate","ai_product_classification_gate","ai_safety_rating_threshold","ai_data_responsibility_rating_threshold","ai_transparency_rating_threshold","ai_minor_interaction","streaming_age_rating_enforce","ugc_world_age_gate","time_daily_limit","time_scheduled_hours","time_per_app_limit","time_downtime","phone_free_school_hours","purchase_approval","purchase_spending_cap","social_contacts","social_multiplayer","stranger_outreach_friction","privacy_account_creation","parental_consent_gate","notification_curfew","dumbphone_migration_mode","content_block_title","content_allow_title","content_allowlist_mode","content_descriptor_block","firearm_content_block","not_for_minors_block","purchase_block_iap","stranger_dm_block","web_safesearch","web_category_block","web_custom_allowlist","web_custom_blocklist","dm_restriction","ai_no_simulated_companionship","ai_no_unsupervised_therapy","ai_no_self_harm_promotion","ai_no_csam_generation","ai_no_personhood_deception","ai_companion_block","library_filter_compliance","gambling_mechanics_block","addictive_pattern_block","app_install_block","voice_chat_minor_limit","educational_credit","monitoring_activity","monitoring_alerts","social_account_content_monitoring","message_content_monitoring","image_content_monitoring","search_query_monitoring","flagged_content_digest","usage_timer_notification","parental_event_notification","screen_time_report","social_media_warning_label_render","digital_literacy_curriculum_link","school_alert_routing","privacy_location","privacy_profile_visibility","privacy_data_sharing","data_minimization_enforce","third_party_tracker_block","geolocation_precision_cap","targeted_ad_block","data_deletion_request","geolocation_opt_in","ai_training_data_opt_out","image_rights_minor","student_privacy_school_mode","commercial_data_ban","prosocial_weight","role_model_weight","representation_weight","dpia_request_generator","algo_feed_control","addictive_design_control","infinite_scroll_block","autoplay_disable","ai_no_engagement_dark_patterns","ai_synthetic_media_disclosure","algorithmic_audit","cipa_filter_attestation","minor_data_sale_audit_log","parental_attestation_cert","ai_toy_safety_cert","csam_reporting","esafety_commission_reporting","platform_liability_evidence_capture","age_appropriate_profile_mode","ncii_takedown","ephemeral_message_disable","dm_feature_disable","named_contact_block","crisis_resource_surface","presence_status_hide","harm_report_intake"],"type":"string"},"RuleSummary":{"properties":{"bedtime_hour":{"type":"integer"},"content_rating":{"type":"string"},"screen_time_minutes":{"type":"integer"},"total_rules_enabled":{"type":"integer"},"web_filter_level":{"type":"string"}},"type":"object"},"Source":{"properties":{"api_tier":{"enum":["managed","guided"],"type":"string"},"auto_sync":{"type":"boolean"},"capabilities":{"items":{"$ref":"#/components/schemas/SourceCapability"},"type":"array"},"child_id":{"format":"uuid","type":"string"},"config":{"type":"object"},"created_at":{"format":"date-time","type":"string"},"display_name":{"type":"string"},"error_message":{"nullable":true,"type":"string"},"family_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"last_sync_at":{"format":"date-time","nullable":true,"type":"string"},"last_sync_status":{"nullable":true,"type":"string"},"source_slug":{"type":"string"},"status":{"enum":["pending","connected","syncing","error","disconnected"],"type":"string"},"sync_version":{"type":"integer"},"updated_at":{"format":"date-time","type":"string"}},"type":"object"},"SourceCapability":{"properties":{"category":{"$ref":"#/components/schemas/RuleCategory"},"notes":{"type":"string"},"read_write":{"enum":["push_only","pull_only","bidirectional"],"type":"string"},"support_level":{"enum":["full","partial","none"],"type":"string"}},"type":"object"},"SourceSyncJob":{"properties":{"completed_at":{"format":"date-time","nullable":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"error_message":{"nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"rules_failed":{"type":"integer"},"rules_pushed":{"type":"integer"},"rules_skipped":{"type":"integer"},"source_id":{"format":"uuid","type":"string"},"started_at":{"format":"date-time","nullable":true,"type":"string"},"status":{"enum":["pending","running","completed","failed","partial"],"type":"string"},"sync_mode":{"enum":["full","incremental","single_rule"],"type":"string"},"trigger_type":{"enum":["manual","auto","webhook","policy_change"],"type":"string"}},"type":"object"},"SourceSyncResult":{"properties":{"created_at":{"format":"date-time","type":"string"},"error_message":{"nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"job_id":{"format":"uuid","type":"string"},"rule_category":{"$ref":"#/components/schemas/RuleCategory"},"source_id":{"format":"uuid","type":"string"},"source_response":{"type":"object"},"source_value":{"type":"object"},"status":{"enum":["pushed","skipped","failed","unsupported"],"type":"string"}},"type":"object"},"Standard":{"properties":{"adoption_count":{"type":"integer"},"created_at":{"format":"date-time","type":"string"},"description":{"type":"string"},"icon_url":{"type":"string"},"id":{"format":"uuid","type":"string"},"long_description":{"type":"string"},"max_age":{"nullable":true,"type":"integer"},"min_age":{"nullable":true,"type":"integer"},"name":{"type":"string"},"organization":{"type":"string"},"published":{"type":"boolean"},"rules":{"items":{"$ref":"#/components/schemas/StandardRule"},"type":"array"},"slug":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"version":{"type":"string"}},"type":"object"},"StandardAdoption":{"properties":{"adopted_at":{"format":"date-time","type":"string"},"child_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"standard_id":{"format":"uuid","type":"string"}},"type":"object"},"StandardRule":{"properties":{"category":{"$ref":"#/components/schemas/RuleCategory"},"config":{"type":"object"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"label":{"type":"string"},"sort_order":{"type":"integer"},"standard_id":{"format":"uuid","type":"string"}},"type":"object"},"SyncJob":{"properties":{"child_id":{"format":"uuid","type":"string"},"completed_at":{"format":"date-time","type":"string"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"policy_id":{"format":"uuid","type":"string"},"started_at":{"format":"date-time","type":"string"},"status":{"enum":["pending","running","completed","failed","partial"],"type":"string"},"trigger_type":{"enum":["manual","auto","webhook"],"type":"string"}},"type":"object"},"SyncJobResult":{"properties":{"details":{"type":"object"},"error_message":{"type":"string"},"id":{"format":"uuid","type":"string"},"provider_id":{"type":"string"},"rules_applied":{"type":"integer"},"rules_failed":{"type":"integer"},"rules_skipped":{"type":"integer"},"status":{"type":"string"},"sync_job_id":{"format":"uuid","type":"string"}},"type":"object"},"TokenPair":{"properties":{"access_token":{"type":"string"},"expires_at":{"format":"date-time","type":"string"},"refresh_token":{"type":"string"}},"type":"object"},"UIFeedback":{"properties":{"click_x":{"nullable":true,"type":"integer"},"click_y":{"nullable":true,"type":"integer"},"comment":{"type":"string"},"component_hint":{"nullable":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"css_selector":{"type":"string"},"id":{"format":"uuid","type":"string"},"page_route":{"type":"string"},"resolved_at":{"format":"date-time","nullable":true,"type":"string"},"reviewer_name":{"type":"string"},"status":{"enum":["open","approved","dismissed","fixed"],"type":"string"},"viewport_height":{"nullable":true,"type":"integer"},"viewport_width":{"nullable":true,"type":"integer"}},"type":"object"},"UpdateDeviceRequest":{"properties":{"apns_token":{"type":"string"},"app_version":{"type":"string"},"device_name":{"type":"string"},"os_version":{"type":"string"}},"type":"object"},"User":{"properties":{"created_at":{"format":"date-time","type":"string"},"email":{"format":"email","type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"}},"type":"object"},"Webhook":{"properties":{"active":{"type":"boolean"},"created_at":{"format":"date-time","type":"string"},"events":{"items":{"type":"string"},"type":"array"},"family_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"},"url":{"type":"string"}},"type":"object"},"WebhookDelivery":{"properties":{"attempts":{"type":"integer"},"created_at":{"format":"date-time","type":"string"},"event":{"type":"string"},"id":{"format":"uuid","type":"string"},"next_retry_at":{"format":"date-time","nullable":true,"type":"string"},"payload":{"type":"object"},"response_code":{"nullable":true,"type":"integer"},"success":{"type":"boolean"},"webhook_id":{"format":"uuid","type":"string"}},"type":"object"}},"securitySchemes":{"BearerAuth":{"bearerFormat":"JWT","scheme":"bearer","type":"http"},"DeviceKeyAuth":{"description":"Device API key issued at registration (stored in iOS Keychain)","in":"header","name":"X-Device-Key","type":"apiKey"},"OCSSSignature":{"description":"RFC 9421 HTTP Message Signature (Signature + Signature-Input headers) under the caller's OCSS Trust List signing key (keyid `did:ocss:\u003cslug\u003e#\u003ckid\u003e`). Required on the OCSS census enforcement-endpoint operations; a request without a verifiable signature answers 401 `signature_invalid`.","in":"header","name":"Signature","type":"apiKey"}}},"info":{"contact":{"name":"Phosra"},"description":"Universal Parental Controls API - Define once, push everywhere","title":"Phosra API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/advisors/self-register":{"post":{"description":"Self-service, permissionless registration of a `did:ocss:\u003cslug\u003e` onto the sandbox Trust List — the first step a platform (or any counterparty) takes to become discoverable and to sign census calls. SANDBOX ONLY: rejected `403 self_register_sandbox_only` unless `SANDBOX_MODE=true`, so production censuses never accept self-registered DIDs. The inserted entry is ALWAYS `trust_tier: \"provisional\"` — no self-service path can escalate to verified/accredited (those stay attestation- and admin-gated). Re-using an existing slug is rejected `409` (self-register can never shadow a seeded/accredited entry). On success the new DID appears in the served `/.well-known/ocss/trust-list` on the next (synchronously triggered) recompile.","operationId":"selfRegisterAdvisor","requestBody":{"content":{"application/json":{"schema":{"properties":{"did":{"description":"The self-minted `did:ocss:\u003cslug\u003e` to register.","example":"did:ocss:my-gatekeeper","type":"string"},"entry_role":{"description":"Optional Trust-List role marker. Bounded to `verifying-agency` (the §5.4 open assessor market) — any other value is `400`; `classifier-accredited` stays attestation-gated.","enum":["verifying-agency"],"type":"string"},"public_key_b64url":{"description":"The Ed25519 PUBLIC key, raw 32 bytes, base64url (no padding). This is the key census calls are RFC-9421-verified against.","example":"nQo4udngSdhaOUlNDO6BcE-7i2AeNzcDbyBGM3YB8R0","type":"string"},"roles":{"description":"Informational capability-domain tags stored verbatim on the provisional entry.","example":["enforcement-agent"],"items":{"type":"string"},"type":"array"}},"required":["did","public_key_b64url"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"did":{"type":"string"},"entry_role":{"description":"Present only when an `entry_role` was requested and recorded.","type":"string"},"published_key_x":{"description":"The registered public key (base64url), echoed back.","type":"string"},"trust_tier":{"enum":["provisional"],"type":"string"}},"type":"object"}}},"description":"Registered as a provisional Trust-List entry."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing/invalid `did`, malformed `public_key_b64url` (must decode to 32 bytes), or an out-of-bounds `entry_role`."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing/invalid bearer session (group auth)."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"`self_register_sandbox_only` — `SANDBOX_MODE` is not `true`."},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"`did_already_registered` — the slug already exists on the Trust List."}},"security":[{"BearerAuth":[]}],"summary":"Permissionless self-register a DID onto the sandbox Trust List (SANDBOX only)","tags":["OCSS Enforcement Endpoints"]}},"/auth/logout":{"post":{"responses":{"204":{"description":"Logged out"}},"summary":"Logout (no-op — session revocation is managed by WorkOS)","tags":["Auth"]}},"/auth/me":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"description":"Current user"}},"summary":"Get current user","tags":["Auth"]}},"/children/{childID}":{"delete":{"responses":{"204":{"description":"Deleted"}},"summary":"Delete child","tags":["Children"]},"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Child"}}},"description":"Child details"},"404":{"description":"Child not found"}},"summary":"Get child","tags":["Children"]},"parameters":[{"in":"path","name":"childID","required":true,"schema":{"format":"uuid","type":"string"}}],"put":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"birth_date":{"format":"date","type":"string"},"name":{"type":"string"}},"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Child"}}},"description":"Updated"}},"summary":"Update child","tags":["Children"]}},"/children/{childID}/age-ratings":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgeRatings"}}},"description":"Age ratings"}},"summary":"Get age-appropriate ratings for child","tags":["Children"]},"parameters":[{"in":"path","name":"childID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/children/{childID}/devices":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DeviceRegistration"},"type":"array"}}},"description":"Device list"}},"summary":"List registered devices for a child","tags":["Apple Device Sync"]},"parameters":[{"in":"path","name":"childID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Register a device for on-device policy enforcement. Returns a one-time API key the iOS app stores in Keychain.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeviceRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeviceResponse"}}},"description":"Device registered"},"404":{"description":"Child not found"}},"summary":"Register an Apple device","tags":["Apple Device Sync"]}},"/children/{childID}/enforce":{"parameters":[{"in":"path","name":"childID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Push the child's active policy rules to connected platforms. If platform_ids is provided, only those platforms are targeted; otherwise enforcement fans out to ALL connected platforms.","requestBody":{"content":{"application/json":{"schema":{"properties":{"platform_ids":{"description":"Optional list of platform IDs to target. If omitted, pushes to ALL connected platforms.","items":{"type":"string"},"type":"array"}},"type":"object"}}}},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnforcementJob"}}},"description":"Enforcement job started"}},"summary":"Trigger enforcement for a child","tags":["Enforcement"]}},"/children/{childID}/enforcement/jobs":{"get":{"parameters":[{"description":"Maximum number of jobs to return (default 20)","in":"query","name":"limit","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/EnforcementJob"},"type":"array"}}},"description":"Enforcement job list"}},"summary":"List enforcement jobs for a child","tags":["Enforcement"]},"parameters":[{"in":"path","name":"childID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/children/{childID}/policies":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ChildPolicy"},"type":"array"}}},"description":"Policy list"}},"summary":"List child's policies","tags":["Policies"]},"parameters":[{"in":"path","name":"childID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Create a new policy for a child. Policies start in 'draft' status.","requestBody":{"content":{"application/json":{"schema":{"properties":{"name":{"type":"string"}},"required":["name"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChildPolicy"}}},"description":"Policy created"}},"summary":"Create policy for child","tags":["Policies"]}},"/children/{childID}/sources":{"get":{"description":"List all connected sources for a child.","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Source"},"type":"array"}}},"description":"Source list"}},"summary":"List sources for child","tags":["Sources"]},"parameters":[{"in":"path","name":"childID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/children/{childID}/standards":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Standard"},"type":"array"}}},"description":"Adopted standards list"}},"summary":"List standards adopted by a child","tags":["Standards"]},"parameters":[{"in":"path","name":"childID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Apply a community standard's rules to a child's policy.","requestBody":{"content":{"application/json":{"schema":{"properties":{"standard_id":{"format":"uuid","type":"string"}},"required":["standard_id"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StandardAdoption"}}},"description":"Standard adopted"}},"summary":"Adopt a standard for a child","tags":["Standards"]}},"/children/{childID}/standards/{standardID}":{"delete":{"responses":{"204":{"description":"Standard unadopted"},"404":{"description":"Standard or child not found"}},"summary":"Unadopt a standard for a child","tags":["Standards"]},"parameters":[{"in":"path","name":"childID","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"standardID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/children/{childID}/sync":{"post":{"parameters":[{"in":"path","name":"childID","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncJob"}}},"description":"Sync triggered"}},"summary":"Trigger sync for child across all providers","tags":["Sync"]}},"/compliance":{"post":{"description":"Connect a platform to the family by providing credentials for verification.","requestBody":{"content":{"application/json":{"schema":{"properties":{"credentials":{"type":"string"},"family_id":{"format":"uuid","type":"string"},"platform_id":{"type":"string"}},"required":["family_id","platform_id","credentials"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComplianceLink"}}},"description":"Platform connected"}},"summary":"Connect a platform","tags":["Compliance"]}},"/compliance/{linkID}":{"delete":{"responses":{"204":{"description":"Platform disconnected"},"404":{"description":"Compliance link not found"}},"summary":"Disconnect a platform","tags":["Compliance"]},"parameters":[{"in":"path","name":"linkID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/compliance/{linkID}/enforce":{"parameters":[{"in":"path","name":"linkID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Push the active policy rules to the platform associated with this compliance link.","responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnforcementJob"}}},"description":"Enforcement job started"},"404":{"description":"Compliance link not found"}},"summary":"Trigger enforcement for a specific compliance link","tags":["Compliance"]}},"/compliance/{linkID}/verify":{"parameters":[{"in":"path","name":"linkID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Test that credentials still work for an existing platform connection.","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"status":{"type":"string"}},"type":"object"}}},"description":"Verification result"},"502":{"description":"Verification failed"}},"summary":"Re-verify a platform connection","tags":["Compliance"]}},"/connections":{"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"credentials":{"type":"string"},"family_id":{"format":"uuid","type":"string"},"provider_id":{"type":"string"}},"required":["family_id","provider_id","credentials"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderConnection"}}},"description":"Connected"}},"summary":"Connect to a provider","tags":["Connections"]}},"/device/ack":{"post":{"description":"Confirm that the device has successfully applied a specific policy version.","requestBody":{"content":{"application/json":{"schema":{"properties":{"version":{"type":"integer"}},"required":["version"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"acknowledged_version":{"type":"integer"}},"type":"object"}}},"description":"Version acknowledged"},"401":{"description":"Invalid or revoked device key"}},"security":[{"DeviceKeyAuth":[]}],"summary":"Acknowledge policy version","tags":["Apple Device Sync"]}},"/device/policy":{"get":{"description":"Returns a structured policy document the iOS app interprets to configure FamilyControls/ManagedSettings. Supports conditional polling via since_version query parameter.","parameters":[{"description":"Return 304 if policy version is \u003c= this value","in":"query","name":"since_version","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompiledPolicy"}}},"description":"Compiled policy document"},"304":{"description":"Policy unchanged since the given version"},"401":{"description":"Invalid or revoked device key"}},"security":[{"DeviceKeyAuth":[]}],"summary":"Get compiled policy for device","tags":["Apple Device Sync"]}},"/device/report":{"post":{"description":"Submit screen time, app usage, or other activity data. Stored in device_reports and fanned out to activity_logs. Use report_type: enforcement_status with an EnforcementStatusPayload to report per-category enforcement results; this also updates the device's enforcement_summary for the parent dashboard.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceReportRequest"}}},"required":true},"responses":{"202":{"description":"Report accepted"},"401":{"description":"Invalid or revoked device key"}},"security":[{"DeviceKeyAuth":[]}],"summary":"Submit activity report from device","tags":["Apple Device Sync"]}},"/devices/{deviceID}":{"delete":{"description":"Revoke the device's API key. The device will receive 401 on its next request.","responses":{"204":{"description":"Device revoked"}},"summary":"Revoke a device","tags":["Apple Device Sync"]},"parameters":[{"in":"path","name":"deviceID","required":true,"schema":{"format":"uuid","type":"string"}}],"put":{"description":"Update APNs token, app version, or device name. All fields are optional.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDeviceRequest"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceRegistration"}}},"description":"Device updated"},"404":{"description":"Device not found"}},"summary":"Update device metadata","tags":["Apple Device Sync"]}},"/directory":{"get":{"description":"Public, read-only discovery index of registered Trust-List DIDs by role + capabilities, enriched with each provider's active enclave when one is registered. This is how a platform arriving with only a DID FINDS a provider counterparty to connect with. It serves NO key material by design (§11.9): key discovery is a read of the ONE root-signed Trust List document, verified offline against the published root key — `trust_list` in the response points there. Entries carry booleans/pointers (`has_payload_key`, `enclave`) only. The listing is derived from the same verified, delta-applied snapshot the census verifier reads, so a revoked entry vanishes on the same cadence it stops resolving keys.","operationId":"listDirectory","parameters":[{"description":"Keep only entries whose entry_role equals this value (`role=routing` matches the empty default role).","in":"query","name":"role","required":false,"schema":{"type":"string"}},{"description":"Keep only entries declaring this §5.3 capability cell as enforce or alert_only on any OS.","in":"query","name":"capability","required":false,"schema":{"type":"string"}},{"description":"`status=all` includes non-active entries (default: active only).","in":"query","name":"status","required":false,"schema":{"enum":["all"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"entries":{"items":{"properties":{"capabilities":{"items":{"type":"string"},"type":"array"},"did":{"type":"string"},"enclave":{"description":"Present when the DID has an active enclave — the \"discover a sealed-classify counterparty\" bit.","nullable":true,"properties":{"attested":{"type":"boolean"},"enclave_id":{"type":"string"},"endpoint_url":{"type":"string"},"mode":{"type":"string"}},"type":"object"},"entity":{"type":"string"},"has_payload_key":{"description":"You can seal to this DID once you resolve its JWKS from the signed Trust List.","type":"boolean"},"jurisdiction":{"items":{"type":"string"},"type":"array"},"role":{"description":"Empty string (a routing entry) or one of classifier-accredited | verifying-agency | enforcement-agent | independent-advocate.","type":"string"},"status":{"type":"string"},"tier":{"type":"string"},"valid_through":{"format":"date-time","type":"string"}},"type":"object"},"type":"array"},"issue_number":{"format":"int64","type":"integer"},"issued_at":{"format":"date-time","type":"string"},"trust_list":{"description":"Path to the root-signed Trust List document where key material actually lives.","example":"/.well-known/ocss/trust-list","type":"string"}},"type":"object"}}},"description":"The discovery listing."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No Trust List issue has been published yet."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No Trust List source configured (vacant boot — no root identity)."}},"security":[],"summary":"Browse registered Trust-List counterparties (public discovery index)","tags":["OCSS Enforcement Endpoints"]}},"/enforcement-endpoints":{"post":{"description":"Mints one §9.3 endpoint binding — profile P (compiled from the child's active policy) applies to endpoint E (the returned label) for resolver R (`audience_did`) during window W. Consent-first: an active, non-withdrawn consent attestation over (`audience_did`, `child_ref`) must exist before any mint. The caller is the authority-holder's delegated client (never the consuming platform — the governed party does not mint its own credentials, EXT-04 §3.4). The returned `endpoint_id_label` and per-binding `connect_secret` are cleartext EXACTLY ONCE.","operationId":"mintEnforcementEndpoint","requestBody":{"content":{"application/json":{"schema":{"properties":{"audience_did":{"description":"The consuming platform's Trust List DID (the bound resolver, §4.2.2 per-receiver sense).","example":"did:ocss:loopline","type":"string"},"child_ref":{"description":"`child:\u003cuuid\u003e` census reference.","example":"child:a11ce0fa-0000-4000-8000-0000000000a1","type":"string"},"statutory_fail_closed":{"description":"§9.2 deployment-declared fail-closed category list (empty for the family lane).","items":{"type":"string"},"type":"array"},"window_seconds":{"description":"§6.3 rotation window; the declared default is 86400 (24 h).","minimum":1,"type":"integer"}},"required":["audience_did","child_ref","window_seconds"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"binding_id":{"format":"uuid","type":"string"},"connect_secret":{"description":"Per-binding HMAC-SHA256 secret channel (EXT-04 §3.5 reserved; v1 senders sign with the platform-scoped secret).","type":"string"},"endpoint_id_label":{"description":"§9.3(a) label — 43-char base64url; stored only as a SHA-256 digest.","type":"string"}},"type":"object"}}},"description":"Binding minted. Cleartext credentials returned ONCE."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Malformed body (`class:malformed`)."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing/invalid RFC 9421 signature (`class:signature_invalid`)."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No active consent attestation backs the binding (`class:standing_failure`, failed step `authority_binding`)."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unknown child (`class:not_found`)."}},"security":[{"OCSSSignature":[]}],"summary":"Mint a child enforcement-endpoint binding (OCSS §4.2.2 / §9.3)","tags":["OCSS Enforcement Endpoints"]}},"/enforcement-endpoints/{id}/rotate":{"parameters":[{"description":"The binding id returned at mint time (never the label).","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Mints a fresh §9.3 clause-a label for the binding and retires the old one in the same statement. Post-rotation labels support no linkage to pre-rotation labels (`rotate-label-assert-no-join`). Cleartext once.","operationId":"rotateEnforcementEndpoint","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"endpoint_id_label":{"type":"string"}},"type":"object"}}},"description":"Rotated. The new label is returned ONCE."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing/invalid RFC 9421 signature."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unknown enforcement endpoint (`class:not_found`)."}},"security":[{"OCSSSignature":[]}],"summary":"Rotate a binding's endpoint label (OCSS §9.3 clause c)","tags":["OCSS Enforcement Endpoints"]}},"/enforcement-profiles/{endpoint_id_label}":{"get":{"description":"Dereferences an endpoint label to its compiled, router-signed enforcement profile — the §8.3.6 pull lane a gatekeeper's `refreshProfile()` calls. RFC 9421 bound-resolver authenticated: the poll MUST be signed by the SAME Trust List key the binding was minted for (`caller.DID == binding.audience_did`); any other caller — unknown label, wrong resolver, rotated-away label — answers the IDENTICAL 404, so no existence leaks and no join composes (§9.3(a)). Serves cached signed bytes with a strong `ETag`; `If-None-Match` answers `304` at no cost. Mid-window revocation arrives as a SUPERSEDING profile on the next poll (a policy write bumps the version) — there is no revocation verb. A freshly minted endpoint whose child has no active policy yet resolves to a VALID signed EMPTY profile (fail-closed), never a 404.","operationId":"readEnforcementProfile","parameters":[{"description":"Strong ETag from a prior poll; a match answers 304.","in":"header","name":"If-None-Match","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"description":"Signed §8.3.6 profile document (Ed25519 detached signature over the canonical bytes, verifiable against the router key published on the Trust List). Opaque to the transport — the gatekeeper verifies then compiles it.","type":"object"}}},"description":"The router-signed §8.3.6 enforcement profile document. `ETag` is a strong validator (quoted SHA-256 of the body); `Cache-Control: private` keeps shared caches out of a per-resolver authenticated read.","headers":{"Cache-Control":{"description":"Always `private`.","schema":{"type":"string"}},"ETag":{"description":"Strong validator — quoted SHA-256 hex of the signed body.","schema":{"type":"string"}}}},"304":{"description":"Not Modified — the supplied `If-None-Match` matched the current ETag."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing/invalid RFC 9421 signature (`class:signature_invalid`)."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unknown label, a label bound to a DIFFERENT resolver than the authenticated caller, or a dangling binding (child/family gone) — all answer the same `class:not_found`, no existence leak."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Router document-signing identity not provisioned — honest vacancy, never a fabricated signature."}},"security":[{"OCSSSignature":[]}],"summary":"Poll the signed enforcement profile for an endpoint (OCSS §8.3.6)","tags":["OCSS Enforcement Endpoints"]},"parameters":[{"description":"The §9.3(a) endpoint label handed back at mint time (43-char base64url). It is a high-entropy NAME, not a key — never logged, never receipted, never echoed into an error body.","in":"path","name":"endpoint_id_label","required":true,"schema":{"type":"string"}}]},"/enforcement/jobs/{jobID}":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnforcementJob"}}},"description":"Job status"},"404":{"description":"Job not found"}},"summary":"Get enforcement job status","tags":["Enforcement"]},"parameters":[{"in":"path","name":"jobID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/enforcement/jobs/{jobID}/results":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/EnforcementResult"},"type":"array"}}},"description":"Per-platform enforcement results"}},"summary":"Get enforcement job results per platform","tags":["Enforcement"]},"parameters":[{"in":"path","name":"jobID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/enforcement/jobs/{jobID}/retry":{"parameters":[{"in":"path","name":"jobID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnforcementJob"}}},"description":"Retry job started"},"404":{"description":"Job not found"}},"summary":"Retry a failed enforcement job","tags":["Enforcement"]}},"/families":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Family"},"type":"array"}}},"description":"Family list"}},"summary":"List user's families","tags":["Families"]},"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"name":{"type":"string"}},"required":["name"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Family"}}},"description":"Family created"}},"summary":"Create a family","tags":["Families"]}},"/families/{familyID}":{"delete":{"responses":{"204":{"description":"Deleted"}},"summary":"Delete family","tags":["Families"]},"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Family"}}},"description":"Family details"},"404":{"description":"Family not found"}},"summary":"Get family","tags":["Families"]},"parameters":[{"in":"path","name":"familyID","required":true,"schema":{"format":"uuid","type":"string"}}],"put":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"name":{"type":"string"}},"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Family"}}},"description":"Updated"}},"summary":"Update family","tags":["Families"]}},"/families/{familyID}/children":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Child"},"type":"array"}}},"description":"Children list"}},"summary":"List children in family","tags":["Children"]},"parameters":[{"in":"path","name":"familyID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"birth_date":{"format":"date","type":"string"},"name":{"type":"string"}},"required":["name","birth_date"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Child"}}},"description":"Child added"}},"summary":"Add child to family","tags":["Children"]}},"/families/{familyID}/compliance":{"get":{"description":"List all platform connections for a family, showing verification status and last enforcement.","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ComplianceLink"},"type":"array"}}},"description":"Compliance link list"}},"summary":"List compliance links for a family","tags":["Compliance"]},"parameters":[{"in":"path","name":"familyID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/families/{familyID}/members":{"get":{"description":"List all members of a family with their roles (owner, parent, guardian).","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FamilyMember"},"type":"array"}}},"description":"Member list"},"403":{"description":"Not a member of this family"},"404":{"description":"Family not found"}},"summary":"List family members","tags":["Families"]},"parameters":[{"in":"path","name":"familyID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Add a user to a family with a specific role.","requestBody":{"content":{"application/json":{"schema":{"properties":{"role":{"enum":["owner","parent","guardian"],"type":"string"},"user_id":{"description":"User UUID to add","format":"uuid","type":"string"}},"required":["user_id","role"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"status":{"type":"string"}},"type":"object"}}},"description":"Member added"},"400":{"description":"Invalid request body"},"403":{"description":"Insufficient role to add members"},"404":{"description":"Family or user not found"}},"summary":"Add a member to a family","tags":["Families"]}},"/families/{familyID}/members/{memberID}":{"delete":{"responses":{"204":{"description":"Member removed"},"403":{"description":"Insufficient role to remove members"},"404":{"description":"Family or member not found"}},"summary":"Remove a member from a family","tags":["Families"]},"parameters":[{"in":"path","name":"familyID","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"memberID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/families/{familyID}/reports/overview":{"get":{"description":"Get a health dashboard for a family: children, active policies, enforcement status, and recommendations.","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FamilyOverview"}}},"description":"Family overview"},"404":{"description":"Family not found"}},"summary":"Family overview report","tags":["Reports"]},"parameters":[{"in":"path","name":"familyID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/families/{familyID}/sources":{"get":{"description":"List all connected sources for a family.","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Source"},"type":"array"}}},"description":"Source list"}},"summary":"List sources for family","tags":["Sources"]},"parameters":[{"in":"path","name":"familyID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/families/{familyID}/webhooks":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Webhook"},"type":"array"}}},"description":"Webhook list"}},"summary":"List webhook subscriptions for a family","tags":["Webhooks"]},"parameters":[{"in":"path","name":"familyID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/feedback":{"get":{"description":"List all feedback items, optionally filtered by status. No authentication required.","parameters":[{"description":"Filter by status (open, approved, dismissed, fixed)","in":"query","name":"status","schema":{"enum":["open","approved","dismissed","fixed"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/UIFeedback"},"type":"array"}}},"description":"Feedback list"}},"security":[],"summary":"List feedback items","tags":["Feedback"]},"post":{"description":"Submit visual feedback from reviewers. No authentication required.","requestBody":{"content":{"application/json":{"schema":{"properties":{"click_x":{"type":"integer"},"click_y":{"type":"integer"},"comment":{"type":"string"},"component_hint":{"type":"string"},"css_selector":{"type":"string"},"page_route":{"type":"string"},"reviewer_name":{"type":"string"},"viewport_height":{"type":"integer"},"viewport_width":{"type":"integer"}},"required":["page_route","comment"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UIFeedback"}}},"description":"Feedback submitted"}},"security":[],"summary":"Submit UI feedback","tags":["Feedback"]}},"/feedback/{feedbackID}/status":{"parameters":[{"in":"path","name":"feedbackID","required":true,"schema":{"format":"uuid","type":"string"}}],"patch":{"description":"Update the status of a feedback item. Requires authentication.","requestBody":{"content":{"application/json":{"schema":{"properties":{"status":{"enum":["open","approved","dismissed","fixed"],"type":"string"}},"required":["status"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"status":{"type":"string"}},"type":"object"}}},"description":"Status updated"}},"summary":"Update feedback status","tags":["Feedback"]}},"/platform-mappings/{platformID}":{"get":{"description":"Returns platform-specific app identifiers, age ratings, system apps, always-allowed apps, and category_frameworks mapping (which framework + API class to use for each Phosra rule category). Supports platformID=apple and platformID=android.","responses":{"200":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ApplePlatformMappings"},{"$ref":"#/components/schemas/AndroidPlatformMappings"}]}}},"description":"Platform mappings"},"404":{"description":"Platform not supported"}},"security":[],"summary":"Get platform-specific identifier mappings","tags":["Device Sync"]},"parameters":[{"in":"path","name":"platformID","required":true,"schema":{"enum":["apple","android"],"type":"string"}}]},"/platforms":{"get":{"description":"List all platforms Phosra can integrate with, including category, compliance tier, auth type, and capabilities.","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Platform"},"type":"array"}}},"description":"Platform list"}},"security":[],"summary":"List all platforms","tags":["Platforms"]}},"/platforms/by-capability":{"get":{"parameters":[{"in":"query","name":"capability","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Platform"},"type":"array"}}},"description":"Filtered platform list"}},"security":[],"summary":"Filter platforms by capability","tags":["Platforms"]}},"/platforms/by-category":{"get":{"parameters":[{"in":"query","name":"category","required":true,"schema":{"enum":["dns","streaming","gaming","device","browser"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Platform"},"type":"array"}}},"description":"Filtered platform list"}},"security":[],"summary":"Filter platforms by category","tags":["Platforms"]}},"/platforms/{did}/endpoints":{"parameters":[{"description":"The platform's own Trust List DID (`did:ocss:\u003cslug\u003e`). Must equal the authenticated caller's DID.","in":"path","name":"did","required":true,"schema":{"type":"string"}}],"post":{"description":"The platform endpoint-mint: a self-registered gatekeeper declares the base URL its `POST /api/ocss/connect` receiver lives under plus its capability slugs, and receives `{endpoint_id_label, connect_secret}` EXACTLY ONCE — the out-of-band establishment step EXT-04 §3.5 names (\"delivered exactly once ... when the platform registers its connect metadata\"). The census stores only SHA-256 digests (§9.3 — an endpoint_id is a name, not a key). `connect_secret` is the HMAC-SHA256 key the platform feeds into `gk.config({ connectSecret })` to verify `X-Phosra-Signature` on inbound connect-leg deliveries. Self-scoped — `{did}` must equal the caller's authenticated DID; a mismatch answers the same 404 an unknown platform answers. Re-registering the same `(did, connect_url)` pair ROTATES both credentials (the lost-secret recovery path).","operationId":"mintPlatformEndpoint","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"capabilities":{"description":"Declared capability slugs (informative; the signed gatekeeper capabilities document remains the enforced artifact).","example":["content_block_title","dm_restriction"],"items":{"pattern":"^[a-z][a-z0-9_.:-]{0,63}$","type":"string"},"maxItems":128,"type":"array"},"connect_url":{"description":"Absolute HTTPS base URL the `POST /api/ocss/connect` receiver lives under (EXT-04 §3.3 clause 2). No query, fragment, or embedded credentials; plain http is admitted only on sandbox deployments.","example":"https://gatekeeper.example.com","format":"uri","type":"string"}},"required":["connect_url"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"capabilities":{"items":{"type":"string"},"type":"array"},"connect_secret":{"description":"Platform-scoped HMAC-SHA256 secret for verifying X-Phosra-Signature on connect-leg deliveries.","type":"string"},"connect_url":{"format":"uri","type":"string"},"endpoint_id":{"format":"uuid","type":"string"},"endpoint_id_label":{"description":"§9.3(a) registration label — 43-char base64url; digest-only at rest.","type":"string"},"rotated_at":{"format":"date-time","type":"string"}},"type":"object"}}},"description":"Endpoint registered (or re-registered with rotated credentials). `endpoint_id_label` and `connect_secret` are cleartext EXACTLY ONCE — never re-readable."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Malformed body / connect_url / capability slug (`class:malformed`)."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing/invalid RFC 9421 signature (`class:signature_invalid`)."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unknown platform, or `{did}` is not the caller's own DID (`class:not_found` — no existence leak)."}},"security":[{"OCSSSignature":[]}],"summary":"Register a platform connect endpoint and receive the connect secret ONCE (EXT-04 §3.5)","tags":["OCSS Enforcement Endpoints"]}},"/platforms/{platformID}":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Platform"}}},"description":"Platform details"},"404":{"description":"Platform not found"}},"security":[],"summary":"Get platform details","tags":["Platforms"]},"parameters":[{"in":"path","name":"platformID","required":true,"schema":{"type":"string"}}]},"/platforms/{platformID}/oauth/authorize":{"get":{"parameters":[{"in":"query","name":"state","schema":{"type":"string"}},{"in":"query","name":"redirect_uri","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"authorize_url":{"type":"string"}},"type":"object"}}},"description":"Authorization URL"},"400":{"description":"Platform does not support OAuth"}},"security":[],"summary":"Get OAuth authorization URL for a platform","tags":["Platforms"]},"parameters":[{"in":"path","name":"platformID","required":true,"schema":{"type":"string"}}]},"/platforms/{platformID}/oauth/callback":{"get":{"parameters":[{"in":"query","name":"code","required":true,"schema":{"type":"string"}},{"in":"query","name":"redirect_uri","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"description":"Platform-specific OAuth token response","type":"object"}}},"description":"OAuth tokens exchanged"},"400":{"description":"Platform does not support OAuth"},"502":{"description":"OAuth exchange failed"}},"security":[],"summary":"Handle OAuth callback","tags":["Platforms"]},"parameters":[{"in":"path","name":"platformID","required":true,"schema":{"type":"string"}}]},"/policies/{policyID}":{"delete":{"responses":{"204":{"description":"Deleted"},"404":{"description":"Policy not found"}},"summary":"Delete a policy and all its rules","tags":["Policies"]},"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChildPolicy"}}},"description":"Policy details"},"404":{"description":"Policy not found"}},"summary":"Get policy details","tags":["Policies"]},"parameters":[{"in":"path","name":"policyID","required":true,"schema":{"format":"uuid","type":"string"}}],"put":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"name":{"type":"string"},"priority":{"type":"integer"}},"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChildPolicy"}}},"description":"Policy updated"},"404":{"description":"Policy not found"}},"summary":"Update a policy's name or priority","tags":["Policies"]}},"/policies/{policyID}/activate":{"post":{"parameters":[{"in":"path","name":"policyID","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChildPolicy"}}},"description":"Policy activated"}},"summary":"Activate policy","tags":["Policies"]}},"/policies/{policyID}/generate-from-age":{"post":{"parameters":[{"in":"path","name":"policyID","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PolicyRule"},"type":"array"}}},"description":"Rules generated"}},"summary":"Auto-generate rules based on child's age","tags":["Policies"]}},"/policies/{policyID}/pause":{"post":{"parameters":[{"in":"path","name":"policyID","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChildPolicy"}}},"description":"Policy paused"}},"summary":"Pause policy","tags":["Policies"]}},"/policies/{policyID}/rules":{"get":{"operationId":"listPolicyRules","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PolicyRule"},"type":"array"}}},"description":"Rule list"}},"summary":"List policy rules","tags":["Policy Rules"]},"parameters":[{"in":"path","name":"policyID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"operationId":"createRule","requestBody":{"content":{"application/json":{"schema":{"properties":{"category":{"$ref":"#/components/schemas/RuleCategory"},"config":{"description":"Category-specific configuration JSON","type":"object"},"enabled":{"type":"boolean"}},"required":["category","enabled","config"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyRule"}}},"description":"Rule created"}},"summary":"Create rule","tags":["Policy Rules"]}},"/policies/{policyID}/rules/bulk":{"put":{"description":"Create or update multiple rules at once for a policy. Each rule in the array must include category, enabled, and config.","operationId":"bulkUpsertRules","parameters":[{"in":"path","name":"policyID","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"rules":{"items":{"properties":{"category":{"$ref":"#/components/schemas/RuleCategory"},"config":{"type":"object"},"enabled":{"type":"boolean"}},"required":["category","enabled","config"],"type":"object"},"type":"array"}},"required":["rules"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PolicyRule"},"type":"array"}}},"description":"Rules upserted"}},"summary":"Bulk upsert rules","tags":["Policy Rules"]}},"/ratings/by-age":{"get":{"parameters":[{"in":"query","name":"age","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgeRatings"}}},"description":"Age-appropriate ratings across all systems"}},"security":[],"summary":"Get ratings for a specific age","tags":["Ratings"]}},"/ratings/systems":{"get":{"description":"List all content rating systems (MPAA, TV, ESRB, PEGI, CSM).","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RatingSystem"},"type":"array"}}},"description":"Rating systems"}},"security":[],"summary":"List rating systems","tags":["Ratings"]}},"/ratings/systems/{systemID}":{"get":{"description":"Returns all individual ratings within the given rating system.","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Rating"},"type":"array"}}},"description":"Ratings in the system"}},"security":[],"summary":"Get ratings for a specific rating system","tags":["Ratings"]},"parameters":[{"in":"path","name":"systemID","required":true,"schema":{"type":"string"}}]},"/ratings/systems/{systemID}/descriptors":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ContentDescriptor"},"type":"array"}}},"description":"Content descriptors"}},"security":[],"summary":"Get content descriptors for a rating system","tags":["Ratings"]},"parameters":[{"in":"path","name":"systemID","required":true,"schema":{"type":"string"}}]},"/ratings/{ratingID}/convert":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RatingEquivalence"},"type":"array"}}},"description":"Equivalent ratings across systems"}},"security":[],"summary":"Convert a rating to equivalents in other systems","tags":["Ratings"]},"parameters":[{"in":"path","name":"ratingID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/rules/{ruleID}":{"delete":{"operationId":"deleteRule","responses":{"204":{"description":"Rule deleted"},"404":{"description":"Rule not found"}},"summary":"Delete a rule","tags":["Policy Rules"]},"parameters":[{"in":"path","name":"ruleID","required":true,"schema":{"format":"uuid","type":"string"}}],"put":{"operationId":"updateRule","requestBody":{"content":{"application/json":{"schema":{"properties":{"config":{"description":"Updated configuration JSON","type":"object"},"enabled":{"type":"boolean"}},"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyRule"}}},"description":"Rule updated"},"404":{"description":"Rule not found"}},"summary":"Update a rule's enabled status or config","tags":["Policy Rules"]}},"/sandbox/test-connect":{"post":{"description":"The F3 counterparty. A cold platform dev self-registers a gatekeeper DID and stands up a `POST /api/ocss/connect` receiver, but the sandbox directory carries no accredited PROVIDER to run the EXT-04 §3.2 connect ceremony against it — so the dev never receives an `{endpoint_id_label, state}` delivery and never sees a signed profile land. This route IS that missing provider: it mints a §9.3 binding for a seeded test child scoped to your `platform_did`, then POSTs the signed §3.6 callback to your `webhook_url` — byte-identical to the production Link sender. You then poll the returned `profile_url` (RFC 9421 signed as your platform DID) to observe the live signed enforcement profile. SANDBOX ONLY — answers `404` when `PHOSRA_ENV != sandbox`.","operationId":"sandboxTestConnect","requestBody":{"content":{"application/json":{"schema":{"properties":{"child_ref":{"description":"Seeded test child to connect (`child:\u003cuuid\u003e`); defaults to the seeded Mia child.","example":"child:a11ce0fa-0000-4000-8000-0000000000a1","type":"string"},"connect_secret":{"description":"The HMAC secret you fed into `gk.config({ connectSecret })`; the §3.6 callback is signed with it so your receiver verifies `X-Phosra-Signature`. The census holds only its digest, so you supply it here.","type":"string"},"platform_did":{"description":"Your self-registered Trust-List DID — the resolver the binding is minted for and the DID you later sign the profile poll with.","example":"did:ocss:my-gatekeeper","type":"string"},"webhook_url":{"description":"Your connect-receiver base; the callback is delivered to `webhook_url + /api/ocss/connect`. Absolute http/https, no query/fragment/credentials.","example":"https://my-gatekeeper.example.com","type":"string"},"window_seconds":{"description":"§6.3 rotation window in seconds; defaults to 3600.","example":3600,"format":"int64","type":"integer"}},"required":["platform_did","webhook_url","connect_secret"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"binding_id":{"type":"string"},"connect_receiver":{"description":"The exact receiver URL the callback was POSTed to.","type":"string"},"delivered":{"description":"True when your receiver returned 2xx.","type":"boolean"},"endpoint_id_label":{"description":"The §9.3(a) label just minted for you; poll it at `profile_url`.","type":"string"},"http_status":{"description":"The status your receiver returned (0 if unreachable).","type":"integer"},"note":{"description":"Present only when delivery did not receive a 2xx — how to fix the receiver leg.","type":"string"},"profile_url":{"description":"`/api/v1/enforcement-profiles/{endpoint_id_label}` — poll it (signed as your DID) to observe the signed profile.","type":"string"},"resolver_did":{"description":"Echo of your platform_did — the DID the label was compiled for.","type":"string"},"state":{"description":"The opaque §3.2-step-3 principal ref echoed in the callback.","type":"string"}},"type":"object"}}},"description":"Binding minted and callback delivery attempted. The binding is minted and `profile_url` is observable even when the webhook leg fails — check `delivered`/`http_status`/`note` for the receiver leg."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing/invalid `platform_did`, `connect_secret`, `webhook_url`, or `child_ref`."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not in sandbox (`PHOSRA_ENV != sandbox`), or `child_ref` names an unseeded child."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Census storage not yet available."}},"security":[{"OCSSSignature":[]}],"summary":"Run the PROVIDER side of the connect ceremony to your own webhook (SANDBOX only)","tags":["OCSS Enforcement Endpoints"]}},"/setup/quick":{"post":{"description":"Creates a family (or uses existing), adds a child, generates age-appropriate policy rules, and activates the policy.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuickSetupRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuickSetupResponse"}}},"description":"Setup complete"},"400":{"description":"Invalid request (missing child_name or birth_date)"}},"summary":"One-step onboarding","tags":["Quick Setup"]}},"/sources":{"post":{"description":"Connect a parental control app as a source for a child.","requestBody":{"content":{"application/json":{"schema":{"properties":{"auto_sync":{"description":"Enable automatic sync on policy changes","type":"boolean"},"child_id":{"format":"uuid","type":"string"},"credentials":{"description":"Source credentials (for managed sources)","type":"object"},"family_id":{"format":"uuid","type":"string"},"source":{"description":"Source slug (e.g., 'bark', 'qustodio')","type":"string"}},"required":["child_id","family_id","source"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Source"}}},"description":"Source connected"}},"summary":"Connect a source","tags":["Sources"]}},"/sources/available":{"get":{"description":"List all available parental control source adapters that can be connected.","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AvailableSource"},"type":"array"}}},"description":"Available source list"}},"security":[],"summary":"List available source adapters","tags":["Sources"]}},"/sources/{sourceID}":{"delete":{"responses":{"204":{"description":"Source disconnected"},"404":{"description":"Source not found"}},"summary":"Disconnect source","tags":["Sources"]},"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Source"}}},"description":"Source details"},"404":{"description":"Source not found"}},"summary":"Get source details","tags":["Sources"]},"parameters":[{"in":"path","name":"sourceID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/sources/{sourceID}/guide/{category}":{"get":{"description":"Get manual setup instructions for a guided-tier source.","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/GuidedStep"},"type":"array"}}},"description":"Guided steps"},"404":{"description":"Source or category not found"}},"summary":"Get guided steps","tags":["Sources"]},"parameters":[{"in":"path","name":"sourceID","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"category","required":true,"schema":{"type":"string"}}]},"/sources/{sourceID}/jobs":{"get":{"description":"List sync job history for a source.","parameters":[{"description":"Maximum number of jobs to return","in":"query","name":"limit","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SourceSyncJob"},"type":"array"}}},"description":"Sync job list"}},"summary":"List sync jobs","tags":["Sources"]},"parameters":[{"in":"path","name":"sourceID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/sources/{sourceID}/jobs/{jobID}":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceSyncJob"}}},"description":"Sync job details"},"404":{"description":"Sync job not found"}},"summary":"Get sync job","tags":["Sources"]},"parameters":[{"in":"path","name":"sourceID","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"jobID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/sources/{sourceID}/jobs/{jobID}/results":{"get":{"description":"Get per-rule results for a sync job.","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SourceSyncResult"},"type":"array"}}},"description":"Sync result list"}},"summary":"Get sync results","tags":["Sources"]},"parameters":[{"in":"path","name":"sourceID","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"jobID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/sources/{sourceID}/jobs/{jobID}/retry":{"parameters":[{"in":"path","name":"sourceID","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"jobID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Retry a failed sync job.","responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceSyncJob"}}},"description":"Retry job started"},"404":{"description":"Sync job not found"}},"summary":"Retry sync","tags":["Sources"]}},"/sources/{sourceID}/rules":{"parameters":[{"in":"path","name":"sourceID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Push a single rule to a connected source.","requestBody":{"content":{"application/json":{"schema":{"properties":{"category":{"$ref":"#/components/schemas/RuleCategory"},"value":{"description":"Rule value/config","type":"object"}},"required":["category"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceSyncResult"}}},"description":"Rule push result"}},"summary":"Push single rule","tags":["Sources"]}},"/sources/{sourceID}/sync":{"parameters":[{"in":"path","name":"sourceID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"description":"Push all active policy rules to a connected source.","requestBody":{"content":{"application/json":{"schema":{"properties":{"sync_mode":{"description":"Sync mode (default: full)","enum":["full","incremental"],"type":"string"}},"type":"object"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceSyncJob"}}},"description":"Sync job started"}},"summary":"Sync source","tags":["Sources"]}},"/standards":{"get":{"description":"Browse all published community standards/movements. No authentication required.","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Standard"},"type":"array"}}},"description":"Standards list"}},"security":[],"summary":"List published community standards","tags":["Standards"]}},"/standards/{slug}":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Standard"}}},"description":"Standard details including rules"},"404":{"description":"Standard not found"}},"security":[],"summary":"Get a community standard by slug","tags":["Standards"]},"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}]},"/sync/jobs/{jobID}":{"get":{"parameters":[{"in":"path","name":"jobID","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncJob"}}},"description":"Job status"}},"summary":"Get sync job status","tags":["Sync"]}},"/sync/jobs/{jobID}/results":{"get":{"parameters":[{"in":"path","name":"jobID","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SyncJobResult"},"type":"array"}}},"description":"Job results"}},"summary":"Get sync job results per provider","tags":["Sync"]}},"/webhooks":{"post":{"description":"Register a webhook endpoint to receive event notifications.","requestBody":{"content":{"application/json":{"schema":{"properties":{"events":{"description":"Events to subscribe to (e.g. policy.updated, enforcement.completed)","items":{"type":"string"},"type":"array"},"family_id":{"format":"uuid","type":"string"},"url":{"description":"Webhook URL to receive events","type":"string"}},"required":["family_id","url","events"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}},"description":"Webhook created"}},"summary":"Create a webhook subscription","tags":["Webhooks"]}},"/webhooks/{webhookID}":{"delete":{"responses":{"204":{"description":"Webhook deleted"},"404":{"description":"Webhook not found"}},"summary":"Delete a webhook","tags":["Webhooks"]},"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}},"description":"Webhook details"},"404":{"description":"Webhook not found"}},"summary":"Get webhook details","tags":["Webhooks"]},"parameters":[{"in":"path","name":"webhookID","required":true,"schema":{"format":"uuid","type":"string"}}],"put":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"active":{"type":"boolean"},"events":{"items":{"type":"string"},"type":"array"},"url":{"type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}},"description":"Webhook updated"},"404":{"description":"Webhook not found"}},"summary":"Update a webhook","tags":["Webhooks"]}},"/webhooks/{webhookID}/deliveries":{"get":{"parameters":[{"description":"Maximum number of deliveries to return (default 50)","in":"query","name":"limit","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/WebhookDelivery"},"type":"array"}}},"description":"Delivery history"}},"summary":"List webhook deliveries","tags":["Webhooks"]},"parameters":[{"in":"path","name":"webhookID","required":true,"schema":{"format":"uuid","type":"string"}}]},"/webhooks/{webhookID}/test":{"parameters":[{"in":"path","name":"webhookID","required":true,"schema":{"format":"uuid","type":"string"}}],"post":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDelivery"}}},"description":"Test delivery result"},"404":{"description":"Webhook not found"}},"summary":"Send a test delivery to a webhook","tags":["Webhooks"]}}},"security":[{"BearerAuth":[]}],"servers":[{"description":"Production","url":"https://prodapi.phosra.com/api/v1"},{"description":"Sandbox (seeded demo family + phosra_test_ keys)","url":"https://sandbox.phosra.com/api/v1"},{"description":"Local development","url":"http://localhost:8080/api/v1"}]}