Generate a client in your language

Phosra ships a first-party TypeScript SDK. Every other language talks to the same plain-JSON REST API — and because that API is described by a published OpenAPI 3.1 document, you can generate a fully-typed client in your language in about a minute. This page runs the real generators end-to-end against the live sandbox and pastes the actual output, including one generated-client call that returns 201.

Verified copy-paste. Every command on this page was run on 2026-07-06 against the hosted spec at https://phosra-api-sandbox-production.up.railway.app/openapi.yaml with openapi-generator-cli 7.23.0, openapi-typescript 7.13.0, and openapi-fetch 0.13. The TypeScript, Python, and Go clients each made the same live POST /setup/quick call and printed the same three lines: Emma's Family / preteen / 20.

Which spec do I generate from?

Phosra publishes three OpenAPI documents. Pick the one that matches what you are building — generating from the wrong spec gives you endpoints you can't call.

SpecGenerate fromUse it when you are…
Product API (openapi.yaml)https://phosra-api-sandbox-production.up.railway.app/openapi.yamlBuilding a parental-controls app — families, children, policies, rules, enforcement, quick-setup. This is the one almost everyone wants. Also mirrored at docs.phosra.com/openapi.yaml.
Control plane (openapi.control-plane.yaml)https://docs.phosra.com/openapi.control-plane.yamlManaging your provider account — developer orgs, API keys, federation, advisors. Org/key/usage management authenticates with your WorkOS session bearer, not a phosra_ key (see Create your account & get keys).
Data plane (openapi.data-plane.yaml)https://docs.phosra.com/openapi.data-plane.yamlBuilding an OCSS gatekeeper / resolver — signed profile reads, binding, §8.3.8 confirmations. See the signing caveat below before you generate.

The sandbox host serves the product API spec (/openapi.yaml) so generated clients can point straight at a live server. The two split specs (openapi.control-plane.yaml, openapi.data-plane.yaml) are served from the docs site. All three are the same documents that drive the API Reference.

Prerequisites

For TypeScript

Node.js 18+. Uses openapi-typescript (types) + openapi-fetch (runtime). No Java, no code to vendor — types only.

For Python / Go / Java / others

Java 11+ on your PATH (the openapi-generator engine is a JAR) plus Node to run npx @openapitools/openapi-generator-cli. Check with java -version.

TypeScript — openapi-typescript + openapi-fetch

The idiomatic TS path is types-only generation paired with a tiny typed fetch wrapper — no generated client code to vendor, and full autocomplete on every path, body, and response.

  1. 1
    Generate the types

    bash
    npx openapi-typescript \
      https://phosra-api-sandbox-production.up.railway.app/openapi.yaml \
      -o ./phosra.d.ts

    Real output:

    text
    ✨ openapi-typescript 7.13.0
    🚀 https://phosra-api-sandbox-production.up.railway.app/openapi.yaml → phosra.d.ts [430.5ms]

    You now have a phosra.d.ts describing all 80 paths. The /setup/quick entry looks exactly like this (excerpt from the real generated file):

    typescript
    "/setup/quick": {
        // …
        /**
         * One-step onboarding
         * @description Creates a family (or uses existing), adds a child,
         *   generates age-appropriate policy rules, and activates the policy.
         */
        post: {
            requestBody: {
                content: {
                    "application/json": components["schemas"]["QuickSetupRequest"];
                };
            };
            responses: {
                /** @description Setup complete */
                201: {
                    content: {
                        "application/json": components["schemas"]["QuickSetupResponse"];
                    };
                };
                // …
            };
        };
    };

  2. 2
    Make a typed call

    bash
    npm install openapi-fetch
    typescript
    import createClient from "openapi-fetch";
    import type { paths } from "./phosra.d.ts";
     
    const client = createClient<paths>({
      baseUrl: "https://phosra-api-sandbox-production.up.railway.app/api/v1",
    });
     
    // Fully typed: the body, the path, and `data` below are all inferred from the spec.
    const { data, error } = await client.POST("/setup/quick", {
      body: { child_name: "Emma", birth_date: "2016-03-15", strictness: "recommended" },
    });
    if (error) throw error;
     
    console.log("family:", data.family?.name);
    console.log("age_group:", data.age_group);
    console.log("rules_enabled:", data.rule_summary?.total_rules_enabled);

    Run it (Node 22+ strips the types natively):

    bash
    node --experimental-strip-types run.ts

    Real output:

    text
    family: Emma's Family
    age_group: preteen
    rules_enabled: 20

Prefer a batteries-included, hand-maintained client? Skip generation and use the first-party @phosra/sdk (PhosraClient) — it wraps the same API with retries and token management.

