Contact Support
One Contact Support gives every App a focused customer conversation system:
- Send a Message
- Report a Bug
- Suggest a Feature
- Complete customer/support conversation history
- Optional email and push notifications when Support replies
- Exact-thread notification deep links when the host supports push
The SDK generates an internal subject from the App and runtime. Do not show a subject field to customers.
Headless Desktop
Section titled “Headless Desktop”Use @repo/sdk-web-core for Electron, macOS, Windows, or another headless JS
runtime. Create one client for the app process and set its actual platform.
import { createOneClient, type SupportDiagnostics } from '@repo/sdk-web-core';
const one = createOneClient({ publicKey: onePublicKey, platform: 'macos', // or 'windows' appVersion});
function collectSupportDiagnostics(): SupportDiagnostics { return { transferCount: currentTransferCount, localNetworkAvailable: currentLocalNetworkState, selectedFolder: selectedFolderName ?? null };}Create a conversation:
const thread = await one.support.createThread({ category: 'bug', // 'message' | 'bug' | 'feature' body: message, email: customerEmail, diagnostics: collectSupportDiagnostics()});Reply with fresh app data:
const updated = await one.support.reply(thread.id, { body: reply, diagnostics: collectSupportDiagnostics()});Collect diagnostics immediately before every create and reply. One separately attaches authoritative current customer, identity, entitlement, App, environment, platform, SDK, locale, and version context on the server for every customer message.
Conversation lifecycle
Section titled “Conversation lifecycle”const page = await one.support.listThreads({ limit: 50 });const conversation = await one.support.getThread(threadId);const contact = await one.support.getContact();await one.support.markRead(threadId);await one.support.resolve(threadId);Refresh on screen entry, after a customer action, or after a support notification. Do not poll or wrap these calls in another retry loop.
Use the complete customer UI from @repo/sdk-web inside OneProvider:
import { ContactSupport } from '@repo/sdk-web';import '@repo/sdk-web/styles.css';
<ContactSupport initialThreadId={supportThreadId} defaultEmail={customerEmail} emailRequirement="optional" emailVerificationMode="optional" magicLinkRedirectUrl="simpleshare://auth" showsPushNotificationOption={true} getDiagnostics={collectSupportDiagnostics}/>For a non-React webview or Desktop renderer, mount the same UI:
import { mountContactSupport } from '@repo/sdk-web';
const mounted = mountContactSupport({ client: one, container: document.querySelector('#support')!, initialThreadId: supportThreadId, getDiagnostics: collectSupportDiagnostics});
mounted.unmount();Svelte 5
Section titled “Svelte 5”Place the component under OneProvider, or pass a client explicitly:
<script lang="ts"> import { ContactSupport } from '@repo/sdk-svelte';</script>
<ContactSupport initialThreadId={supportThreadId} defaultEmail={customerEmail} getDiagnostics={collectSupportDiagnostics}/>Attachments
Section titled “Attachments”Use the SDK’s prepare/upload/claim flow rather than sending file bytes through the message endpoint:
const bytes = await file.arrayBuffer();const checksumSha256 = await sha256Hex(bytes);const prepared = await one.support.prepareAttachmentUpload({ fileName: file.name, contentType: file.type || 'application/octet-stream', byteSize: file.size, checksumSha256});
await one.support.uploadAttachment(prepared, bytes, file.type);await one.support.reply(thread.id, { body: reply, attachmentTokens: [prepared.uploadToken], diagnostics: collectSupportDiagnostics()});The complete iOS and React support surfaces provide Camera, Photos, and Files. They compress Camera/Photos images before upload, allow up to five attachments per message, and keep the resulting objects private in One storage.
Email, magic links, and notification deep links
Section titled “Email, magic links, and notification deep links”Email deliverability belongs to the customer’s One Auth identity rather than
Support. Native apps can read customerInfo.emailUnreachable (or the refreshed
AuthUser.emailUnreachable); web clients receive the same property on the Auth
user. It becomes true only after a permanent provider rejection. Magic-link
requests also return emailUnreachable; callers must not show “check your
email” when it is true. Support reuses this signal for its email warning.
Support stores its email and push flags in the customer’s notification
preferences. An unverified email can receive generic reply notifications. When
verification is enabled, the tenant-owned magic-link template upgrades that
email into the customer’s passwordless One Auth identity; Support does not keep
a second email identity. Once One Auth supplies the email, Support displays it
as account-managed and does not allow the notification settings to replace it.
A verified support-only address remains editable. The standard SDK UI confirms
a replacement, and separately warns before a magic link for a different address
can sign in to or link that address’s customer account.
One never parses inbound email replies or includes
message or attachment content in notification email.
The tenant-owned support_reply_notification template is notification-only and
must direct the customer back to the app. To show its optional Open
conversation button, set the template variable supportDeepLinkBaseUrl to the
app-owned support URL without a query string. One appends
one_support_thread_id and one_support_message_id; the host reads the thread
ID and opens the support surface with that conversation selected. Keep those IDs
inside the link rather than displaying them as a customer-facing reference.
On iOS, first validate the app-owned scheme or universal-link host, then use
SupportNotificationDestination(url:) and present
SupportView(threadId: destination.threadId). On web/Desktop, use
client.support.deepLinkDestination(url). Reuse an existing app URL scheme;
plist or Associated Domains changes are only needed when no suitable route exists.
When a host receives notification data, recognize the support payload and open the exact conversation:
const destination = one.support.notificationDestination(notificationData);
if (destination) { openContactSupport(destination.threadId);}This method parses support_reply data; it does not provision browser or
Desktop push delivery. Use verified email until the host has a supported native
notification transport. Never embed a One secret key in a website, renderer,
or Desktop webview.