Authentication
How parents sign in, how child devices pair, and which credential each part of the API expects.
SafeCircle has two kinds of callers, and each has its own credential.
| Caller | Credential | Obtained by | Sent as |
|---|---|---|---|
| Parent in a browser | Session cookie | Signing in on the dashboard | Cookie, automatically with credentials: "include" |
| Parent in a native app | Session token | Signing in through the API | Authorization: Bearer <token> |
| Child device | Device token | Pairing with a 6-digit code | x-device-token: <token> |
Parent authentication is handled by Better Auth at /api/auth/*. The data API then reads the same session.
Parent accounts
Sign-up
Sign-up is open to everyone. New accounts need a verified email, and the SafeCircle team can pause registration at any time.
- Email verification. Email and password sign-up requires a verified address. The verification link expires after 24 hours, and the user is signed in automatically once they verify.
- Registration switch. While registration is paused, sign-up (email or social) fails with
400and the codeSIGNUPS_DISABLED, the same answer for registered and unregistered emails. Admins flip the switch in the dashboard. - Mobile apps. The Android apps skip the captcha only with a valid Play Integrity attestation. Web sign-up uses Cloudflare Turnstile.
Sign-in methods
| Method | Notes |
|---|---|
| Email and password | Passwords are checked against known breaches in production. Reset links expire after 1 hour |
| Social sign-in | Google, Apple, Discord and X (Twitter), each enabled only when configured |
| Passkeys | WebAuthn, with the relying party name SafeCircle |
| Authenticator app (2-step verification) | TOTP with backup codes. Issuer name SafeCircle |
A last-used method is remembered per user, and a user can be signed in on several devices at once.
Bot protection
When Cloudflare Turnstile is configured, the protected requests (such as sign-in and sign-up) must include the challenge result in an x-captcha-response header. The official Android apps satisfy this check through a separate mechanism that is not available to third parties.
Sessions
On the web, the session is a cookie. Its name starts with safecircle, it is shared across *.safecircle.tech, and it is set Secure with SameSite=None so that fetch with credentials works from other SafeCircle subdomains. Your calls must include credentials, and your origin must be on the CORS list.
Native apps use the bearer plugin instead. The sign-in response contains a token, which you send as Authorization: Bearer <token>.
Useful Better Auth routes:
| Route | Purpose |
|---|---|
POST /api/auth/sign-in/email | Sign in. Returns { token, user } |
POST /api/auth/sign-up/email | Create an account. Needs email, password and name |
GET /api/auth/get-session | Current session and user, or null |
POST /api/auth/sign-out | End the session |
GET /api/auth/list-sessions, POST /api/auth/revoke-session | See and sign out your sessions |
POST /api/auth/request-password-reset, POST /api/auth/reset-password | Password reset |
POST /api/auth/two-factor/enable, .../verify-totp, .../disable | 2-step verification |
GET /api/auth/passkey/list-user-passkeys, DELETE /api/auth/passkey/delete-passkey | Passkey management |
The full surface, including social sign-in redirects and passkey registration, is documented by Better Auth.
get-session is called on every page load, so it uses the general rate-limit bucket. Other /api/auth/* routes are limited to 30 requests per minute per IP.
Child devices
A child's phone never has a parent account. It pairs once and keeps a device token.
The parent creates a code
A signed-in parent calls devices.createPairingToken for a child. The response is a 6-digit code valid for 5 minutes. Creating a new code invalidates any earlier unused code for that child.
The child app redeems it
The child app calls devices.pair with the code and a device name. This procedure is public. The code is claimed atomically, so it works once.
The device keeps a token
The response contains a random 256-bit deviceToken, a deviceId and the childId. The app stores them and sends the token in x-device-token on every later call.
Revoking
A parent calls devices.revoke. The device record is deleted, so the token stops working immediately.
A device token can create alerts (alerts.create), request a safe-search family link (search.familyLink) and submit feedback (POST /api/feedback). It cannot read any data. It does not expire on its own; revoking it is the only way to end it.
Other credentials
| Credential | Used by | Notes |
|---|---|---|
| Telegram link code | Dashboard and Telegram bot | 8 characters, valid for 10 minutes, deleted when used. See Telegram bot |
| Family token | Child app and SafeCircle Search | Signed with HMAC, bound to a device, valid for 365 days. Once the device is unpaired, the parent's site rules stop applying. See SafeCircle Search |
| Bot key | Telegram bot to backend | Server to server only |
| Admin role | SafeCircle staff | Gives access to admin procedures such as the registration switch, which are not documented here |