# Switch from another support tool

> Replace your current in-app support SDK with Concierge in one prompt for your coding agent. Call mapping, identity, knowledge, conversation history and a checklist.

Source: https://conciergesdk.com/docs/migrate/

Already running another in-app chat or helpdesk SDK? Paste the prompt below into Claude Code, Cursor, Codex or any coding agent. It installs Concierge from the platform guide, swaps every SDK call, keeps identity verified, moves your saved replies into knowledge, and imports your conversation history so nobody is left waiting. The short version lives at [conciergesdk.com/switch](/switch/).

## Before you start

- A Concierge app. Sign up, add your app and copy the **app key** (`app_...`) and the **identity secret** (`ids_...`). See [Getting started](https://conciergesdk.com/docs.md).
- Optional but worth it: connect the [MCP server](https://conciergesdk.com/docs/mcp.md) to your agent, so it can write knowledge drafts for you.
- A clean git working tree, so the whole switch is one reviewable change.

There is no free plan and no trial, but you can do the whole switch before you pay: install, configure and send test messages from up to 3 of your own devices. Importing conversation history is part of Pro.

## The prompt

Replace `app_YOUR_APP_KEY` with your app key, then paste it into your agent from the root of your app's repo.

```text
Replace this project's in-app support SDK with Concierge (https://conciergesdk.com).
My Concierge app key: app_YOUR_APP_KEY

1. Read https://conciergesdk.com/docs/migrate.md and the guide for this platform
   (https://conciergesdk.com/llms.txt lists every page as Markdown).
2. Find the current support SDK. Search the dependency files (Package.swift, Podfile,
   project.pbxproj, build.gradle or build.gradle.kts, package.json, app.json, pubspec.yaml)
   and the code for its init or configure, identify or login, logout, custom attributes or
   metadata, open chat, unread count, push token and notification calls. List every call site
   before changing anything.
3. Install Concierge by following the install steps in the guide for this platform: add the
   package, then call configure with app_YOUR_APP_KEY. Show me the diff before you write it.
   For a website, add @conciergesdk/web.
4. Swap each call using the mapping table in migrate.md. Keep the same user id the old SDK used.
   Custom attributes become string metadata; never put names or emails in metadata.
5. Identity: if my backend signed a user hash or JWT for the old tool, add an endpoint that signs
   a Concierge identity token (HS256, sub = user id, exp at most 30 days) with the identity secret,
   and pass it to identify. The secret stays on the server, never in the app or the repo.
6. Remove the old SDK completely: dependency, imports, keys, plist or manifest entries and its
   push handler. Leave no dead code behind.
7. Saved replies, macros and help articles: if the Concierge MCP server is connected, add each
   one with upsert_knowledge and draft: true, one article per topic. Otherwise write them to
   support-export/knowledge/ as Markdown. If the old tool hosts a help center, give me its URL
   so I can add it under Knowledge, Sources, Add a link.
8. Conversations: if the old tool has an export or an API, write every thread to
   support-export/conversations/conversations.json in the shape migrate.md shows, add
   support-export/ to .gitignore and never commit it. Import it with the import_conversations
   MCP tool.
   Import is part of Pro. List the customers still waiting for a reply.
9. Build, open Support in the app and send "Hello from the switch". Confirm the dashboard
   shows Connected, or check it with the verify_install MCP tool.
10. Report: files changed, each old call and what replaced it, anything left as
    TODO(concierge), the knowledge drafts, the imported conversations and the waiting customers.
```

## The CLI

Prefer the terminal? `npx @conciergesdk/cli` does the same steps by hand. Each one shows the diff before it writes:

```sh
npx @conciergesdk/cli login      # sign in with your browser and save a key
npx @conciergesdk/cli init       # detect the app, install the SDK, configure it, wait for the first message
npx @conciergesdk/cli migrate    # rewrite the old support SDK's calls where it recognizes them
npx @conciergesdk/cli doctor     # check the install and config
```

Add `--dry-run` to see the changes without writing anything. The prompt still covers more ground (saved replies, help center, waiting customers), so use it when you have a coding agent at hand.

## Call mapping

Nearly every in-app support SDK has the same handful of calls. Names vary, the jobs don't.

| Job | Typical old call | iOS | Android | React Native | Flutter | Web |
|---|---|---|---|---|---|---|
| Start the SDK | init, configure, setup, start | `Concierge.configure(appKey:)` | `Concierge.configure(context, appKey)` | `Concierge.configure({ appKey })` | `Concierge.configure(appKey: ...)` | `Concierge.configure({ appKey })` |
| Sign a user in | identify, login, setUser, registerUser | `Concierge.identify(userId:name:email:token:)` | `Concierge.identify(userId, name, email, token)` | `Concierge.identify(userId, { name, email, token })` | `Concierge.identify(userId, name:, email:, token:)` | `Concierge.identify(userId, { name, email, token })` |
| Verified identity | user hash, HMAC, identity JWT | `token: HS256 JWT from your server` | `token = HS256 JWT` | `token: HS256 JWT` | `token: HS256 JWT` | `token: HS256 JWT` |
| User attributes | custom attributes, user properties, metadata | `Concierge.setMetadata([String: String])` | `Concierge.setMetadata(mapOf(...))` | `Concierge.setMetadata({ ... })` | `Concierge.setMetadata({ ... })` | `Concierge.setMetadata({ ... })` |
| Events and logs | logEvent, track, breadcrumbs | `Concierge.breadcrumb(_:)` | `Concierge.breadcrumb(...)` | `Concierge.breadcrumb(...)` | `Concierge.breadcrumb(...)` | `Concierge.breadcrumb(...)` |
| Open the chat | show, present, showMessenger, openChat | `Concierge.present() or ConciergeChatView()` | `Concierge.open(context)` | `Concierge.open()` | `Concierge.open()` | `Concierge.open()` |
| Unread badge | unreadCount, unread listener | `Concierge.shared.unreadCount` | `Concierge.unreadCount (StateFlow)` | `useUnreadCount()` | `Concierge.unreadCountStream` | `Concierge.onUnreadCountChange(...)` |
| Push token | setDeviceToken, registerPush, setFCMToken | `Concierge.registerPushToken(_:)` | `Concierge.registerPushToken(token)` | `Concierge.registerPushToken(token)` | `Concierge.registerPushToken(token)` | Not needed |
| Notification tap | handlePush, isSupportNotification | `Concierge.handleNotification(userInfo)` | `Concierge.handleNotification(data)` | `Concierge.handleNotification(data)` | `Concierge.handleNotification(data)` | Not needed |
| Sign out | logout, reset, clearUser | `Concierge.logout()` | `Concierge.logout()` | `Concierge.logout()` | `Concierge.logout()` | `Concierge.logout()` |

Metadata values are strings shown next to the conversation. Never put names or emails in metadata: pass them to `identify`.

## Before and after

The "before" snippets are a generic pattern, not any one vendor's API. Your old calls will have their own names.

### iOS

```swift
// Before
import SupportSDK

SupportSDK.configure(apiKey: "OLD_KEY")
SupportSDK.login(userId: user.id, email: user.email, userHash: hash)
SupportSDK.setCustomAttributes(["plan": "Pro"])
SupportSDK.setDeviceToken(token)

Button("Help") { SupportSDK.showMessenger() }
SupportSDK.logout()
```

```swift
// After
import Concierge

Concierge.configure(appKey: "app_YOUR_APP_KEY")
Concierge.identify(userId: user.id, name: user.name, email: user.email,
                   token: try await api.conciergeToken())
Concierge.setMetadata(["plan": "Pro"])
Concierge.registerPushToken(token)

Button("Help") { Concierge.present() }
Concierge.logout()
```

Full reference: [iOS guide](https://conciergesdk.com/docs/ios.md).

### Android

```kotlin
// Before
import com.example.support.SupportSDK

SupportSDK.init(this, apiKey = "OLD_KEY")
SupportSDK.login(userId = user.id, email = user.email, userHash = hash)
SupportSDK.setCustomAttributes(mapOf("plan" to "Pro"))
SupportSDK.setFcmToken(token)

Button(onClick = { SupportSDK.showMessenger(context) }) { Text("Help") }
SupportSDK.logout()
```

```kotlin
// After
import com.conciergesdk.Concierge

Concierge.configure(this, appKey = "app_YOUR_APP_KEY")
Concierge.identify(userId = user.id, name = user.name, email = user.email,
                   token = api.conciergeToken())
Concierge.setMetadata(mapOf("plan" to "Pro"))
Concierge.registerPushToken(token)

Button(onClick = { Concierge.open(context) }) { Text("Help") }
Concierge.logout()
```

Full reference: [Android guide](https://conciergesdk.com/docs/android.md).

### React Native

```tsx
// Before
import Support from 'react-native-support-sdk';

Support.configure({ apiKey: 'OLD_KEY' });
Support.login({ userId: user.id, email: user.email, userHash: hash });
Support.setCustomAttributes({ plan: 'Pro' });
Support.setPushToken(token);

<Button title="Help" onPress={() => Support.showMessenger()} />
Support.logout();
```

```tsx
// After
import Concierge from '@conciergesdk/react-native';

Concierge.configure({ appKey: 'app_YOUR_APP_KEY' });
Concierge.identify(user.id, { name: user.name, email: user.email,
                              token: await api.conciergeToken() });
Concierge.setMetadata({ plan: 'Pro' });
Concierge.registerPushToken(token);

<Button title="Help" onPress={() => Concierge.open()} />
Concierge.logout();
```

Full reference: [React Native guide](https://conciergesdk.com/docs/react-native.md).

### Flutter

```dart
// Before
import 'package:support_sdk/support_sdk.dart';

SupportSdk.init(apiKey: 'OLD_KEY');
SupportSdk.login(userId: user.id, email: user.email, userHash: hash);
SupportSdk.setCustomAttributes({'plan': 'Pro'});
SupportSdk.setPushToken(token);

TextButton(onPressed: () => SupportSdk.showMessenger(), child: const Text('Help'))
SupportSdk.logout();
```

```dart
// After
import 'package:concierge_sdk/concierge_sdk.dart';

Concierge.configure(appKey: 'app_YOUR_APP_KEY');
Concierge.identify(user.id, name: user.name, email: user.email,
                   token: await api.conciergeToken());
Concierge.setMetadata({'plan': 'Pro'});
Concierge.registerPushToken(token);

TextButton(onPressed: () => Concierge.open(), child: const Text('Help'))
Concierge.logout();
```

Full reference: [Flutter guide](https://conciergesdk.com/docs/flutter.md).

### Web

```html
<!-- Before -->
<script src="https://cdn.example-support.com/widget.js"></script>
<script>
  window.Support('boot', { app_id: 'OLD_ID', user_id: user.id,
                           email: user.email, user_hash: hash });
  window.Support('update', { plan: 'Pro' });
  helpButton.onclick = () => window.Support('show');
  window.Support('shutdown');
</script>
```

```ts
// After
import { Concierge } from '@conciergesdk/web';

Concierge.configure({ appKey: 'app_YOUR_APP_KEY' });
Concierge.identify(user.id, { name: user.name, email: user.email,
                              token: await api.conciergeToken() });
Concierge.setMetadata({ plan: 'Pro' });
helpButton.onclick = () => Concierge.open();
Concierge.logout();
```

Full reference: [Web guide](https://conciergesdk.com/docs/web.md).

## Identity

Most support SDKs verify users with a hash or a JWT your server signs. Concierge does the same with an HS256 JWT: `sub` is your user id, `exp` is at most 30 days away, and the key is the app's identity secret. **Require verified users** is on by default for new apps, so an `identify` without a token is refused. Sign the token on your server, fetch it when the user signs in, and pass it as `token`. Details: [Identity verification](https://conciergesdk.com/docs/identity.md).

Keep the user id your app already sends. Returning users then land in the same Concierge customer on every device.

## Knowledge and saved replies

Concierge has no separate saved reply list: the AI agent and your team both work from knowledge articles.

- **Saved replies and macros:** with the MCP server connected, the agent adds each topic with `upsert_knowledge` and `draft: true`. Drafts never reach the AI until you publish them on the Articles tab.
- **A hosted help center:** add its URL under **Knowledge, Sources, Add a link**. Concierge reads it, drafts one article per feature and reads it again every 7 days. See [Knowledge autopilot](https://conciergesdk.com/docs/knowledge.md).
- **Your codebase:** run the `build-knowledge` MCP prompt to draft articles from your screens, settings and paywall.

## Conversation history

When the old tool has an export or an API, the prompt writes every thread to `support-export/conversations/conversations.json` (kept out of git) and imports it:

- **MCP:** the agent calls `import_conversations` with up to 100 conversations and 2,000 messages per call.
- **CLI:** `npx @conciergesdk/cli import support-export/conversations/conversations.json` sends the file in request sized batches. Add `--dry-run` to check the file first.

The file looks like this:

```json
{
  "conversations": [
    {
      "externalId": "old-thread-8812",
      "customer": { "userId": "user_42", "name": "Ana", "email": "ana@example.com" },
      "status": "open",
      "metadata": { "plan": "Pro" },
      "messages": [
        { "id": "m1", "role": "customer", "body": "Sync stopped after the update", "createdAt": "2026-09-30T08:12:00Z" },
        { "id": "m2", "role": "team", "authorName": "Sam", "body": "Thanks, looking into it", "createdAt": "2026-09-30T09:01:00Z" }
      ]
    }
  ]
}
```

- `externalId` is the old tool's thread id. Importing the same thread again changes nothing, so a rerun after an error is safe.
- `customer.userId` is the user id your app passes to `identify`, so the history lands under the same customer the app signs in.
- `role` is `customer`, `team`, `ai` or `system`. `createdAt` is ISO 8601 with a time zone. Messages are sorted by time.
- `status` is `open` or `closed`. `metadata` holds short string facts, never names or emails.

What import does and doesn't do:

- Imported threads join the customer's Concierge conversation as history, before any new messages, and are marked imported.
- They never trigger AI replies, push notifications or emails, and they count as read.
- Tokens, keys, emails and IP addresses inside message text are replaced with placeholders.
- Imported data is part of the customer's data export and is erased with it.
- Limits: 1,000 messages per conversation, 10,000 characters per message, 120 import requests an hour and 50,000 conversations a day per workspace.
- Import is part of Pro. Every import is recorded in the audit log.

Then answer the customers still waiting from the Concierge inbox once their app update is out.

## Push

Forward the device token to `Concierge.registerPushToken` and route notification taps through `Concierge.handleNotification`. Upload your APNs auth key (`.p8`) or Firebase service account JSON in the dashboard. Remove the old SDK's push handler so a tap never opens two chats. See [Push notifications](https://conciergesdk.com/docs/push.md).

## Checklist

1. Create your Concierge app and copy the app key (app_...) and identity secret (ids_...).
2. Connect the Concierge MCP server to your coding agent, so it can write knowledge drafts.
3. Paste the prompt, review the diff your agent shows, and let it apply.
4. Add the identity token endpoint on your server, and keep Require verified users on.
5. Forward push tokens to Concierge and upload your APNs key or FCM service account in the dashboard.
6. Review and publish the knowledge drafts, then turn the AI agent on.
7. Import your conversation history (Pro), then reply to the waiting customers from the Concierge inbox.
8. Ship the release. Keep the old inbox open, read only, until its last open thread closes.
9. Cancel the old plan once nobody on an old app version still uses it.

## FAQ

### Which support SDKs can the prompt replace?

Any in-app chat or helpdesk SDK with the usual shape: configure, identify, attributes, open chat, push and logout. Those calls exist in nearly every support SDK, so the agent maps them one to one. Anything without a safe equivalent is commented out with a TODO(concierge) note so the project still builds and you decide.

### Which coding agents does it work in?

Claude Code, Cursor, Codex, Windsurf, VS Code with Copilot, Gemini CLI, Zed or any agent that can read a URL and edit files. Moving knowledge drafts needs the Concierge MCP server connected; without it the agent writes them to Markdown files for you.

### Can I bring my conversation history?

Yes, on Pro. When the old tool has an export or an API, the prompt writes every thread to one JSON file (kept out of git) and imports it with the import_conversations MCP tool. Each thread lands under the same customer, marked imported, and never triggers AI replies, push or emails. Running it again is safe: threads already imported are skipped.

### Do my users have to sign in again?

No. The agent keeps the user id your app already passes, so a returning user lands in the same Concierge customer on every device once identify carries a signed token.

### Can I run both SDKs side by side?

Yes, while you test. Ship Concierge in one build and keep the old dashboard open for users on older app versions until their threads close.

### What about my saved replies and help center?

Saved replies and articles become Concierge knowledge drafts, one per topic. A hosted help center can be added as a link source instead: Concierge reads it, drafts articles and checks the pages again every 7 days. Drafts never reach the AI until you publish them.

### Is there a free trial to test the switch?

No free plan and no trial. Before you pay you can do the whole switch, follow the install steps and send test messages from up to 3 of your own devices. Real users and AI replies start with Pro, one flat monthly price with 1,000 AI replies included. See [pricing](/#pricing) for the founder price and spots left.

### What if my agent gets something wrong?

The prompt asks the agent to list every call site first and show you the diff before it writes, and the dashboard (or the verify_install MCP tool) confirms the install afterwards. It is all a normal code change, so review it and revert with git like any other.
