Skip to content
Sendora Cloud
Create account
2026-05-29 · Platform

Device-takeover on signIn + global plan-gate UX + settings polish

  • Closed the duplicate-push class of bug architecturally. When a previously-anonymous device signs in to an identified account, Sendora retires the anon `user_id`, reassigns its push tokens, hard-deletes the anon user row, and emits `auth.device_takeover` — one device always resolves to one identity. Wired on every signin path: email-password, social (Google / GitHub / Apple / Microsoft / LinkedIn / Facebook / Discord), magic link, email OTP, passkey, MFA challenge, OIDC SSO, SAML SSO.
  • Customer-side cleanup signals: subscribe `auth.device_takeover` webhook to delete the matching row from your own users mirror (full server-pipeline path), OR register `auth.onDeviceTakeover(cb)` inline in your app code (zero infra). Inline path lives on `@sendoracloud/sdk-react-native` 1.0.5 + `@sendoracloud/sdk-web` 3.0.1 + iOS / Android 4.0.5. Full mechanism + examples at /docs/device-takeover.
  • Belt + braces — recipient-dedup at the messaging dispatch layer: email + SMS sends within a 60s window to the same `(orgId, recipient, content)` are suppressed and tagged with `status='suppressed'` + `metadata.suppressed_reason='recent_duplicate'` for audit trail.
  • Global TierGateModal — any 402 `tier_required` or 403 `ENTITLEMENT_ERROR` from the API now triggers a universal upgrade prompt instead of a silent failure. New `usePlan()` + inline `UpgradeNotice` primitives wired across Push (geofences / live-activities / templates / A-B tests) + Audiences pages.
  • Email-domain settings: BYOD plan gate is now surfaced inline (amber Growth+ callout + disabled input) instead of silent save() no-ops. Same posture across the dashboard — destructive paths emit toasts on every preflight failure.
  • Step-up wiring (Touch ID / passkey) on API-key revoke + Stripe billing portal button. 'Manage subscription' is hidden on Free plan + backend returns typed 409 `NO_BILLING_ACCOUNT` instead of opaque 500.
  • Settings sweep — Modules page deleted (read-only dead weight), Identity folded into API Keys as an Advanced card. Security page hardened — 8s `/auth/refresh` timeout + 10s Web Locks acquisition timeout + module-level toast singleton (root cause of an intermittent ~50 GETs / 12s infinite-loop on hot-reload).
  • Migration 0056 (SSO state rows carry `prev_anon_refresh_token`). Backend on `f90f302`. 742/742 tests + 11/11 shared-drift green throughout.