@freshworks/react-native-freshdesk-sdk
Version:
React Native wrapper for Freshdesk Android and iOS SDKs
563 lines (397 loc) • 21.9 kB
Markdown
# Freshdesk React Native SDK — Integration Knowledge Base
Use this document to **answer customer integration questions** directly — like a support knowledge base. Search by topic or symptom before writing code.
**Related references:** [integration-faq.md](integration-faq.md) · [platform-apis.md](platform-apis.md) · [examples.md](examples.md) · [SKILL.md](SKILL.md)
---
## Quick answers — most asked
| Question | Answer |
|----------|--------|
| How do I install? | `npm install @freshworks/react-native-freshdesk-sdk`, then `cd ios && pod install`. Autolinking handles native linking. |
| Where do I get credentials? | Freshdesk portal → **Admin Settings → Mobile Chat SDK** → your SDK → App Keys (`token`, `host`, `sdkId`). |
| What goes in `.env`? | `FRESHDESK_TOKEN`, `FRESHDESK_HOST`, `FRESHDESK_SDK_ID`, optional `FRESHDESK_LOCALE`, `FRESHDESK_JWT`. Never commit `.env`. |
| Host with or without `https://`? | **Both work.** `yourcompany.freshdesk.com` and `https://yourcompany.freshdesk.com` — SDK auto-prefixes `https://` (1.2.2+). |
| Minimum React Native version? | **0.75.0+**. Backward-compatible with both the New Architecture (TurboModule) and the classic bridge. |
| Minimum iOS / Android? | iOS **15.0+**, Android **minSdk 26**, `compileSdk 35` (wrapper's fallback if the app doesn't set its own), AGP **8.6+**. |
| Do I need push for chat/support? | **No.** In-app support, chat, and KB work with JS init only. Push is optional. |
| How do I open support? | `await FreshdeskSDK.openSupport()` after `initialize()`. |
| How do I open FAQ/KB? | `await FreshdeskSDK.openKnowledgeBase()`. |
| How do I verify integration? | `await FreshdeskSDK.runDiagnostics()` — apply each check's `fixHint`. |
| How do I use the AI kit? | Copy from `node_modules/.../ai-integration-kit/.` into app root; prompt your AI tool to use the skill. |
| `pod install` fails — `native-versions.json`? | Upgrade to SDK **1.2.2+** (file must be in npm tarball). |
| Widget spinner forever on Android? | Check double init (MainApplication + JS), host format, JWT. See [Spinner / blank widget](#spinner--blank-widget-android). |
| `openSupport`/`trackEvent`/`setUserProperties` silently no-op on iOS right after `initialize()`? | Native SDK's own async load isn't done yet — the wrapper's `initialize()` settle delay fixes this. See [Support widget or trackEvent/setUserProperties silently fail (iOS)](#support-widget-or-trackeventsetuserproperties-silently-fail-ios). |
---
## Getting started
### Q: What is `@freshworks/react-native-freshdesk-sdk`?
A React Native wrapper around the native Freshdesk Android and iOS SDKs. It exposes JavaScript APIs for in-app support chat, knowledge base, user identity (JWT or properties), content customization, and diagnostics. Push is wired **natively** — there is no JS push API.
### Q: What are the install steps?
```bash
npm install @freshworks/react-native-freshdesk-sdk
cd ios && pod install # iOS only
```
**iOS Podfile (2.0.0+):**
```ruby
platform :ios, '15.0'
```
Nothing else Freshdesk-specific — the native SDK is a vendored, statically
linked xcframework, so it never forces `use_frameworks!` and needs no
`post_install` hook of its own. (Apps still pinned to `~1.4.x` need the old
SPM setup instead — see [troubleshooting.md](troubleshooting.md); upgrading
is simpler.)
**Android:** Ensure `mavenCentral()` in repositories, `minSdkVersion 26`. `compileSdkVersion`/`targetSdkVersion` are inherited from your app's own `ext` block; the wrapper falls back to **35** (AGP 8.6+) if your app doesn't set them.
### Q: How do I use the AI Integration Kit?
After npm install:
```bash
cp -R node_modules/@freshworks/react-native-freshdesk-sdk/ai-integration-kit/. .
```
Then ask your AI tool: *"Use the freshdesk-react-native-integration skill."* The kit teaches agents greenfield integration, Q&A (this document), and diagnostics-driven debugging.
### Q: What is the sample app?
The SDK repo includes `sample_app/` — reference wiring for init, push, JWT, content config, and diagnostics. Point customers to it for copy-paste patterns.
---
## Credentials & configuration
### Q: Which credentials are required?
| Field | Required | Source |
|-------|----------|--------|
| `token` | Yes | Portal → Mobile Chat SDK → App Keys |
| `host` | Yes | Your Freshdesk instance URL |
| `sdkId` | Yes | Portal → Mobile Chat SDK |
| `locale` | No | Default `en` — widget language |
| `jwt` | Only if JWT enforced | Server-generated per user |
### Q: Where should credentials live?
| Layer | File | Keys |
|-------|------|------|
| JavaScript | `.env` (gitignored) | `FRESHDESK_TOKEN`, `FRESHDESK_HOST`, `FRESHDESK_SDK_ID`, `FRESHDESK_LOCALE`, `FRESHDESK_JWT` |
| Android push (native) | `android/app/build.gradle` → `buildConfigField` | Same names as BuildConfig |
| iOS push (native) | `ios/<App>/Info.plist` | `FreshdeskToken`, `FreshdeskHost`, `FreshdeskSdkId`, `FreshdeskLocale`, `FreshdeskJwt` |
**Never ask customers to paste secrets in chat.** Point them to these files.
### Q: What host format should I use?
Either format works (1.2.2+):
```bash
FRESHDESK_HOST=yourcompany.freshdesk.com
# or
FRESHDESK_HOST=https://yourcompany.freshdesk.com
```
The SDK normalizes bare domains to `https://` automatically in JS, Android, and iOS.
### Q: How do I wire `.env` to JavaScript?
Use `react-native-dotenv` (or equivalent):
1. `babel.config.js` — include the plugin
2. `src/types/env.d.ts` — declare `FRESHDESK_*` keys
3. Import: `import { FRESHDESK_TOKEN } from '@env'`
4. **Restart Metro** after changing `.env`
---
## Initialization
### Q: How do I initialize the SDK?
Call once, early (root component or provider):
```typescript
import FreshdeskSDK from '@freshworks/react-native-freshdesk-sdk';
await FreshdeskSDK.initialize({
token: FRESHDESK_TOKEN,
host: FRESHDESK_HOST, // bare or https:// — both OK
sdkId: FRESHDESK_SDK_ID,
locale: FRESHDESK_LOCALE || 'en',
jwt: FRESHDESK_JWT, // only if JWT enforced
debugMode: __DEV__, // Android Logcat only
});
```
### Q: Can I call `initialize()` more than once?
**No** — once per app session. For credential changes, call `resetUser()` then re-init, or use `authenticateAndUpdate(jwt)` for JWT refresh.
### Q: What errors can `initialize()` throw?
| Code | Meaning | Fix |
|------|---------|-----|
| `FRESHDESK_INVALID_CONFIG` | Missing token, host, or sdkId | Fill `.env`, restart Metro |
| `FRESHDESK_INIT_ERROR` | Native bootstrap failed | Check credentials, network, portal SDK config |
| `FRESHDESK_INIT_TIMEOUT` | **Android only** — init did not complete in 20s | Verify token/host/sdkId; check JWT if enforced; check network reachability to the host |
iOS has no equivalent timeout-based rejection — its `initialize()` has no
failure signal from the native SDK at all, so it only rejects on a
programmer error (missing config, or a call before it resolves). If iOS
`initialize()` genuinely never resolves, that points at a network/credential
problem the native SDK isn't surfacing — try Android with the same
credentials to get a clearer error, or check reachability to the host
directly.
### Q: Why does `initialize()` always take a couple of seconds on iOS?
By design. The native iOS SDK gives no signal for when it's actually
finished its own internal async loading, so the wrapper holds
`initialize()`'s promise for a fixed ~2s settle delay past the native call
returning before resolving — otherwise a call made right after `await
initialize()` (completely normal usage) could race the SDK's real load and
silently fail. See [platform-apis.md](platform-apis.md) for the full
explanation and `CHANGELOG.md` for which version added it. Do not treat this
delay itself as a bug; do treat a `openSupport()`/`trackEvent()`/etc. call
that fails or no-ops **after** `initialize()` has resolved as a real issue.
### Q: JS-only init vs native init — when do I need native?
| Scenario | Init location |
|----------|---------------|
| In-app support / chat / KB only | **JS only** — `FreshdeskSDK.initialize()` in app |
| Push while app is open | JS init + register FCM/APNs token after init |
| Push when app killed/background (Android) | Native init **inside `FirebaseMessagingService` only** — not `MainApplication.onCreate` |
| Push when app killed/background (iOS) | Native init in `AppDelegate` (APNs token arrives before JS) |
> **Android critical:** Do **not** call native `FreshdeskSDK.initialize()` in `MainApplication.onCreate()` **and** JS `initialize()` — this double-init breaks `openSupport()` (spinner forever). Use JS for in-app; FCM service for headless push.
---
## Opening support & knowledge base
### Q: How do I open the support widget?
```typescript
await FreshdeskSDK.openSupport();
```
Wire to a button, settings item, or tab. Guard until init completes.
### Q: How do I open the knowledge base / FAQ directly?
```typescript
await FreshdeskSDK.openKnowledgeBase();
```
### Q: How do I open a specific topic?
```typescript
await FreshdeskSDK.openTopic({ topicName: 'Billing' });
// optional:
await FreshdeskSDK.openTopic({ topicName: 'Billing', topicId: '123' });
```
### Q: How do I dismiss the widget?
```typescript
await FreshdeskSDK.dismiss();
```
### Q: How do I show unread message count?
```typescript
const count = await FreshdeskSDK.getUnreadCount();
FreshdeskSDK.addUnreadCountListener((event) => {
console.log(event.count);
});
// cleanup: subscription?.remove()
```
---
## JWT authentication
### Q: How do I know if JWT is required?
Check Freshdesk portal widget settings, or run iOS diagnostics — look for `remoteConfig.jwtEnforced`. Symptoms without JWT: spinner after open, user state `jwtNotPresent` or `notAuthenticated`.
### Q: How do I wire JWT?
```typescript
await FreshdeskSDK.initialize({ ..., jwt: userJwt });
FreshdeskSDK.addUserStateListener(({ state }) => {
if (state === 'authExpired') {
// fetch new JWT from your server
FreshdeskSDK.authenticateAndUpdate(newJwt);
}
});
// on logout:
await FreshdeskSDK.resetUser();
```
### Q: Can I use `setUserProperties` with JWT?
**No** — when JWT is enforced, user identity comes from the JWT payload. Use `setUserProperties` only for non-JWT SDKs.
### Q: Where is JWT generated?
Server-side, signed with the encryption key from Admin → Mobile Chat SDK → JWT settings. See [Freshdesk JWT docs](https://support.freshdesk.com/en/support/solutions/articles/50000011580-enable-jwt-authentication).
---
## Push notifications
### Q: Is there a JavaScript push API?
**No.** Push is native-only. Forward FCM/APNs tokens and messages in native code.
### Q: Android push setup summary
1. Firebase project + `google-services.json` in `android/app/`
2. Google Services Gradle plugin
3. `FirebaseMessagingService` — forward token + messages:
```kotlin
FreshdeskSDK.setPushRegistrationToken(token)
if (FreshdeskSDK.isFreshdeskSDKNotification(remoteMessage)) {
FreshdeskSDK.handleFCMNotification(remoteMessage)
}
```
4. For **headless/killed** delivery: initialize natively inside the messaging service (see sample app `FreshdeskInitializer`), **not** in `MainApplication.onCreate`
5. Upload FCM credentials to Freshdesk portal
6. `POST_NOTIFICATIONS` permission (Android 13+)
### Q: iOS push setup summary
1. Push Notifications + Background Modes capabilities in Xcode
2. APNs `.p8` key uploaded to Freshdesk portal
3. Native init in `AppDelegate` (token arrives before JS)
4. Forward all three delivery paths: foreground, tap, background/killed
5. Test on a **real device** (Simulator cannot receive remote pushes)
### Q: Push works in foreground but not background?
**iOS:** Missing `application:didReceiveRemoteNotification:fetchCompletionHandler:` or Background Modes → Remote notifications.
**Android:** SDK not initialized before `handleFCMNotification` in headless process — init in FCM service, wait for ready before handling.
---
## Content configuration & locale
### Q: How do I change widget text / labels?
```typescript
await FreshdeskSDK.setContentConfiguration({
headers: { chat: 'Talk to us', faq: 'Help Centre' },
placeholders: { replyField: 'Type here...', searchField: 'Search...' },
actions: { tabChat: 'Chat' },
});
```
Pass `{}` to reset to widget defaults. Changes persist immediately.
### Q: How do I set the widget language?
Pass `locale` at init: `locale: 'fr'`, `locale: 'de'`, etc. Also set `FRESHDESK_LOCALE` in `.env` and native credential files if using push.
---
## Events & listeners
| Event | API | Platform |
|-------|-----|----------|
| Unread count changed | `addUnreadCountListener(cb)` | Both |
| User auth state changed | `addUserStateListener(cb)` | Both |
| New user created | `addUserCreatedListener(cb)` | **iOS only** |
| Link pressed in widget | `setLinkHandler(cb)` | Both |
Always clean up: `subscription?.remove()` and `FreshdeskSDK.removeAllListeners()` on unmount.
**User states:** `authenticated`, `authExpired`, `notAuthenticated`, `identifierUpdated`, `jwtNotPresent`, `undefined`
---
## User management
### Q: How do I set user info (non-JWT)?
```typescript
await FreshdeskSDK.setUserProperties({
name: 'Jane Doe',
email: 'jane@example.com',
phone: '+1234567890',
});
```
On iOS, only call this **after `initialize()` has actually resolved** — see
the [settle-delay Q&A](#q-why-does-initialize-always-take-a-couple-of-seconds-on-ios)
above. It won't error, it will just silently not take effect if called too
early on an SDK version without the fix.
### Q: How do I set ticket defaults?
```typescript
await FreshdeskSDK.setTicketProperties({ subject: 'App support', priority: 3 });
```
### Q: What happens on logout?
```typescript
const result = await FreshdeskSDK.resetUser();
// result.success === true when cleared
```
**iOS caveat:** the native SDK has no failure callback for `resetUser()` —
the promise always resolves `{ success: true }` once the SDK is initialized,
even if the reset didn't actually succeed server-side. It only **rejects**
for the programmer-error case (called before `initialize()` resolved). Don't
rely on `result.success === false` as a cross-platform failure signal — see
`PLATFORM_DIFFERENCES.md`.
---
## Analytics
### Q: How do I track a custom event?
```typescript
await FreshdeskSDK.trackEvent('purchase_completed', { amount: 42, currency: 'USD' });
```
**Platform difference:** Android passes property values through with their
original type (`string | number | boolean`); iOS coerces every value to a
string before handing it to the native SDK. The same call above reaches
Android's analytics backend with a numeric `amount` and iOS's with the
string `"42"`. If your backend does numeric aggregation on a property, be
aware iOS always reports it as text. See `PLATFORM_DIFFERENCES.md`. On iOS,
only call after `initialize()` has actually resolved (same settle-delay
caveat as `openSupport()`/`setUserProperties()`).
---
## Diagnostics & debugging
### Q: How do I run diagnostics?
```typescript
await FreshdeskSDK.enableDebugLogs(true);
const report = await FreshdeskSDK.runDiagnostics();
console.log(report.prettyPrinted);
```
Each check has `id`, `status` (`pass|warn|fail|skipped`), `details`, and `fixHint`. **Apply `fixHint` verbatim.**
### Q: iOS vs Android diagnostics?
| | iOS | Android |
|---|-----|---------|
| Depth | Full native report | Wrapper checks + `runtime.diagnostics: skipped` |
| Extra signal | Native logs via `enableDebugLogs` | `debugMode: true` in init + Logcat |
### Q: Key diagnostic checks
| Check | Meaning |
|-------|---------|
| `runtime.sdkInitialized` | Init completed |
| `runtime.nativeModule` | RN bridge linked |
| `config.token` / `config.host` / `config.sdkId` | Credentials present |
| `network.configEndpoint` | Portal reachable with credentials |
| `runtime.doubleInit` (Android) | Native + JS double init — remove MainApplication init |
| `config.hostScheme` (Android) | Bare host — auto-normalized on 1.2.2+ |
| `push.*` | Push wiring status |
---
## Troubleshooting by symptom
### Native module not found
1. `npm install @freshworks/react-native-freshdesk-sdk`
2. iOS: `cd ios && pod install` (2.0.0+ needs nothing else — no `use_frameworks!` to confirm)
3. Android: `./gradlew clean`, rebuild
4. Restart Metro; **rebuild native app** (not just JS reload)
### Spinner / blank widget (Android)
1. **Double init** — remove native init from `MainApplication.onCreate`; use JS init only for in-app
2. **Host** — ensure valid host; both bare and `https://` work on 1.2.2+
3. **JWT** — if enforced, pass valid `jwt` at init
4. Run diagnostics; check `runtime.doubleInit`, `config.hostScheme`
### Support widget or `trackEvent`/`setUserProperties` silently fail (iOS)
**Symptom:** `initialize()` resolves fine, but `openSupport()` /
`trackEvent()` / `setUserProperties()` / `setTicketProperties()` called
right after do nothing — no UI, no error, no data on the dashboard.
Intermittent, or "it worked the second time." The native SDK may log
`"Tasks will be executed once the SDK is loaded"`.
1. **Root cause:** the native SDK has no readiness signal of its own and
keeps loading asynchronously after `initialize()` returns; a call issued
too early is silently dropped by the native SDK itself, not by this
wrapper.
2. **Fix:** confirm the SDK version includes the `initialize()` settle-delay
fix (`CHANGELOG.md`) — it makes plain `await initialize()` usage safe.
3. **If still on an older version:** add a short delay (1–2s) after
`initialize()` resolves before the first other call, or upgrade.
4. See [platform-apis.md](platform-apis.md) for the full explanation.
### `FRESHDESK_INVALID_CONFIG`
Empty `token`, `host`, or `sdkId`. Check `.env`, `@env` babel plugin, Metro restart.
### `FRESHDESK_NOT_INITIALIZED` / `FRESHDESK_NOT_READY`
Called SDK method before `initialize()` resolved. Await init in provider; guard UI buttons.
`FRESHDESK_NOT_READY` (Android) additionally means `initialize()` resolved
but the native SDK's own readiness check hasn't confirmed yet — retry after
a short wait, it self-resolves.
### Android build — `FreshdeskModule.kt` "Argument type mismatch … `HashMap<String, Any?>` … `Map<String, Any>`"
RN 0.79+ with Kotlin 2.1.x, failing at lines 253 / 268 / 283 of the wrapper's
`FreshdeskModule.kt`. RN ≥ 0.79 made `ReadableMap.toHashMap()` return
`HashMap<String, Any?>`; wrapper ≤ 2.0.0 passed it into the native SDK's
`Map<String, Any>` and Kotlin 2.1 rejects it. Wrapper bug — upgrade to
`@freshworks/react-native-freshdesk-sdk@2.0.1+`, then `./gradlew clean`. No
app-side change; do not edit files under `node_modules/`. Different from the
Kotlin *metadata* mismatch (that one needs `freshdesk-consumer.gradle`).
### iOS crash — Library not loaded FreshdeskSDK.framework
**`~1.4.x` only.** Missing Embed Frameworks step in Podfile `post_install`
for the old SPM path. On **2.0.0+** this cannot happen — the native SDK is a
vendored static xcframework with nothing to embed via SPM. Upgrade instead
of chasing the `post_install` fix. See troubleshooting doc.
### iOS `pod install` — native-versions.json missing
Upgrade to `@freshworks/react-native-freshdesk-sdk@1.2.2+`.
### Push not received
1. Run diagnostics — check `push.*` checks
2. iOS: real device, capabilities, `.p8` in portal, all three notification paths
3. Android: `google-services.json`, FCM in portal, messaging service registered, headless init in FCM service
### Metro ECONNREFUSED
Start Metro; for USB Android: `adb reverse tcp:8081 tcp:8081`
---
## API quick reference
```typescript
// Core
FreshdeskSDK.initialize(config)
FreshdeskSDK.openSupport()
FreshdeskSDK.openKnowledgeBase()
FreshdeskSDK.openTopic({ topicName, topicId? })
FreshdeskSDK.dismiss()
FreshdeskSDK.getUnreadCount()
// User
FreshdeskSDK.setUserProperties(props) // non-JWT only
FreshdeskSDK.setTicketProperties(props)
FreshdeskSDK.authenticateAndUpdate(jwt)
FreshdeskSDK.resetUser()
FreshdeskSDK.getUser()
// Customization
FreshdeskSDK.setContentConfiguration(config)
// Analytics
FreshdeskSDK.trackEvent(name, props?)
// Diagnostics
FreshdeskSDK.enableDebugLogs(true)
FreshdeskSDK.runDiagnostics()
// Events
FreshdeskSDK.addUnreadCountListener(cb)
FreshdeskSDK.addUserStateListener(cb)
FreshdeskSDK.addUserCreatedListener(cb) // iOS only
FreshdeskSDK.setLinkHandler(cb)
FreshdeskSDK.removeAllListeners()
// Info
FreshdeskSDK.getSDKVersion()
// Error codes (typed enum — match on these instead of string literals)
import { FreshdeskErrorCode } from '@freshworks/react-native-freshdesk-sdk';
// e.g. FreshdeskErrorCode.NOT_INITIALIZED, FreshdeskErrorCode.INIT_TIMEOUT
```
Not every `FreshdeskErrorCode` member is emitted on both platforms — Android
has several method-specific codes iOS doesn't (and vice versa for two iOS
ones). Check each member's JSDoc, or [platform-apis.md](platform-apis.md),
for which platform(s) can throw it.
Full types: see `docs/integration/api_reference.md` in the SDK repo.
---
## Agent instructions — answering questions
When a customer asks an integration question (not a full greenfield request):
1. **Search this knowledge base** for the topic or symptom.
2. **Answer directly** with the relevant section — include code snippets when helpful.
3. **Cite platform differences** from [platform-apis.md](platform-apis.md) when Android vs iOS differ.
4. If the question implies a bug, switch to **Mode B** — run or ask for `runDiagnostics()` output.
5. If they want full integration, switch to **Mode A** — phased workflow in [SKILL.md](SKILL.md).
6. Never invent credentials. Point to portal and file paths.
7. If unsure, say what you know and what to verify (diagnostics, Logcat, portal settings).