LifeCare.ID® Developer Docs Public

Sandbox ✅

Engage with LifeCare.ID without requesting access. The sandbox is an isolated instance where you self-register an app and mint a synthetic signed-in user in seconds, then run the full sign-in to grant to token to introspect loop. It holds only synthetic, zero-PHI data, and it is separate from production.


1. Register your app (self-service)

On the hosted sandbox, pass your developer access token (from /devaccess) as a Bearer. (A local sandbox you run yourself needs no token.)

DEVACCESS=   # the token shown on the /devaccess verify page
curl -s -X POST "$SANDBOX/v1/sandbox/clients" \
  -H "Authorization: Bearer $DEVACCESS" \
  -H "Content-Type: application/json" \
  -d '{"name":"My app","redirect_uris":["http://localhost:4000/callback"]}'
{
  "client_id": "sbx_Zy47k_NDW4E",
  "client_secret": "qDkujP10i72…",
  "redirect_uris": ["http://localhost:4000/callback"],
  "note": "Sandbox client. Store the secret now - it is not shown again."
}

You now have a client_id and client_secret, the same credentials an operator would otherwise issue you. redirect_uris is an exact-match allowlist; pass every callback URL you will use.

2. Mint a test identity (no passkey)

Production sign-in needs a real WebAuthn passkey on a device, which you cannot script. In the sandbox you can mint a synthetic signed-in user instead, so the whole loop runs headlessly (CI, curl, tests):

curl -s -X POST "$SANDBOX/v1/sandbox/test-identity" -H "Content-Type: application/json" -d '{}'
{
  "did": "did:web:sandbox.example:u:817612d13f3219b6",
  "session_token": "lcs_…",
  "expires_at": "2026-06-24T09:00:00+00:00",
  "note": "Synthetic sandbox identity (no passkey)."
}

The session_token is a real user/session token. Use it two ways:

3. Run the whole loop

With a client and a test identity you can exercise everything:

# (a) act as the signed-in user: set the session cookie, then run /authorize -> /token
#     (this is what the app quickstart does for you)
# (b) grant consent for a scope
curl -s -X POST "$SANDBOX/v1/consent/grant" -H "Content-Type: application/json" -d '{
  "subject_did":"'"$DID"'","grantee":{"name":"My app","id":"'"$CLIENT_ID"'"},
  "scopes":["cgm"],"purpose":{"text":"demo"},"duration_days":1}'

# (c) issue a data-access token, then introspect it
curl -s -X POST "$SANDBOX/v1/token" -H "Content-Type: application/json" -d '{
  "subject_did":"'"$DID"'","scope":"cgm","purpose":"demo","requester":"'"$CLIENT_ID"'"}'
curl -s -X POST "$SANDBOX/v1/token/introspect" -H "Content-Type: application/json" -d '{"token":"lc_…"}'

The quickstart repos run this end to end. Point them at your sandbox base URL and the credentials from step 1.


What the sandbox is (and isn't)


Run it locally

The sandbox is the same service with one flag. To run your own:

LIFECAREID_SANDBOX=true \
LIFECAREID_STATE_DIR=/tmp/lifecareid-sandbox \
LIFECAREID_DOMAIN=sandbox.local \
uv run uvicorn --factory lifecareid:create_app --port 8821
# → http://localhost:8821   (set SANDBOX=http://localhost:8821 for the curls above)

Operators host a shared sandbox by running this profile as a separate instance with isolated state; the production instance leaves LIFECAREID_SANDBOX unset, so it never exposes these routes.