> ## 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.

# Environments & base URLs

> The one canonical reference for every Phosra environment and base URL — sandbox vs production, control plane vs data plane, test-data isolation, and how to point each SDK at an environment. Every URL verified live.

This is the single page to bookmark for "which URL do I call, in which environment, with which key."
Every base URL below was verified live while writing this page. Nothing here touches a real family
until you deliberately point at production with a `phosra_live_…` key.

<CardGroup cols={2}>
  <Card title="Sandbox — start here, no key" icon="flask" href="#the-environment-matrix">
    `https://phosra-api-sandbox-production.up.railway.app` — open, seeded, safe to hammer.
  </Card>

  <Card title="Production — real families" icon="tower-broadcast" href="#the-environment-matrix">
    `https://prodapi.phosra.com` — requires a `phosra_live_…` key.
  </Card>
</CardGroup>

## The environment matrix

Three environments. One host per environment — the same host serves **both planes** (see
[Control plane vs data plane](#control-plane-vs-data-plane) — the plane is a routing/credential
choice, not a different domain). Append `/api/v1` for the REST API; well-known paths (`/health`,
`/.well-known/ocss/…`) hang off the bare host.

|                                       | **Local dev**                                                              | **Sandbox** (partner)                                                      | **Production**                                                             |
| ------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| **Host**                              | `http://localhost:8080`                                                    | `https://phosra-api-sandbox-production.up.railway.app`                     | `https://prodapi.phosra.com`                                               |
| **REST base URL**                     | `http://localhost:8080/api/v1`                                             | `…up.railway.app/api/v1`                                                   | `https://prodapi.phosra.com/api/v1`                                        |
| **Key prefix**                        | `phosra_test_`                                                             | `phosra_test_`                                                             | `phosra_live_`                                                             |
| **Touches real families?**            | No                                                                         | No — isolated sandbox data                                                 | **Yes**                                                                    |
| **Consumer routes need a key?**       | No (open)                                                                  | No (open)                                                                  | Yes (WorkOS JWT)                                                           |
| **`/developer/*` routes need a key?** | Yes (`phosra_test_`)                                                       | Yes (`phosra_test_`)                                                       | Yes (`phosra_live_`)                                                       |
| **Trust List root `key_id`**          | your local root                                                            | `root-sandbox-2026-06`                                                     | `root-prod-2026-06`                                                        |
| **Where to get a key**                | [dashboard → test](https://dashboard.phosra.com/dashboard/developers/keys) | [dashboard → test](https://dashboard.phosra.com/dashboard/developers/keys) | [dashboard → live](https://dashboard.phosra.com/dashboard/developers/keys) |
| **Status**                            | your machine                                                               | **live** ✔                                                                 | **live** ✔                                                                 |

<Info>
  **One sandbox, one base URL.** `phosra-api-sandbox-production.up.railway.app` is the open **partner
  sandbox** — every `curl`, SDK snippet, and quickstart on this site targets it. You may notice a
  `-staging` host elsewhere in the census; that one is a **Phosra-internal mirror** (it does not
  expose the reference-provider OAuth surface and may run ahead of the partner sandbox), so build
  against the `-production` host above. See [The two sandbox hosts](#the-two-sandbox-hosts-why).
</Info>

## Control plane vs data plane

Phosra separates two **surfaces**, but not two **domains**. Within a single environment host, the
plane is selected by the **route prefix and the credential you present** — there is no separate
`controlplane.…` or `dataplane.…` hostname to configure.

<CardGroup cols={2}>
  <Card title="Control plane" icon="sliders">
    Manage your account — orgs, `phosra_` keys, usage, advisor agents, MCP tokens. Routes under
    `/api/v1/developers/orgs/…`. Auth: your **WorkOS session bearer**.
  </Card>

  <Card title="Data plane" icon="bolt">
    The enforcement path — families, children, policies, rules, enforcement profiles, receipts.
    Consumer routes (`/api/v1/…`) take a WorkOS JWT; developer routes (`/api/v1/developer/…`) take
    a `phosra_` **API key**.
  </Card>
</CardGroup>

|                    | Control plane                                     | Data plane                                           |
| ------------------ | ------------------------------------------------- | ---------------------------------------------------- |
| Same host per env? | **Yes** — `prodapi.phosra.com` / the sandbox host | **Yes** — same host                                  |
| Route prefix       | `/api/v1/developers/orgs/…`, `/api/v1/mcp/…`      | `/api/v1/…` (consumer) · `/api/v1/developer/…` (key) |
| Credential         | WorkOS session bearer                             | WorkOS JWT (consumer) · `phosra_` key (developer)    |
| Reference          | [Control-plane API](/platform/overview)           | [Data-plane API](/api-reference/data-plane/overview) |

<Note>
  The open **OCSS protocol** surface (the Trust List, succession record, signed receipts) is served on
  the **same host** too, at well-known paths — `GET /.well-known/ocss/trust-list` and
  `GET /.well-known/ocss-succession` — and needs **no** credential. See [Architecture](/concepts/architecture).
</Note>

## Test keys vs live keys

Every developer key is bound to **one environment at creation** — the prefix tells you which, and
crossing them fails closed.

|               | Sandbox / test                                                                              | Production / live                     |
| ------------- | ------------------------------------------------------------------------------------------- | ------------------------------------- |
| Prefix        | `phosra_test_<64 hex>`                                                                      | `phosra_live_<64 hex>`                |
| Valid against | the sandbox host only                                                                       | `prodapi.phosra.com` only             |
| Header        | `Authorization: Bearer phosra_test_…`                                                       | `Authorization: Bearer phosra_live_…` |
| Mint at       | [dashboard → environment: **test**](https://dashboard.phosra.com/dashboard/developers/keys) | same page → environment: **live**     |

<Warning>
  A `phosra_test_` key against production — or a `phosra_live_` key against the sandbox — returns
  `401 invalid API key`. The hash simply isn't in that environment's key store; keys never span
  environments. Full auth details: [Authentication](/authentication).
</Warning>

The sandbox is designed to be tried **without any signup**: its **consumer** routes
(`/api/v1/families`, `/api/v1/children/…`, `/api/v1/setup/quick`) are open, so you can explore
request/response shapes with zero credentials. The `/api/v1/developer/*` routes still require a real
`phosra_test_` key so you exercise the exact scope + rate-limit behavior you will hit in production.

## Test-data isolation & reset

The sandbox has **no destructive "reset" button** — instead it gives you two levers so tests stay
clean and repeatable, and it never shares state with production.

<AccordionGroup>
  <Accordion title="Per-caller isolation — X-Sandbox-Session">
    Pass `X-Sandbox-Session: <any-string>` and the sandbox keys all your writes to a stable sandbox
    user for that value. Reuse the same string to accumulate against one caller; use a fresh string
    for a clean slate. Omit it and you share the `default` session.

    ```bash theme={null}
    curl -s -X POST "https://phosra-api-sandbox-production.up.railway.app/api/v1/setup/quick" \
      -H "Content-Type: application/json" \
      -H "X-Sandbox-Session: my-test-run-42" \
      -d '{"child_name":"Nora","birth_date":"2015-05-01","strictness":"strict"}'
    ```
  </Accordion>

  <Accordion title="Idempotent retries — family_id">
    `POST /setup/quick` deduplicates on `(family_id, child_name, birth_date)`. A repeat call that
    threads back the `family_id` from a prior response returns the **same** child and policy rather
    than minting a duplicate — safe to retry after a network failure.
  </Accordion>

  <Accordion title="Production isolation">
    Sandbox and production run on **separate hosts backed by separate databases**. Nothing you create
    in the sandbox is visible in production, and there is no path that reads production data into the
    sandbox. Switching environments is a base-URL + key swap — no data migrates.
  </Accordion>
</AccordionGroup>

For every fixed, seeded sandbox value — the demo children (Mia/Leo/Ava), reference providers,
the 22 reference platforms, and connect scopes — see the flat lookup table:
[Sandbox test data](/api-reference/sandbox-test-data).

## Point each SDK at an environment

Each SDK exposes one knob for the base URL. The **important gotcha**: `@phosra/sdk` and the MCP
server expect the base URL **with** the `/api/v1` suffix, while `@phosra/link`, `@phosra/gatekeeper`,
and the CLI take the **bare host** (they append paths themselves).

| SDK / tool                                | Knob                           | Suffix             | Sandbox value            | Production                                  |
| ----------------------------------------- | ------------------------------ | ------------------ | ------------------------ | ------------------------------------------- |
| `@phosra/sdk` (`PhosraClient`)            | `baseUrl`                      | **with** `/api/v1` | `…up.railway.app/api/v1` | default `https://prodapi.phosra.com/api/v1` |
| `@phosra/link`                            | `censusBaseUrl`                | **bare host**      | `…up.railway.app`        | `https://prodapi.phosra.com`                |
| `@phosra/gatekeeper` (`createGatekeeper`) | `censusBaseUrl`                | **bare host**      | `…up.railway.app`        | `https://prodapi.phosra.com`                |
| `@phosra/cli`                             | `PHOSRA_CENSUS_URL` (env)      | **bare host**      | `…up.railway.app`        | `https://prodapi.phosra.com`                |
| `@phosra/mcp`                             | `--api-url` / `PHOSRA_API_URL` | **with** `/api/v1` | `…up.railway.app/api/v1` | default `https://prodapi.phosra.com/api/v1` |
| raw HTTP                                  | `$PHOSRA_BASE_URL`             | **with** `/api/v1` | `…up.railway.app/api/v1` | `https://prodapi.phosra.com/api/v1`         |

<CodeGroup>
  ```typescript @phosra/sdk theme={null}
  import { PhosraClient } from "@phosra/sdk";

  // Sandbox — no key required. baseUrl includes /api/v1.
  const client = new PhosraClient({
    baseUrl: "https://phosra-api-sandbox-production.up.railway.app/api/v1",
  });

  // Production — drop baseUrl (defaults to https://prodapi.phosra.com/api/v1) and add a live key.
  const prod = new PhosraClient({ apiKey: process.env.PHOSRA_API_KEY }); // phosra_live_…
  ```

  ```typescript @phosra/link theme={null}
  import { PhosraLink } from "@phosra/link";

  const link = new PhosraLink({
    // censusBaseUrl is the BARE host — no /api/v1 suffix.
    censusBaseUrl: "https://phosra-api-sandbox-production.up.railway.app",
    providerDid:   "did:ocss:loopline",
  });
  // Production: censusBaseUrl: "https://prodapi.phosra.com"
  ```

  ```typescript @phosra/gatekeeper theme={null}
  import { createGatekeeper } from "@phosra/gatekeeper";

  const gk = createGatekeeper({
    // BARE host — the SDK appends the profile path itself.
    censusBaseUrl: "https://phosra-api-sandbox-production.up.railway.app",
    endpointId:    "pg-mia-network-edge-2026-06",
  });
  // Production: censusBaseUrl: "https://prodapi.phosra.com"
  ```

  ```bash @phosra/cli theme={null}
  # CLI reads the BARE host from PHOSRA_CENSUS_URL (defaults to the sandbox host).
  export PHOSRA_CENSUS_URL="https://phosra-api-sandbox-production.up.railway.app"
  npx @phosra/cli doctor
  # Production: export PHOSRA_CENSUS_URL="https://prodapi.phosra.com"
  ```

  ```bash @phosra/mcp theme={null}
  # MCP takes the base URL WITH /api/v1 (defaults to production when --api-url is omitted).
  npx @phosra/mcp \
    --api-url "https://phosra-api-sandbox-production.up.railway.app/api/v1" \
    --api-key "$PHOSRA_API_KEY"
  ```

  ```bash raw curl theme={null}
  # One env var for every raw HTTP snippet on this site. Includes /api/v1.
  export PHOSRA_BASE_URL="https://phosra-api-sandbox-production.up.railway.app/api/v1"
  curl -s "$PHOSRA_BASE_URL/families" | head
  # Production: export PHOSRA_BASE_URL="https://prodapi.phosra.com/api/v1"
  ```
</CodeGroup>

<Note>
  **Local dev** is the same shape: point any knob at `http://localhost:8080` (bare host) or
  `http://localhost:8080/api/v1` (with suffix), matching the column above, after
  `go run ./cmd/server/` is up on port 8080.
</Note>

## Verify every URL

Copy-paste this to prove each live host end-to-end. These are the **real** responses captured while
writing this page:

```bash theme={null}
# Production control plane — live, no key needed for /health
curl -fsS https://prodapi.phosra.com/health
# → {"status":"ok"}

# Partner sandbox — live, open
curl -fsS https://phosra-api-sandbox-production.up.railway.app/health
# → {"status":"ok"}

# A well-formed request with NO key to a /developer/* route → 403 (proves the route is real + gated)
curl -s -o /dev/null -w "%{http_code}\n" \
  https://prodapi.phosra.com/api/v1/developer/families
# → 403

# The signed Trust List — no key. Sandbox has a growing signed list; production is a
# validly-signed EMPTY list until accreditation entries are added via OCSS governance.
curl -fsS https://prodapi.phosra.com/.well-known/ocss/trust-list \
  | python3 -c "import sys,json; d=json.load(sys.stdin); doc=json.loads(d['document']); \
print('prod  root:', d['key_id'], '| entries:', len(doc['entries']))"
# → prod  root: root-prod-2026-06 | entries: 0

curl -fsS https://phosra-api-sandbox-production.up.railway.app/.well-known/ocss/trust-list \
  | python3 -c "import sys,json; d=json.load(sys.stdin); doc=json.loads(d['document']); \
print('sbx   root:', d['key_id'], '| entries:', len(doc['entries']))"
# → sbx   root: root-sandbox-2026-06 | entries: <a live, growing number>
```

<Note>
  The sandbox Trust List **grows** as developers self-register DIDs, so treat its total entry count as
  a live number — verify each entry's signature and accreditation tier rather than asserting on the
  total. Production returning `entries: 0` is the honest, correct state (a validly-signed empty list),
  not a bug: no test or sandbox keys are ever present in production.
</Note>

## Going to production

Three changes flip you from sandbox to production — the request and response shapes are **identical**:

<Steps>
  <Step title="Swap the base URL">
    `https://phosra-api-sandbox-production.up.railway.app` → `https://prodapi.phosra.com`
    (update the SDK knob from the [table above](#point-each-sdk-at-an-environment)).
  </Step>

  <Step title="Add a live key">
    Attach `Authorization: Bearer phosra_live_…`. [Create one](/platform/create-account) in the
    dashboard under environment **live**.
  </Step>

  <Step title="Use real DIDs">
    The reference providers (`did:ocss:loopline`, …) and demo children are sandbox-only. In
    production you connect real accredited providers and real family data.
  </Step>
</Steps>

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/authentication" horizontal>
    Test vs live keys, the one correct header, scoping, and rotation.
  </Card>

  <Card title="Sandbox test data" icon="flask" href="/api-reference/sandbox-test-data" horizontal>
    Every fixed, seeded value in the sandbox — the Stripe-test-cards of Phosra.
  </Card>

  <Card title="Architecture" icon="diagram-project" href="/concepts/architecture" horizontal>
    Why the control plane, data plane, and OCSS protocol are kept separate.
  </Card>

  <Card title="Go-live checklist" icon="rocket" href="/integration/go-live-checklist" horizontal>
    Everything to confirm before you point at production.
  </Card>
</CardGroup>

## The two sandbox hosts (why)

For completeness: the census runs in two Railway environments, so two sandbox hosts exist. **You only
ever use the first one.**

| Host                                           | Role                                                                                                       | Use it?                           |
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `phosra-api-sandbox-production.up.railway.app` | **Partner sandbox** — open, seeded, reference-provider OAuth surface live. The canonical testing endpoint. | **Yes** — every doc targets this. |
| `phosra-api-sandbox-staging.up.railway.app`    | Phosra-internal mirror — where new census changes land first. May run ahead of the partner sandbox.        | No — internal only.               |

Both are validly-signed sandbox censuses (root `root-sandbox-2026-06`), but only the `-production`
partner sandbox is a supported integration target. If a Phosra-authored example ever shows the
`-staging` host, treat the `-production` host as the correct one.
