> ## Documentation Index
> Fetch the complete documentation index at: https://docs.phosra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Postman & Insomnia collection

> Import the full Phosra API as a ready-to-run Postman or Insomnia collection — 93 requests across 20 folders, pre-wired to the hosted sandbox with a one-variable API key. Hit Send and you are live.

Prefer an API client to curl? Import the entire Phosra API into **Postman** or **Insomnia** in
under a minute. The collection's structure is generated from the canonical
[OpenAPI 3.1 spec](/api-reference/overview#openapi-spec), with **curated example bodies** layered
on top, and comes **pre-wired to the hosted sandbox** — every request already points at
`{{baseUrl}}` and authenticates with a single `{{apiKey}}` variable. Import it, drop in your key,
and press **Send**.

<CardGroup cols={2}>
  <Card title="Download collection" icon="download" href="/tools/phosra-api.postman_collection.json">
    `phosra-api.postman_collection.json` — Postman Collection v2.1, **93 requests** in **20
    folders**. Works in Postman and Insomnia.
  </Card>

  <Card title="Download sandbox environment" icon="gear" href="/tools/phosra-sandbox.postman_environment.json">
    `phosra-sandbox.postman_environment.json` — sets `baseUrl` to the live sandbox and gives you
    an `apiKey` slot to fill in.
  </Card>
</CardGroup>

<Info>
  Both files are checked into the docs repo under
  [`docs/api/tools/`](https://github.com/Phosra-Inc). The request/folder structure is regenerated
  from the spec whenever it changes; the example bodies are curated by hand so they stay realistic
  rather than reverting to schema stubs.
</Info>

## What's inside

The collection mirrors the API reference one-to-one:

<CardGroup cols={2}>
  <Card title="93 requests, 20 folders" icon="folder-tree">
    Auth, Families, Children, Policies, Rules, Enforcement, Platforms, Compliance, Webhooks,
    Standards, Ratings, Device Sync, and more — one folder per tag.
  </Card>

  <Card title="Pre-wired auth" icon="key">
    Collection-level Bearer auth sends `Authorization: Bearer {{apiKey}}` on every request. Public
    reads (platforms, ratings, standards) work even before you set a key.
  </Card>

  <Card title="Example bodies filled in" icon="pen-to-square">
    Every write request that carries a body ships with a realistic, curated one — a single
    coherent "Rivera Household" family, real OCSS rule categories, and config shapes that match
    the API reference (`time_daily_limit → {"daily_minutes": 120}`, not a guessed field). No
    `"string"` placeholders; the create/update/enforce bodies run against the sandbox as-is.
    Action routes that take no body (activate, pause, retry, trigger) are correctly left empty.
  </Card>

  <Card title="Sandbox-first" icon="flask">
    `{{baseUrl}}` defaults to `https://phosra-api-sandbox-production.up.railway.app/api/v1`. Nothing
    you send can touch a production family.
  </Card>
</CardGroup>

## Import into Postman

<Steps>
  <Step title="Download both files">
    Grab the [collection](/tools/phosra-api.postman_collection.json) and the
    [sandbox environment](/tools/phosra-sandbox.postman_environment.json) using the buttons above.
  </Step>

  <Step title="Import them">
    In Postman, click **Import** (top-left) and drag both `.json` files in — or paste the file
    URLs into the **Import → Link** box. Postman detects the OpenAPI-derived collection and the
    environment automatically.
  </Step>

  <Step title="Select the environment">
    In the environment dropdown (top-right), choose **Phosra Sandbox**. `baseUrl` is already set to
    the live sandbox.
  </Step>

  <Step title="Add your key (optional for public reads)">
    Open the **Phosra Sandbox** environment, set `apiKey` to your `phosra_test_…` key, and save.
    Get one from [Create your account](/platform/create-account). Public endpoints —
    **Platforms**, **Ratings**, **Standards** — return `200` without a key.
  </Step>

  <Step title="Send a request">
    Open **Platforms → List all platforms** and press **Send**. You should get a `200` with the
    seeded platform catalog. You are now talking to the live sandbox.
  </Step>
</Steps>

## Import into Insomnia

Insomnia reads Postman v2.1 collections natively:

<Steps>
  <Step title="Import the collection">
    In Insomnia, open **Application menu → Import**, choose **From File**, and select
    `phosra-api.postman_collection.json`. Insomnia maps the 20 folders to request groups.
  </Step>

  <Step title="Set the base environment">
    Open **Manage Environments** and add:

    ```json theme={null}
    {
      "baseUrl": "https://phosra-api-sandbox-production.up.railway.app/api/v1",
      "apiKey": "phosra_test_your_key_here"
    }
    ```

    The imported requests already reference `{{ baseUrl }}` and `{{ apiKey }}`.
  </Step>

  <Step title="Send">
    Pick any request and hit **Send**. Public reads work immediately; authenticated routes use your
    `apiKey`.
  </Step>
</Steps>

## Verify it works

The first request most people run is **Platforms → List all platforms**. Resolved against the
sandbox environment, it is exactly this call — copy-paste it to confirm the same `200` you'll see
in Postman:

```bash theme={null}
curl -s "https://phosra-api-sandbox-production.up.railway.app/api/v1/platforms"
```

Real response from the live sandbox (`HTTP 200`, \~220 ms), trimmed to the first entry:

```json theme={null}
[
  {
    "id": "fire_tablet",
    "name": "Amazon Fire Tablet",
    "category": "device",
    "tier": "pending",
    "enabled": true
  }
  // … 22 platforms total
]
```

That is the same endpoint, base URL, and response the collection produces on **Send** — no mocks,
no key required.

## Environment variables

Two variables control **where** you send and **how** you authenticate:

| Variable  | Default                                                       | What it's for                                                                                                                                                                                                                                                                                                        |
| --------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `baseUrl` | `https://phosra-api-sandbox-production.up.railway.app/api/v1` | The hosted sandbox root. Swap for `https://prodapi.phosra.com/api/v1` when you go live.                                                                                                                                                                                                                              |
| `apiKey`  | `phosra_test_REPLACE_ME`                                      | Your developer key. Sent as `Authorization: Bearer {{apiKey}}` on every authenticated request. Marked **secret** so it never syncs to a shared workspace. In the sandbox, the family/child/policy resources you create yourself are readable without a key — so most of the collection runs before you paste one in. |

To move to production, duplicate the environment, set `baseUrl` to
`https://prodapi.phosra.com/api/v1`, and use a `phosra_live_…` key. Every request shape is
identical — only these two variables change.

## Resource IDs — defined, not left blank

Every path and body that needs an id (`/families/:familyID`, `{"family_id": "…"}`, and so on) reads
from a **named collection variable** — never a hand-copied UUID buried in a URL. The collection
ships with all of them defined, so nothing resolves to a dangling `{{familyId}}` on import:

| Variable                                                                                               | Seeded with                                               | How it gets a live value                                                                     |
| ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `standardId`                                                                                           | `33e8b8cf-…` — the real **Four Norms** community standard | Works immediately: `GET /standards` and `GET /standards/four-norms` return it with no setup. |
| `familyId`                                                                                             | the shared sandbox demo family                            | Overwritten the moment you run **Setup → One-step onboarding** or **Create a family**.       |
| `childId`                                                                                              | demo child **Mia**                                        | Overwritten by **One-step onboarding** or **Add child to family**.                           |
| `policyId`                                                                                             | Mia's demo policy                                         | Overwritten by **One-step onboarding** or **Create policy for child**.                       |
| `ruleId`, `webhookId`, `sourceId`, `linkId`, `deviceId`, `jobId`, `memberId`, `feedbackId`, `ratingId` | empty                                                     | Captured automatically by the matching **Create** request (see below).                       |
| `userId`                                                                                               | empty                                                     | The one id you bring yourself — a real WorkOS user id you want to add as a family member.    |

<Warning>
  The `familyId` / `childId` / `policyId` defaults point at the **shared demo family** (Mia, Leo,
  Ava). Those rows are read-scoped to the guided walkthrough, so a `GET` against them from your own
  session returns `403 not a member of this family`. That is expected — run **Setup → One-step
  onboarding** first (next section) and every id switches to a resource your session owns.
</Warning>

### Path ids on public reads are pre-bound to real sandbox values

A handful of requests take a **fixed, keyless** identifier in the URL rather than a captured
variable — the public catalog reads (**Platforms**, **Standards**, **Ratings**, **Providers**).
Instead of the generator's `:platformID = "string"` placeholder (which `404`s on **Send**), every
one of these is bound to a real value that resolves against the live sandbox immediately, no key
and no setup:

| Request                                                 | Path binding               | Resolves to                                                                                                        |
| ------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Platforms → Get platform details                        | `platformID = apple`       | `GET /platforms/apple` → `200`                                                                                     |
| Platforms → Get OAuth authorization URL                 | `platformID = microsoft`   | `200` — an **OAuth-capable** platform (device platforms like `apple` return `400 platform does not support OAuth`) |
| Platforms → Handle OAuth callback                       | `platformID = microsoft`   | `200` — but supply a **real `code`** from the authorize step; the seeded `code` is a labeled placeholder           |
| Providers → Get provider connect config                 | `did = did:ocss:loopline`  | `GET /providers/did:ocss:loopline/connect` → `200`                                                                 |
| Standards → Get a community standard by slug            | `slug = four-norms`        | `GET /standards/four-norms` → `200`                                                                                |
| Ratings → Get ratings for a specific rating system      | `systemID = esrb`          | `GET /ratings/systems/esrb` → `200`                                                                                |
| Ratings → Get content descriptors for a rating system   | `systemID = esrb`          | `200`                                                                                                              |
| Device Sync → Get platform-specific identifier mappings | `platformID = apple`       | `GET /platform-mappings/apple` → `200`                                                                             |
| Sources → Get guided steps                              | `category = web_filtering` | `200` **once `{{sourceId}}` is captured** — connect a guided-tier source first (its path-var note explains this)   |

<Info>
  The only path binding that still needs a step first is **Get guided steps**: its `category` is now a
  real OCSS rule-category slug, but the request also reads `{{sourceId}}`, which stays empty until you
  connect a source. That dependency is spelled out on the request's `sourceID` path-variable
  description — it is a labeled prerequisite, not a silent `404`.
</Info>

### Run it top to bottom — IDs chain themselves

The **Create** requests carry a Postman **test script** that saves the new resource's id straight
into the collection variable the later requests read. Send them in order and you never paste a
UUID:

<Steps>
  <Step title="Setup → One-step onboarding">
    `POST /setup/quick` returns a family, child, policy, and starter rules in one call. Its test
    script sets `familyId`, `childId`, `policyId`, and `ruleId` for you.
  </Step>

  <Step title="Anything that reads them">
    **Get family**, **List children**, **Get policy**, **Create rule**, **Connect a webhook** … all
    resolve against the ids the previous step captured. **Create webhook / source / device /
    platform** likewise write `webhookId` / `sourceId` / `deviceId` / `linkId` for their follow-up
    reads.
  </Step>
</Steps>

The capture scripts are plain `pm.collectionVariables.set(...)` calls — inspect them on any
**Create** request under the **Scripts → Post-response** tab.

This full chain is proven against the live sandbox:

```bash theme={null}
BASE=https://phosra-api-sandbox-production.up.railway.app/api/v1

# 1. One-step onboarding → real family + child + policy ids
curl -s -X POST "$BASE/setup/quick" -H 'Content-Type: application/json' \
  -d '{"family_name":"Rivera Household","child_name":"Robin","birth_date":"2014-03-02"}'
# → 201 {"family":{"id":"a67a318c-…"},"child":{"id":"9a6782a2-…"},"policy":{"id":"ececceac-…"}, …}

# 2. GET the family the step above created (the id the test script captured)
curl -s "$BASE/families/a67a318c-f621-47a4-8ec9-e359db814b56"
# → 200 {"id":"a67a318c-…","name":"Rivera Household", …}

# standardId works with zero setup:
curl -s "$BASE/standards/four-norms"
# → 200 {"id":"33e8b8cf-1453-405a-8493-8fe76264623a","slug":"four-norms","name":"Four Norms", …}
```

## Run in Postman

<Note>
  A one-click **Run in Postman** button requires the collection to be published to a public Postman
  workspace, which Phosra has not done yet — so rather than ship a button that 404s, we ship the
  collection file itself. The **Import → Link** flow above (paste the collection URL) is the
  equivalent one-step import and always tracks the checked-in file.
</Note>

## Keep it fresh

The collection's structure is derived from
[`docs/api/openapi.yaml`](/api-reference/overview#openapi-spec) — the same spec that powers the
reference and the inline **Try it** runner — so the endpoints, folders, and auth stay in
lock-step with the live API. When the spec version bumps, re-download the checked-in files.

You can also regenerate the skeleton locally from the published spec (the CLI reads a local file,
so fetch it first). Note the raw output ships **schema-stub bodies** (`"name": "<string>"`); the
checked-in collection replaces those with the curated example bodies described above, so treat a
fresh regen as a starting point, not a drop-in replacement:

```bash theme={null}
curl -s https://phosra-api-sandbox-production.up.railway.app/openapi.json -o openapi.json

npx openapi-to-postmanv2 \
  -s openapi.json \
  -o phosra-api.postman_collection.json \
  -p -O folderStrategy=Tags
```

## Next steps

<CardGroup cols={2}>
  <Card title="API playground" icon="play" href="/playground">
    Run keyless requests without leaving the docs — inline **Try it** on every reference page.
  </Card>

  <Card title="Quickstart" icon="bolt" href="/quickstart">
    Zero to an enforced child-safety policy in under five minutes.
  </Card>

  <Card title="Authentication" icon="lock" href="/authentication">
    Bearer keys, DID signatures, and when each applies.
  </Card>

  <Card title="API reference" icon="book" href="/api-reference/overview">
    Every endpoint, field, and error — with a live sandbox behind each one.
  </Card>
</CardGroup>
