Legal Consent

Publish Terms of Service and Privacy Policy versions from the dashboard, and require signed-in users to accept a material update inside your app. When enabled, a blocking gate appears the moment the version takes effect: the user must accept the new document — or sign out — before they can keep using the app. Manage documents under Dashboard → Legal (workspace-level, with a picker for the app the documents belong to).

Publish a version

  1. Open Legal, pick a document — Terms, Privacy or DPA (Cookies is marked Coming soon) — and click New version.
  2. Write the content, set the effective date, and choose how it's delivered:
    • Notify users by email — announce + reminder emails (optional).
    • Require in-app acceptance — the blocking gate. This is what drives the SDK.
  3. Publish. Signed-in users are gated from the effective date; users who joined before the version took effect are the audience — brand-new users are bound at sign-up and never re-prompted.
Works for Terms AND Privacy. “Require in-app acceptance” is one doc-type-agnostic switch — it defaults ON for Terms of Service and OFF for Privacy/DPA/Cookies, but you can turn it on for any document.
Privacy Policy caveat. Under GDPR a privacy notice is an information duty (Art. 13/14), not a consent gate — that's why it's notify-only by default. Only require in-app acceptance for a Privacy version when the update introduces genuinely new consent-based processing.

How the SDK gate works

Every SDK reads a server-computed blocking flag (enforcement is decided on the server, never the client clock). When a blocking document is outstanding, the SDK presents the hosted gate and, on the user's choice:

  • Accept → records the acceptance, then re-checks for the next stacked document.
  • Sign out → signs the user out (there is no “dismiss”).

Updates arrive in real time — the moment you publish, the gate appears in every running, signed-in app over a live connection. No polling, no restart.

Per-platform behaviour

PlatformPresentationWhat you wire
SwiftHosted gate in a WKWebView; app content is held behind it until resolved.Nothing while OneloAuthView is mounted; use .oneloConsentGate(auth:) on your post-login UI.
ElectronHosted gate in a modal BrowserWindow — non-dismissible, blocks the app.Call onelo.consent.setGateParent(mainWindow) so the gate is modal over your window.
JS / WebFull-screen, non-dismissible SDK overlay (the browser blocks cross-origin iframing of the hosted page).Nothing — on by default. Return to login on sign-out (see below).
React NativeHosted gate in a non-dismissible modal WebView.Mount <ConsentGateModal consent={onelo.consent} /> once, near your app root.
FlutterHosted gate in a WebView that wraps your widget tree.Wrap your post-login tree in OneloConsentGate.
AndroidHosted gate in a dedicated Activity, auto-presented on resume.Call onelo.consent.registerLauncher(activity, auth) { result -> … } in onCreate.
On every platform except Swift the SDK clears the session on Sign out but cannot re-render your UI to the login screen — wire a single sign-out handler so a declined gate returns to sign-in. On Android the decline arrives as result.declined (already signed out); on Web, Electron, React Native and Flutter, listen for the session going null.

Web / Electron — return to login on sign-out

Use ONE SDK instance and subscribe to session changes on onelo.auth — the same instance the consent gate signs out. A separate auth instance would never receive the event.

javascript — Web (@onelo/js)
import { Onelo } from '@onelo/js'

const onelo = new Onelo({ publishableKey: '...', apiUrl: '...' })

// The blocking gate auto-presents on sign-in and on publish. Nothing to wire.
// Just return to your login UI when the gate's "Sign out" clears the session:
onelo.auth.onAuthStateChange((session) => {
  if (!session) showLoginScreen()   // declined gate, revoke, or expiry
})
javascript — Electron (@onelo/electron)
import { Onelo } from '@onelo/electron'

const onelo = new Onelo({ publishableKey: '...', apiUrl: '...', protocol: 'onelo-my-app' }) // YOUR app's unique scheme — see /docs/security/app-identity
const auth = onelo.auth                     // same instance the gate signs out

app.whenReady().then(async () => {
  const mainWindow = new BrowserWindow({ show: false })
  onelo.consent.setGateParent(mainWindow)   // required for a true modal hard block

  auth.onSessionChange((userId) => {
    if (!userId) { mainWindow.hide(); void auth.presentAuthWindow(null) }
  })
  // ...create + show your window after sign-in
})

Custom UI (drive it yourself)

To build your own gate instead of the hosted one, disable auto-present and read the pending documents. This is the same API the built-in gate uses.

javascript
const onelo = new Onelo({ /* ... */ autoPresentConsentGate: false })

// React to a live publish:
onelo.consent.onConsentRequired(() => renderYourBanner())

// Read + record:
const pending = await onelo.consent.requiredConsents()
const blocker = pending.find(c => c.blocking)   // block | notify | unknown
if (blocker) {
  // show blocker.url (the full document), then on accept:
  await onelo.consent.acceptConsent(blocker.versionId)
}

Availability

SDKConsent gate
Swift (iOS / macOS)Available
ElectronAvailable
JS / TS (Web)Available
React NativeAvailable
FlutterAvailable
AndroidAvailable
Legal Consent — Onelo Docs