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

# Developer FAQ

> Fast, straight answers to the questions every Phosra integrator asks first — test vs live keys, the sandbox, 401 vs 403, whether you need request signing, rate limits, and the exact npm package names.

Short answers to the questions integrators hit in the first hour. Each one links to the
authoritative page if you want the full detail. Looking up a specific error instead? The
[Troubleshooting](/troubleshooting) and [Errors](/errors) pages are organized for that.

<Info>
  Every status, header, and error body quoted below was captured live from the partner sandbox
  at `https://phosra-api-sandbox-production.up.railway.app` on **2026-07-06**. Package versions
  were verified against the public npm registry the same day.
</Info>

## Keys & environments

<AccordionGroup>
  <Accordion title="What's the difference between a test key and a live key?">
    The **prefix** tells you which environment a developer API key belongs to, and each key works
    against exactly one base URL:

    |                        | Sandbox / test                                                | Production / live                   |
    | ---------------------- | ------------------------------------------------------------- | ----------------------------------- |
    | Prefix                 | `phosra_test_`                                                | `phosra_live_`                      |
    | Base URL               | `https://phosra-api-sandbox-production.up.railway.app/api/v1` | `https://prodapi.phosra.com/api/v1` |
    | Touches real families? | No — isolated sandbox data                                    | Yes                                 |

    A `phosra_test_` key only authenticates against the sandbox, and a `phosra_live_` key only
    against production. **Crossing them returns `401 invalid API key`** — the key's hash simply
    isn't in that environment's store. Mint both on the same
    [Keys page](https://dashboard.phosra.com/dashboard/developers/keys) (pick the environment).
    Full detail: [Authentication → Test keys vs live keys](/authentication#test-keys-vs-live-keys).
  </Accordion>

  <Accordion title="Which header does the API key go in?">
    `Authorization: Bearer phosra_…` is the canonical header and the one every example on this site
    uses. `X-Api-Key: phosra_…` is accepted as an exact equivalent — use it only if a proxy or
    framework reserves the `Authorization` header. When both are present, `Authorization` wins.
    There is no `Basic` auth and no query-string key. See
    [Authentication → The one correct header](/authentication#the-one-correct-header).
  </Accordion>

  <Accordion title="Do I need an API key to try Phosra at all?">
    No. The sandbox's **consumer routes** (`/api/v1/families`, `/api/v1/children/…`,
    `/api/v1/setup/quick`, `/health`, the Trust List) are open, so your first request returns real
    data with **zero credentials** — the [Quickstart](/quickstart) proves it. You only need a
    `phosra_test_` key when you want to exercise the `/api/v1/developer/*` routes and hit the exact
    scope + rate-limit behavior you'll see in production.
  </Accordion>
</AccordionGroup>

## The sandbox

<AccordionGroup>
  <Accordion title="Does the sandbox reset or wipe my data?">
    There is **no scheduled destructive wipe**. The seeded reference data — the demo family
    (`Mia`, `Leo`, `Ava`) and the accredited reference providers — is always present. Anything you
    create persists, and the write endpoints are **idempotent**: `POST /setup/quick` de-dupes on
    `(family, child_name, birth_date)` and self-registering a DID is create-only (a repeat returns
    `409`, never a silent overwrite). To start from a clean object, use a new child name or a fresh
    self-registered DID rather than looking for a reset button — there isn't one. See
    [Test in the sandbox](/guides/test-in-sandbox).
  </Accordion>

  <Accordion title="Is the sandbox isolated from production?">
    Yes. Nothing you do in the sandbox touches a real family or production data — it runs on its
    own database. Going to production is a two-line change: swap the base URL to
    `https://prodapi.phosra.com/api/v1` and send a `phosra_live_…` key. The request and response
    shapes are byte-for-byte identical. See [Test in the sandbox](/guides/test-in-sandbox).
  </Accordion>

  <Accordion title="The sandbox took a few seconds to answer my first call — is it down?">
    No — sandbox services **auto-sleep when idle and cold-start on the first request**, so an
    occasional `502`/`503` or a slow first response is expected. Retry once after a few seconds and
    it'll be warm. See [Errors → Server errors](/errors#server-errors).
  </Accordion>
</AccordionGroup>

## 401 vs 403

<AccordionGroup>
  <Accordion title="What's the difference between a 401 and a 403?">
    **`401` = we don't know who you are.** The credential is missing, malformed, unknown, revoked,
    or expired.

    ```json theme={null}
    { "error": "Unauthorized", "message": "invalid API key", "code": 401 }
    ```

    **`403` = we know who you are, but you're not allowed to do this.** The credential is valid but
    lacks the scope, standing, or accreditation tier for the request. A `/developer/*` call with
    **no** key at all is a `403` (the request reached the scope check with an empty scope set):

    ```json theme={null}
    { "error": "Forbidden", "message": "no API scopes in context", "code": 403 }
    ```

    A valid key that's missing a scope returns
    `{ "message": "missing required scope: write:enforcement", "code": 403 }`. Fix a `401` by
    correcting the credential; fix a `403` by granting the scope (or standing). Full matrix:
    [Errors → Client errors](/errors#client-errors) and
    [Authentication → Error responses](/authentication#error-responses).
  </Accordion>

  <Accordion title="I'm getting a 401 on a signed route, not an API-key route. Same thing?">
    Same status, different cause. On OCSS Trust Framework routes (rule writes, enforcement
    confirmations, consent) a `401` means the **request signature** is missing or invalid, and the
    body carries `"class": "signature_invalid"`:

    ```json theme={null}
    { "error": "Unauthorized", "message": "exactly one Signature-Input and one Signature header are required", "code": 401, "class": "signature_invalid" }
    ```

    Branch on `code` **and** `class`, never on the prose `message`. See
    [Do I need to sign my requests?](#signing) below and [Errors → OCSS error classes](/errors#ocss-error-classes).
  </Accordion>

  <Accordion title="Should I be getting a 401 or a 404?">
    They mean different things and never substitute for each other: a `404 not_found` means the
    resource genuinely does not exist (a stale ID is never silently redirected to a live one),
    while a `401` means an auth/signature problem. The
    [Troubleshooting → 401 vs 404](/troubleshooting#401-vs-404-which-one-should-i-be-getting)
    section walks through telling them apart.
  </Accordion>
</AccordionGroup>

## Signing

<AccordionGroup>
  <Accordion title="Do I need to sign my requests?" defaultOpen>
    **It depends on the surface — most integrations never sign anything.**

    * **Developer REST routes** (`/api/v1/developer/*` and the consumer `/api/v1/*` routes): **no
      signing.** A `phosra_` API key (or a WorkOS session JWT) in the `Authorization` header is all
      you need. This is the vast majority of the API — families, children, policies, enforcement,
      devices, webhooks.
    * **OCSS Trust Framework routes** (signed rule writes, enforcement confirmations, consent
      attestations, receipts): **yes — RFC 9421 HTTP Message Signatures are required**, signed with
      a key that's published on the [Trust List](/guides/test-in-sandbox). These carry the extra
      `class` field and return `401 signature_invalid` when the signature is absent or wrong.

    You don't hand-roll the signature: the [`@openchildsafety/ocss`](/sdks/phosra-developer-sdk)
    library signs and verifies OCSS envelopes, and [`@phosra/link`](/sdks/link) wraps the consent
    ceremony + signed rule-write directives. See [Authentication](/authentication) for how API
    keys, WorkOS JWTs, and request signing fit together.
  </Accordion>

  <Accordion title="Where does the WorkOS JWT fit in?">
    First-party **parent apps** (web / iOS / Android) sign users in with WorkOS AuthKit and pass the
    resulting RS256 access token as `Authorization: Bearer <workos_jwt>`. The API only *validates*
    the JWT — there is no `/auth/register`, `/auth/login`, or `/auth/refresh` on the Phosra API
    (those live in WorkOS). Server-to-server integrations use a `phosra_` API key instead. See
    [Authentication → Consumer path](/authentication#consumer-path-workos-authkit-jwt).
  </Accordion>
</AccordionGroup>

## Rate limits

<AccordionGroup>
  <Accordion title="What are the rate limits?">
    The free/sandbox default is **100 requests per 60-second rolling window**. Authenticated calls
    are counted **per developer org** (every key in the org shares one bucket, so adding keys does
    **not** add quota); unauthenticated sandbox reads are counted **per source IP**. Paid plans
    raise the ceiling. The unauthenticated OCSS **discovery** reads (Trust List, profiles, Annex B
    editions) are **unmetered**. Full detail: [Rate limits](/rate-limits).
  </Accordion>

  <Accordion title="How do I know how much quota I have left?">
    Read the headers on the response you already have — don't hard-code a number. Every metered
    `/api/v1/*` response carries:

    ```text theme={null}
    x-ratelimit-limit: 100
    x-ratelimit-remaining: 89
    x-ratelimit-reset: 1783334580
    ```

    `X-RateLimit-Reset` is **Unix epoch seconds**. On a `429`, honour `Retry-After` (seconds) and
    retry exactly once — there's a copy-paste backoff loop in curl, TypeScript, Python, and Go on
    the [Rate limits](/rate-limits#handle-it-wait-for-the-reset-then-retry) page.
  </Accordion>
</AccordionGroup>

## SDKs & npm package names

<AccordionGroup>
  <Accordion title="What are the exact npm package names?">
    Phosra's client libraries publish under **`@phosra/*`**, and the vendor-neutral OCSS reference
    library under **`@openchildsafety/*`**. There is **no published `@ocss/*` npm scope** — if you
    saw that anywhere, it's stale. Versions verified live against the npm registry on 2026-07-06:

    | Package                                                                        | Version | What it does                                                                                   |
    | ------------------------------------------------------------------------------ | ------- | ---------------------------------------------------------------------------------------------- |
    | [`@phosra/sdk`](https://www.npmjs.com/package/@phosra/sdk)                     | `0.1.0` | Typed control-plane client (`PhosraClient`) — families, children, policies, rules, enforcement |
    | [`@openchildsafety/ocss`](https://www.npmjs.com/package/@openchildsafety/ocss) | `0.1.3` | OCSS reference library — sign/verify receipts, sealed envelopes, Trust List, vocabulary        |
    | [`@phosra/link`](https://www.npmjs.com/package/@phosra/link)                   | `0.1.2` | Writer-plane SDK — consent ceremony + signed rule-write directives                             |
    | [`@phosra/gatekeeper`](https://www.npmjs.com/package/@phosra/gatekeeper)       | `0.2.2` | Platform-side — verify signed profiles, local decision engine, §8.3.8 receipts                 |
    | [`@phosra/connect`](https://www.npmjs.com/package/@phosra/connect)             | `0.1.1` | Embeddable Connect component (the "Plaid Link" for parental-controls apps)                     |
    | [`@phosra/mcp`](https://www.npmjs.com/package/@phosra/mcp)                     | `0.4.0` | MCP server exposing Phosra tools to AI agents (`npx @phosra/mcp`)                              |
    | [`@phosra/cli`](https://www.npmjs.com/package/@phosra/cli)                     | `0.2.0` | Partner CLI — scaffold configs, verify OCSS setup, sandbox round-trip checks                   |
    | [`@phosra/provider`](https://www.npmjs.com/package/@phosra/provider)           | `0.1.0` | Provider-side helpers over `@openchildsafety/ocss`                                             |
    | [`@phosra/classify`](https://www.npmjs.com/package/@phosra/classify)           | `0.1.1` | Rule-category classification helpers                                                           |

    Start with [`@phosra/sdk`](/sdks/typescript) for the REST API, or the
    [SDK overview](/sdks/overview) for the full map.
  </Accordion>

  <Accordion title="Is there a Python / Go / iOS / Android SDK?">
    **TypeScript** is the first-party REST SDK today; a typed Python client is planned. Every other
    language calls the plain-JSON REST API directly — the [Quickstart](/quickstart) proves the curl,
    Python, and Go paths run copy-paste against the live sandbox, and the [OpenAPI spec](/openapi.yaml)
    drives code generators for any language. The native **iOS** and **Android** enforcement SDKs are
    distributed **privately** (not on npm or Maven Central) — email
    [developers@phosra.com](mailto:developers@phosra.com) for repo access. See
    [iOS SDK](/sdks/ios) and [Android SDK](/sdks/android).
  </Accordion>

  <Accordion title="Can I use the MCP server with Claude / other agents?">
    Yes — `npx @phosra/mcp --api-key=KEY` exposes Phosra's tools over the Model Context Protocol to
    any MCP-compatible agent (Claude, and others). See [MCP Server](/sdks/mcp-server).
  </Accordion>
</AccordionGroup>

## Going to production

<AccordionGroup>
  <Accordion title="How do I move from sandbox to production?">
    Two changes, no code rewrite: (1) swap the base URL from the sandbox host to
    `https://prodapi.phosra.com/api/v1`, and (2) swap your `phosra_test_…` key for a `phosra_live_…`
    key from the same [Keys page](https://dashboard.phosra.com/dashboard/developers/keys). Request
    and response shapes are identical. Note that production and sandbox Trust Lists differ — the
    production census admits only accredited parties (governance-gated). See
    [Authentication → Verify your setup](/authentication#verify-your-setup).
  </Accordion>

  <Accordion title="How do I rotate or revoke a key?">
    Both operations use your WorkOS session bearer. `POST …/keys/{keyId}/regenerate` issues a new
    secret in place (the old one stops working); `DELETE …/keys/{keyId}` kills a key permanently
    (any request with it then returns `401 "API key has been revoked"`). For zero-downtime rotation,
    mint a second key, deploy it, then revoke the old one. Full steps:
    [Authentication → Rotation](/authentication#rotation).
  </Accordion>
</AccordionGroup>

## Still stuck?

<CardGroup cols={2}>
  <Card title="Troubleshooting" icon="wrench" href="/troubleshooting">
    Symptom-first fixes — find what you're seeing, run the check, apply the fix.
  </Card>

  <Card title="Errors reference" icon="list" href="/errors">
    Every HTTP status and error `class`, each with its cause, its fix, and a live example.
  </Card>

  <Card title="Authentication" icon="key" href="/authentication">
    How API keys, WorkOS sessions, and RFC 9421 request signing fit together.
  </Card>

  <Card title="Support" icon="life-ring" href="/support">
    Reach a human — the developer inbox, filing bugs, and honest response expectations.
  </Card>
</CardGroup>
