SDK (@phosra/sdk)
import { PhosraClient } from "@phosra/sdk";
const phosra = new PhosraClient({
baseUrl: "https://phosra-api-sandbox-production.up.railway.app/api/v1",
accessToken: process.env.PHOSRA_API_KEY,
});
const result = await phosra.rules.bulkUpsert("b011c1e5-0000-4000-8000-000000000b01", {
rules: [
{
category: "time_daily_limit",
enabled: true,
config: {
minutes: 120
}
},
{
category: "web_safesearch",
enabled: true,
config: {}
}
]
});
console.log(result);
curl -sS -X PUT "https://phosra-api-sandbox-production.up.railway.app/api/v1/policies/b011c1e5-0000-4000-8000-000000000b01/rules/bulk" \
-H "Authorization: Bearer $PHOSRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"rules": [
{
"category": "time_daily_limit",
"enabled": true,
"config": {
"minutes": 120
}
},
{
"category": "web_safesearch",
"enabled": true,
"config": {}
}
]
}'
import os, requests
BASE = "https://phosra-api-sandbox-production.up.railway.app/api/v1"
res = requests.put(
f"{BASE}/policies/b011c1e5-0000-4000-8000-000000000b01/rules/bulk",
headers={"Authorization": f"Bearer {os.environ['PHOSRA_API_KEY']}"},
json={
"rules": [
{
"category": "time_daily_limit",
"enabled": True,
"config": {
"minutes": 120
}
},
{
"category": "web_safesearch",
"enabled": True,
"config": {}
}
]
},
)
print(res.status_code, res.json())
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
base := "https://phosra-api-sandbox-production.up.railway.app/api/v1"
body := bytes.NewBufferString(`{
"rules": [
{
"category": "time_daily_limit",
"enabled": true,
"config": {
"minutes": 120
}
},
{
"category": "web_safesearch",
"enabled": true,
"config": {}
}
]
}`)
req, _ := http.NewRequest("PUT", base+"/policies/b011c1e5-0000-4000-8000-000000000b01/rules/bulk", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PHOSRA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(out))
}
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://phosra-api-sandbox-production.up.railway.app/api/v1/policies/{policyID}/rules/bulk",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'rules' => [
[
'enabled' => true,
'config' => [
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.put("https://phosra-api-sandbox-production.up.railway.app/api/v1/policies/{policyID}/rules/bulk")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"rules\": [\n {\n \"enabled\": true,\n \"config\": {}\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://phosra-api-sandbox-production.up.railway.app/api/v1/policies/{policyID}/rules/bulk")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"rules\": [\n {\n \"enabled\": true,\n \"config\": {}\n }\n ]\n}"
response = http.request(request)
puts response.read_body[
{
"id": "3d92f6b1-8e04-4a7c-b1d5-6f2093c8ae41",
"policy_id": "7b3e9c04-2d1a-4f68-9e5b-8c07a1f34d29",
"category": "rating_age_gate",
"enabled": true,
"config": {},
"created_at": "2026-06-18T14:32:07Z"
}
]{
"error": "Bad Request",
"message": "child_name is required",
"code": 400
}{
"error": "Unauthorized",
"message": "missing or invalid Authorization header",
"code": 401
}{
"error": "Forbidden",
"message": "not a member of this family",
"code": 403
}{
"error": "Not Found",
"message": "policy not found",
"code": 404
}{
"error": "Too Many Requests",
"message": "rate limit exceeded",
"code": 429
}{
"error": "Internal Server Error",
"message": "internal error",
"code": 500
}Policy Rules
Bulk apply rules
Create or update multiple rules at once for a policy. Each rule in the array must include category, enabled, and config.
PUT
/
policies
/
{policyID}
/
rules
/
bulk
SDK (@phosra/sdk)
import { PhosraClient } from "@phosra/sdk";
const phosra = new PhosraClient({
baseUrl: "https://phosra-api-sandbox-production.up.railway.app/api/v1",
accessToken: process.env.PHOSRA_API_KEY,
});
const result = await phosra.rules.bulkUpsert("b011c1e5-0000-4000-8000-000000000b01", {
rules: [
{
category: "time_daily_limit",
enabled: true,
config: {
minutes: 120
}
},
{
category: "web_safesearch",
enabled: true,
config: {}
}
]
});
console.log(result);
curl -sS -X PUT "https://phosra-api-sandbox-production.up.railway.app/api/v1/policies/b011c1e5-0000-4000-8000-000000000b01/rules/bulk" \
-H "Authorization: Bearer $PHOSRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"rules": [
{
"category": "time_daily_limit",
"enabled": true,
"config": {
"minutes": 120
}
},
{
"category": "web_safesearch",
"enabled": true,
"config": {}
}
]
}'
import os, requests
BASE = "https://phosra-api-sandbox-production.up.railway.app/api/v1"
res = requests.put(
f"{BASE}/policies/b011c1e5-0000-4000-8000-000000000b01/rules/bulk",
headers={"Authorization": f"Bearer {os.environ['PHOSRA_API_KEY']}"},
json={
"rules": [
{
"category": "time_daily_limit",
"enabled": True,
"config": {
"minutes": 120
}
},
{
"category": "web_safesearch",
"enabled": True,
"config": {}
}
]
},
)
print(res.status_code, res.json())
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
base := "https://phosra-api-sandbox-production.up.railway.app/api/v1"
body := bytes.NewBufferString(`{
"rules": [
{
"category": "time_daily_limit",
"enabled": true,
"config": {
"minutes": 120
}
},
{
"category": "web_safesearch",
"enabled": true,
"config": {}
}
]
}`)
req, _ := http.NewRequest("PUT", base+"/policies/b011c1e5-0000-4000-8000-000000000b01/rules/bulk", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PHOSRA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(out))
}
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://phosra-api-sandbox-production.up.railway.app/api/v1/policies/{policyID}/rules/bulk",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'rules' => [
[
'enabled' => true,
'config' => [
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.put("https://phosra-api-sandbox-production.up.railway.app/api/v1/policies/{policyID}/rules/bulk")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"rules\": [\n {\n \"enabled\": true,\n \"config\": {}\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://phosra-api-sandbox-production.up.railway.app/api/v1/policies/{policyID}/rules/bulk")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"rules\": [\n {\n \"enabled\": true,\n \"config\": {}\n }\n ]\n}"
response = http.request(request)
puts response.read_body[
{
"id": "3d92f6b1-8e04-4a7c-b1d5-6f2093c8ae41",
"policy_id": "7b3e9c04-2d1a-4f68-9e5b-8c07a1f34d29",
"category": "rating_age_gate",
"enabled": true,
"config": {},
"created_at": "2026-06-18T14:32:07Z"
}
]{
"error": "Bad Request",
"message": "child_name is required",
"code": 400
}{
"error": "Unauthorized",
"message": "missing or invalid Authorization header",
"code": 401
}{
"error": "Forbidden",
"message": "not a member of this family",
"code": 403
}{
"error": "Not Found",
"message": "policy not found",
"code": 404
}{
"error": "Too Many Requests",
"message": "rate limit exceeded",
"code": 429
}{
"error": "Internal Server Error",
"message": "internal error",
"code": 500
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Body
application/json
Show child attributes
Show child attributes
Response
Rules upserted
Unique identifier for this resource.
Identifier of the policy this resource belongs to.
One of the 123 OCSS rule categories (accepted and signed by the census; capability varies — see the Rule Categories reference).
Available options:
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 Whether this item is currently active.
Category-specific configuration object for this rule.
RFC 3339 timestamp of when the resource was created.