Skip to main content
Connections do not last forever. A parent declines the consent screen, revokes access later, or removes a platform and adds it back. This guide covers every path — the decline, the production teardown, and the reconnect — with the sandbox portions run live.
Sandbox-first. The consent and reconnect paths below run against https://phosra-api-sandbox-production.up.railway.app with no key. The production teardown (DELETE /compliance/{linkID}) is family-scoped and requires a phosra_live_… key, so it is documented here rather than run anonymously.

Before you start

export PHOSRA_BASE="https://phosra-api-sandbox-production.up.railway.app"
You should already understand the connect ceremony from Connect a platform — disconnect and reconnect are its mirror image.
1

Handle a declined connection

When a parent chooses Deny on the consent page, Phosra redirects back to your redirect_uri with an error and no code. Never treat a missing code as success:
Phosra sandbox consent page titled 'Connect your family to this app?' listing Mia, Leo, and Ava, with Approve and Deny buttons; Deny triggers the access_denied redirect
GET /oauth/authorize?client_id=did:ocss:loopline&decision=deny&redirect_uri=…&state=xyz789
302 Location: https://loopline.example/callback?error=access_denied&state=xyz789
Your callback handler must branch on the query string:
// In your redirect_uri handler
const url = new URL(request.url);
if (url.searchParams.get("error")) {
  // e.g. "access_denied" — the parent declined. Stop; do not exchange a token.
  return showConnectDeclined();
}
const code = url.searchParams.get("code");
// …proceed to the token exchange
Always compare the returned state against the value you generated before you act on either branch. A mismatched state means the response is not for this request — discard it.
Query parameters on the deny redirect
FieldTypePresentDescription
errorstringon denyaccess_denied — the parent declined. No token is issued.
statestringalwaysThe state you sent; compare before acting.
codenever on denyAbsent. If error is set, do not look for a code.
How to branch
Callback containsMeaningDo
error=access_deniedParent declinedShow a “connection declined” state; offer to retry later.
code=…Parent approvedProceed to the token exchange (see Connect a platform).
2

Tear down an account-linked platform (production)

For platforms you connected with a stored per-family credential (POST /compliance), remove the link with DELETE /compliance/{linkID}. This stops all future enforcement to that platform and is the durable “disconnect” for account-linked integrations:
curl -s -X DELETE "https://prodapi.phosra.com/api/v1/compliance/$LINK_ID" \
  -H "X-Api-Key: $PHOSRA_API_KEY"
GET /families/{familyID}/compliance returns every active link with its link_id, so you can find the one to remove. Both endpoints are family-scoped: an anonymous caller who is not a member of the family gets 403 not a member of this family. List active links first, then delete the one you mean to remove.
Path parameter
FieldTypeRequiredDescription
linkIDstring (UUID)yesThe link_id from GET /families/{familyID}/compliance.
Request header
HeaderRequiredDescription
X-Api-Keyyes (production)Your phosra_live_… key. The caller must be a member of the family.
Response
StatusMeaning
204 / 200Link removed; no further enforcement is pushed to that platform.
Errors
StatusmessageWhen it happens
403not a member of this familyThe caller is not authorised for the family that owns the link.
404compliance link not foundNo link exists for that linkID.
These endpoints are family-scoped and require an authenticated member, so they cannot be exercised anonymously against the open sandbox — the statuses above are the production contract. The sandbox does confirm the shape: an anonymous DELETE of an unknown id returns 404 compliance link not found, and GET /families/{id}/compliance returns 403 not a member of this family.
3

Reconnect — the parent approves again

Reconnecting is simply re-running the connect ceremony. Send the parent back to the consent page with a fresh state; on Approve you get a new code, and the token exchange yields a new access token. The old token is never reused.
GET /oauth/authorize?client_id=did:ocss:loopline&decision=approve&redirect_uri=…&state=abc123
302 Location: https://loopline.example/callback?code=sbxauth_9LuPuXKcNA5…&state=abc123
curl -s -X POST "$PHOSRA_BASE/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "authorization_code",
    "code": "sbxauth_9LuPuXKcNA5…",
    "client_id": "did:ocss:loopline",
    "redirect_uri": "https://loopline.example/callback"
  }'
Real response (200):
{
  "access_token": "sbxtok_tEMgnUnshlI_TSaZQWaxIH9mcdgjwdXT",
  "expires_in": 3600,
  "scope": "profiles",
  "token_type": "Bearer"
}
The connection is live again. Child profiles flow from GET /oauth/profiles exactly as they did before — pick up from the profiles step of Connect a platform.
Identical to the first connect. Two reconnect-specific rules:
FieldTypeDescription
statestringUse a fresh value for the reconnect, distinct from the original grant.
codestringThe new code from the re-approval — an old code from the first grant must not be replayed.
Response fields (200)
FieldTypeDescription
access_tokenstringA new bearer token; the previous one is not reused.
expires_ininteger3600 (1 hour).
scopestringprofiles — the same grant as before (equivalent to discovery’s child_profiles.read).
token_typestringBearer.
Full request-field and error tables live in Connect a platform → the token step.

After you reconnect

Re-run enforcement so the platform receives the current policy — a reconnected platform starts from a clean slate:
curl -s -X POST "https://prodapi.phosra.com/api/v1/children/$CHILD_ID/enforce" \
  -H "X-Api-Key: $PHOSRA_API_KEY"
See Set up a family & kids for the full enforce-and-confirm loop.

Next steps

Connect a platform

The full connect ceremony this guide mirrors.

Test in the sandbox

Everything you can exercise without a key, and how the Trust List works.