Developer changelog
SDK and API releases.
Tied to SDK version numbers. Separate from the product changelog.
2026-05-14API · 1.8.0
- Deep-link SDK surface DX polish: `fallbackUrl` now optional on `POST /sdk/links` — backend defaults from your project's apps registry (web origin > iOS App Store URL > Android Play Store URL).
- New `GET /sdk/links/:shortcode/stats` — totals, unique clicks, deferred matches, by-OS / by-country / by-device breakdowns. Project-scoped via the public key.
- New `POST /sdk/links/:shortcode/revoke` — idempotent soft-delete (private-content unsend). Worker cache busted on flip.
- OpenAPI spec coverage expanded; spec version 1.8.0.
2026-05-14React Native · 0.16.0
- Typed `linkData<T>` generic across `links.create<T>(...)` / `onLinkOpened<T>(cb)` — branch your deep-link router on a discriminated union, not casts.
- Typed `LinkError` class with codes (`BUNDLE_MISMATCH`, `DATA_TOO_LARGE`, `EXPIRED`, `RATE_LIMITED`, `PLAN_LIMIT`, `FALLBACK_REQUIRED`, ...). `err instanceof LinkError` + `switch (err.code)` instead of `err.message.includes(...)`.
- `links.prewarm(input, { key })` — background-mint + cache so share-row taps skip the HTTPS round-trip ActivityIndicator.
- `links.attachLinkingApi({ onLegacyUrl })` — one-call replacement for `Linking.getInitialURL` + `addEventListener` + extract-pre-check + fallthrough.
- `links.computeDeviceFingerprint()` baked in — no `expo-crypto` / `Dimensions` / `Intl` boilerplate per customer.
- `links.matchDeferred()` auto-probes `react-native-play-install-referrer` (Android) + computes the fingerprint (fallback).
- `links.revoke(shortcode)` + `links.getStats(shortcode)` — no more dashboard scraping.
- `fallbackUrl` now optional; SDK config gains `linkDomain` for custom share hosts (e.g. `pulse.link`).
- `examples/links-share.tsx` + `examples/links-router.tsx` shipped in the npm tarball.
2026-05-14iOS · 3.9.0
- Typed `SendoraCloudLinks.LinkError` with `code: LinkErrorCode` — pattern-match on `.bundleMismatch / .planLimit / .rateLimited / ...`.
- `LinkCreateInput(typedLinkData:)` Codable constructor + `event.decodedLinkData() throws -> T` typed accessor.
- `links.prewarm(_:key:)` + `links.create(_:prewarmKey:)` — background-mint cache.
- `links.revoke(shortcode:completion:)` + `links.getStats(shortcode:completion:)`.
- `SendoraCloudLinks.computeDeviceFingerprint()` — canonical recipe matching Android + RN byte-for-byte.
- `fallbackUrl` now optional. Domain-aware `extractShortcode(from:allowedHosts:)` honours `config.linkHosts`.
2026-05-14Android · 3.8.0
- Typed `SendoraCloudLinks.LinkError(code: LinkErrorCode, ...)` — `when (err.code)` exhaustive match.
- `links.prewarm(input, key)` + `links.create(input, prewarmKey)` — background-mint cache.
- `links.revoke(shortcode)` + `links.getStats(shortcode)`.
- `SendoraCloudLinks.computeDeviceFingerprint()` — canonical recipe matching iOS + RN.
- `matchDeferred(...)` auto-computes the fingerprint when neither input is supplied.
- Domain-aware `extractShortcode(uri, allowedHosts)` honours `config.linkHosts`.
2026-05-13API · 1.7.0
- New SDK-facing deep-link surface: POST /sdk/links (auto-shortcode, bundle-id gated, 2KB linkData cap, 200/hr per IP, plan-limited), GET /sdk/links/:shortcode (warm-path resolve, tenant-guarded), POST /sdk/links/match (deferred match by Android Play Install Referrer OR iOS fingerprint hash, 24h window).
- Public-key (pk_*) gated — Branch / AppsFlyer parity. Leaked key + wrong bundle = 400.
- Successful deferred match flips click_events.matched=true so re-installs don't double-resolve.
- OpenAPI spec coverage expanded; spec version 1.7.0.
2026-05-13React Native · 0.15.0
- New `sendora.links` namespace — `create({ title, fallbackUrl, iosDeepLinkPath, androidDeepLinkPath, linkData, ... })` returns share URL.
- `links.handleUniversalLink(url)` resolves a Universal Link / App Link delivery + fires `onLinkOpened` callback with the link's linkData JSON.
- `links.matchDeferred({ installReferrer | fingerprintHash })` for cold-launch deferred deep links after install. Fires `onLinkOpened` with `isDeferred: true`.
- Optional `iosBundleId` + `androidPackageName` in init() — forwarded to backend bundle-id gate.
2026-05-13iOS · 3.8.0
- New `SendoraCloud.links` surface — `create(input, completion:)`, `handleUniversalLink(url:completion:)`, `matchDeferred(input, completion:)`, `onLinkOpened(_:)`.
- Backed by new `/sdk/links/*` public-key API; warm-path resolve fills `LinkOpenedEvent.linkData` so host app routes via business id (e.g. `linkData["articleId"]`).
- Bundle id auto-supplied from `Bundle.main.bundleIdentifier`; backend rejects if it doesn't match a registered app for the project.
2026-05-13Android · 3.7.0
- New `SendoraCloud.links` surface — `create(input, onResult)`, `handleAppLink(uri, onResult?)`, `matchDeferred({ installReferrer?, fingerprintHash? }, onResult)`, `onLinkOpened { ... }`.
- Package name auto-supplied from app context; backend bundle-id gate rejects mismatches.
- Android Play Install Referrer wins over fingerprint match when both are present — 100% accurate vs probabilistic. Caller fetches via `com.android.installreferrer:installreferrer` (compileOnly).
2026-05-12Web · 2.12.0
- hydrate(): restore the cached user when REFRESH_KEY is present even if the access token is missing/expired (was: required both). Fixes cold-start regression where a navigation that cleared the access token made the user appear signed out even though the refresh chain was alive.
2026-05-12React Native · 0.14.0
- hydrate(): restore the cached user when REFRESH_KEY is present even if the access token is missing/expired (was: required both). Fixes the Pulse News cold-start bug where the SDK appeared signed-out on every launch and host apps minted a fresh anonymous user, fragmenting analytics across sessions.
2026-05-12iOS · 3.7.0
- getAccessToken(): trigger refresh when the access token is missing OR past expiry (was: returned nil immediately when missing). Fixes the cold-start regression where a valid refresh token in Keychain was ignored.
2026-05-12Android · 3.6.0
- getAccessToken(): trigger refresh when the access token is missing OR past expiry (was: returned null immediately when missing). Fixes the cold-start regression where a valid refresh token in EncryptedSharedPreferences was ignored.
2026-05-12Web · 2.11.0
- auth.refreshAccessToken now wipes local identity on a dead-token response (INVALID_REFRESH_TOKEN / UNAUTHORIZED / HTTP_401 / RATE_LIMIT). Stops the indefinite retry loop when the stored refresh token is invalid — host app sees signed-out state instead.
2026-05-12React Native · 0.13.0
- auth.refreshAccessToken now wipes local identity on a dead-token response (INVALID_REFRESH_TOKEN / UNAUTHORIZED / HTTP_401 / RATE_LIMIT). Pre-this fix the SDK silently retried the same dead token forever.
2026-05-12iOS · 3.6.0
- refreshAccessToken now wipes local identity on a dead-token response (INVALID_REFRESH_TOKEN / UNAUTHORIZED / HTTP_401 / RATE_LIMIT). Stops the indefinite retry loop when the stored refresh token is invalid.
2026-05-12Android · 3.5.0
- refreshAccessToken now wipes local identity on a dead-token response (INVALID_REFRESH_TOKEN / UNAUTHORIZED / HTTP_401 / RATE_LIMIT). Stops the indefinite retry loop when the stored refresh token is invalid.
2026-05-12API · 1.6.0
- POST /auth-service/token/refresh: new error code INVALID_REFRESH_TOKEN (HTTP 401) with Retry-After: 60. 10s rotation grace window — replaying the predecessor within 10s of a successful rotation no longer trips reuse-detection (concurrent-refresh race).
- Per-IP failure back-off on /auth-service/token/refresh: 5 fails in 60s → 60s 429 with Retry-After.
- Session cleanup keeps revoked rows 7 days (was: hourly delete). Preserves reuse-detection forensics.
2026-05-08Web · 2.10.0
- New Audiences API: sendora.audiences.list / get / create / update / delete.
- Re-exports Audience + AudienceRule + AudienceRuleGroup types.
- HttpClient: added patch() method.
2026-05-08iOS · 3.5.0
- New SendoraCloudPush module — registerToken(token, platform: .ios, ...) + trackOpen(sendId, clickAction).
- SendoraCloud.push.* surface added on configure().
- APIClient: added get(path:completion:) overload.
2026-05-08Android · 3.4.0
- New SendoraCloudPush module — registerToken(token, platform = ANDROID, ...) + trackOpen(sendId, clickAction).
- SendoraCloud.push.* surface added on init().
- Coroutine-scoped registration via existing SDK CoroutineScope.
2026-05-07iOS · 3.4.0
- Server-managed geofences via CLLocationManager (CRUD + 20-region OS cap).
- SendoraCloud.geofences.start() / refresh() / stop().
- geofence.entered / .exited events fire to backend.
2026-05-07Android · 3.3.0
- Server-managed geofences via GeofencingClient (CRUD + 100-region OS cap).
- SendoraCloud.geofences.start(ctx) / refresh / stop / handleBroadcast(intent).
- geofence.entered / .exited / .dwelled events fire to backend.
2026-05-06iOS · 3.3.0
- iOS Critical Alerts — SendoraCloudCriticalAlerts.requestPermission { granted in }.
- Send body criticalAlert: { soundName?, volume? } — APNs aps.sound{critical:1} + interruption-level=critical.
- Apple entitlement com.apple.developer.usernotifications.critical-alerts required.
2026-05-05iOS · 3.2.0
- iOS Live Activities (ActivityKit, push-type=liveactivity).
- SendoraCloud.liveActivities.track(activity, activityType, externalId, userId) — watches activity.pushTokenUpdates.
- Server-side update / end via PATCH+DELETE /push/live-activities/:id.
2026-05-05Android · 3.2.0
- Android Live Updates — data-only FCM push routed to NotificationCompat.
- SendoraCloud.liveActivities.start(fcmToken, activityType, attributes, contentState, ...) + handleFcmMessage(ctx, data, buildNotification).
- ProgressStyle on API 34+; BigTextStyle fallback on older Android.
2026-05-04React Native · 0.11.0
- Push: sendora.push.registerToken({ token, platform: 'ios' | 'android' | 'web' }).
- Bare workflow only — Expo managed apps use webhook-bridge pattern (see /docs/push#expo).
- Auto-track lifecycle: app.opened, app.backgrounded, screen.viewed.
2026-05-03Web SSR · 0.2.0
- @sendoracloud/sdk-web-ssr — HttpOnly-cookie sessions for Next.js / Remix / SvelteKit.
- createSendoraServerClient(cookies(), { publicKey }) — server-side identify + track auto-binding.
- Edge middleware sendoraMiddleware({ protected, loginPath }).
2026-05-08API · 1.5.0
- OpenAPI spec coverage expanded — full server-to-server push surface now in spec: /orgs/:orgId/push/{send,sends,sends/:id,sends/stats,sends/delivery-health,templates,ab-tests,settings,vapid,live-activities,geofences}.
- SDK-facing public push endpoints in spec: /push/track-open, /push/live-activities/start-token, /push/geofences/{list-for-device,event}.
- Audiences CRUD in spec: /orgs/:orgId/audiences.
- New schemas: PushSendInput, PushSend, PushTemplate, PushAbTest, PushSettings, PushDeliveryHealth, LiveActivity, Geofence, Audience.
- Path drift fix: /sms/send → /orgs/:orgId/sms/send; /sms/suppress* → /orgs/:orgId/sms/suppressions* (matches real backend org-scoping).
2026-05-08API · 1.2.0
- OpenAPI 3.1 spec at /api/v1/docs/openapi.json.
- Push: /push/sends/delivery-health, DELETE /push/sends/:id (cancel scheduled), GET /push/sends/:id.
- Automation IaC: GET .../export, POST .../import (idempotent by name); workflow-bundle.v1.json schema.
- Audiences CRUD on /orgs/:orgId/audiences.
- Webhook event catalog canonical at /docs/webhooks (~30 event types across push / LA / geofence / email / sms / auth / workflow).
2026-04-15Web · 1.0.0
- Initial GA.
- identify / track / link APIs with background retries.
- Automatic consent attachment on every event.
2026-04-15iOS · 1.0.0
- SwiftPM package shipped.
- APNs token management + silent push support.
- Deferred deep linking via AASA.
2026-04-15Android · 1.0.0
- Maven Central publish.
- FCM token registration + refresh.
- Install referrer + assetlinks handshake.
2026-04-15API · v1
- OpenAPI 3.1 spec published.
- /v1/events, /v1/identify, /v1/links, /v1/notifications GA.
- HMAC-signed outbound webhooks with exponential backoff.