Python — openapi-generator-cli

  1. 1
    Generate the client

    bash
    npx @openapitools/openapi-generator-cli generate \
      -i https://phosra-api-sandbox-production.up.railway.app/openapi.yaml \
      -g python \
      -o ./python-client \
      --additional-properties=packageName=phosra_client,projectName=phosra-client

    This writes a full installable package: one *_api.py module per tag (quick_setup_api.py, families_api.py, policies_api.py, …) plus Pydantic models for every schema (QuickSetupRequest, QuickSetupResponse, …).

  2. 2
    Install and call the sandbox

    bash
    python3 -m venv .venv && . .venv/bin/activate
    pip install ./python-client
    python
    from phosra_client import ApiClient, Configuration, QuickSetupApi
    from phosra_client.models.quick_setup_request import QuickSetupRequest
     
    cfg = Configuration(
        host="https://phosra-api-sandbox-production.up.railway.app/api/v1"
    )
    with ApiClient(cfg) as api_client:
        api = QuickSetupApi(api_client)
        resp = api.setup_quick_post(QuickSetupRequest(
            child_name="Emma", birth_date="2016-03-15", strictness="recommended",
        ))
        print("family:", resp.family.name)
        print("age_group:", resp.age_group)
        print("rules_enabled:", resp.rule_summary.total_rules_enabled)
    bash
    python3 run_client.py

    Real output:

    text
    family: Emma's Family
    age_group: preteen
    rules_enabled: 20

Go — openapi-generator-cli

  1. 1
    Generate the client

    bash
    npx @openapitools/openapi-generator-cli generate \
      -i https://phosra-api-sandbox-production.up.railway.app/openapi.yaml \
      -g go \
      -o ./go-client \
      --additional-properties=packageName=phosra,isGoSubmodule=true,disallowAdditionalPropertiesIfNotPresent=false

    The generator writes a placeholder module path (github.com/GIT_USER_ID/GIT_REPO_ID). Edit the first line of go-client/go.mod to your real module path (below it is example.com/phosra) so you can import it.

  2. 2
    Call the sandbox

    go
    package main
     
    import (
        "context"
        "fmt"
        "log"
     
        phosra "example.com/phosra"
    )
     
    func main() {
        cfg := phosra.NewConfiguration()
        // Point the default server at the open sandbox.
        cfg.Servers[0].URL = "https://phosra-api-sandbox-production.up.railway.app/api/v1"
        client := phosra.NewAPIClient(cfg)
     
        req := phosra.NewQuickSetupRequest("Emma", "2016-03-15")
        req.SetStrictness("recommended")
     
        resp, _, err := client.QuickSetupAPI.SetupQuickPost(context.Background()).
            QuickSetupRequest(*req).Execute()
        if err != nil {
            log.Fatal(err)
        }
        family := resp.GetFamily()
        summary := resp.GetRuleSummary()
        fmt.Println("family:", family.GetName())
        fmt.Println("age_group:", resp.GetAgeGroup())
        fmt.Println("rules_enabled:", summary.GetTotalRulesEnabled())
    }
    bash
    go mod tidy && go run .

    Real output:

    text
    family: Emma's Family
    age_group: preteen
    rules_enabled: 20

Java and everything else

The same openapi-generator-cli produces clients for 60+ languages — swap the -g flag. Java, for example:

bash
npx @openapitools/openapi-generator-cli generate \
  -i https://phosra-api-sandbox-production.up.railway.app/openapi.yaml \
  -g java \
  -o ./java-client \
  --additional-properties=library=native,invokerPackage=com.phosra.client,apiPackage=com.phosra.client.api,modelPackage=com.phosra.client.model

Other common targets: -g csharp, -g rust, -g kotlin, -g php, -g ruby, -g swift5. List every generator with npx @openapitools/openapi-generator-cli list.

Authenticating a generated client

The verified calls above hit the open sandbox POST /setup/quick, which needs no key — that is the sandbox-first path. Real product routes live under /developer/* and need a phosra_test_ (sandbox) or phosra_live_ (production) key sent as Authorization: Bearer. Wire it into the generated client's config:

typescript
const client = createClient<paths>({
  baseUrl: "https://phosra-api-sandbox-production.up.railway.app/api/v1",
  headers: { Authorization: `Bearer ${process.env.PHOSRA_API_KEY}` },
});

Get a sandbox key

Mint a phosra_test_… key on the Keys page. See Authentication for the full header rules (Authorization: Bearer is canonical; X-Api-Key is accepted as an equivalent).

The data-plane signing caveat

If you generate from openapi.data-plane.yaml, be aware that those endpoints are not plain REST:

A generated client gives you the correct wire shapes for the data plane, but it will not produce valid requests on its own. Profile GETs are RFC 9421-signed by the resolver, binding POSTs require an accredited caller signature, and profile responses are root-verifiable signed documents. Signing and verification are cryptographic steps the generator does not emit.

For anything on the data plane, use the maintained SDKs that implement the signing:

  • @openchildsafety/ocss — RFC 9421 sign/verify, sealed envelopes, trust-list verification.
  • @phosra/gatekeeper — the platform-side decision engine that drives the data plane end-to-end (verify profiles, enforce, send §8.3.8 receipts).

Use a generated data-plane client only for reading response types into your own models, not for making the signed calls. The product API (openapi.yaml) has no such caveat — a generated client calls it directly, as shown above.

Pin your generator version

Generators evolve; output can shift between major versions. For reproducible builds, pin the exact version instead of letting npx pull latest:

bash
# Pin openapi-generator
npx @openapitools/[email protected] generate ...   # CLI wrapper
# (the wrapper then downloads generator engine 7.23.0 — pin it in openapitools.json)
 
# Pin openapi-typescript
npx [email protected] ...

The spec is versioned by the /api/v1 path and evolves additively (see Versioning). Re-run the generator after a spec change to pick up new fields; a regeneration never silently changes an existing field's type without a version bump.

Next steps