@wetransform/core
v3.4.0
Published
WeTransform browser SDK — embed the WeTransform app in your site.
Maintainers
Readme
@wetransform/core
Headless browser SDK to open WeTransform in your app (modal or inline), authenticate, and interact through a typed event API.
SDK Guide — overview of the SDK packages and how to integrate them into your website
Installation
npm install @wetransform/coreBrowser support
The SDK relies on Promise.withResolvers (ES2024): Chrome/Edge 119+, Firefox 121+, Safari 17.4+, Opera 105+. To support older browsers, load a Promise.withResolvers polyfill before calling createWeTransform.
Usage
import { createWeTransform } from '@wetransform/core'
const sdk = createWeTransform({
organizationHandle: 'your_organization_handle',
locale: 'en',
displayAsModal: true,
authentication: {
customerId: 'your_customer_id',
signature: 'your_signed_token',
},
})
// Subscribing to the successSubmit event
sdk.on('successSubmit', (payload) => console.log('File successfully submitted', payload))
// Opening WeTransform
await sdk.open()
// Dynamically switch locale without remounting
await sdk.setLocale('fr')
// Close WeTransform
await sdk.close()
// Destroy WeTransform session
await sdk.destroy()API
createWeTransform(config)
Returns a WeTransformInstance with:
open(): Promise<void>- initializes auth and mounts WeTransformclose(): Promise<void>- closes SDK UI and unmounts WeTransformdestroy(): Promise<void>- closes and tears down SDK resourcessetLocale(locale): Promise<void>- updates locale/auth context and syncs iframe locale when activeon(event, handler): () => void- subscribes to SDK events, returns unsubscribe function
Calling open() more than once
- While an open is already in flight,
open()joins it: the returned promise resolves when the in-flight open settles, and no second open is started. - While the SDK is already open, closing, or destroyed,
open()is a no-op that resolves immediately. - After
close()has finished,open()can be called again to reopen the SDK.
WeTransformConfig
organizationHandle: stringlocale?: 'en' | 'fr'authentication: WeTransformAuthenticationConfiginitialLocation?: 'transformations' | 'uploader' | 'mapper' | 'finalize' | 'sourcePreview' | 'sourceUpdate' | 'loadSource' | 'scheduler'displayAsModal?: boolean(default:true)mountElement?: string | HTMLElement(default id:weTransform_iframeContainer, used whendisplayAsModalisfalse)urlOverwrite?: { apiUrl?: string; baseUrl?: string }(override SDK URLs, mainly for testing)customization?: WeTransformSDKCustomization(colors, font, border radius; see Customization)agentChat?: WeTransformAgentChat(open the Agent chat, give it an Agent context; see Agent chat)
locale defaults to 'en' when not provided.
Initial location
initialLocation selects the screen WeTransform opens on. When omitted, it is inferred from the
auth fields you pass:
| initialLocation | What it shows | Required auth fields | Inferred when |
| ----------------- | --------------------------------------- | ------------------------------ | ---------------------------------------- |
| transformations | List of the customer's transformations | customerId, signature | no templateHandle and no sourceId |
| uploader | File upload screen for a template | + templateHandle | templateHandle set, sourceId not set |
| finalize | Final review/submit screen for a source | + templateHandle, sourceId | both templateHandle and sourceId set |
| mapper | Column/field mapping for a source | + templateHandle, sourceId | — |
| sourcePreview | Read-only preview of a source | + templateHandle, sourceId | — |
| sourceUpdate | Update an existing source | + templateHandle, sourceId | — |
| loadSource | Load/import a source | + templateHandle, sourceId | — |
| scheduler | Schedule a source run | + templateHandle, sourceId | — |
Inference only picks transformations, uploader, or finalize. The remaining locations
(mapper, sourcePreview, sourceUpdate, loadSource, scheduler) must be set explicitly and
require both templateHandle and sourceId. TypeScript enforces these auth requirements per
location.
Customization
type WeTransformSDKCustomization = {
colors?: {
// hex or rgb() / rgba() only
primary?: string
secondary?: string
background?: string // page background: light, or dark together with `dark: true`
text?: string // body text; pass it together with textSubtle
textSubtle?: string // hints, captions, labels
surface?: string // cards, dialogs, menus, tooltips and other panels on the page
}
font?: {
family: string // CSS font-family value, e.g. "'Inter', sans-serif"
url?: string // https stylesheet declaring the font (Google Fonts-style), weights 300 to 600
}
borderRadius?: {
button?: string // buttons and search-style inputs (pill by default)
base?: string // inputs, cards, menus and the modal (10px by default, 12px on the modal)
}
dark?: boolean // dark mode, off unless true
}Pass your theme through config.customization:
createWeTransform({
organizationHandle: 'your_organization_handle',
authentication: { customerId: 'your_customer_id', signature: 'your_signed_token' },
customization: {
colors: {
primary: '#7c3aed',
background: '#faf5ff',
},
font: {
family: "'Inter', sans-serif",
url: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap',
},
borderRadius: {
button: '8px',
base: '8px',
},
},
})- Some values are applied immediately, host-side, before the iframe is ready:
colors.primarycolors the opening loader spinner.colors.backgroundis the opening loader and modal background (#121212withdark: true, white otherwise).colors.textis the opening loader text color.borderRadius.baseshapes the modal (displayAsModal: true). Inline containers are left to you.
- The full
customizationobject is forwarded to WeTransform once it is ready, so it can theme itself. Every value is a CSS value; invalid ones are ignored. - Colors must be hex (
#7c3aed) orrgb()/rgba(). Any other syntax (named,hsl(),oklch()…) is ignored. font.urlmust use https and include weights 300 to 600, otherwise the browser fakes light and bold text.- Without
font.url, only fonts already available to the browser (system fonts) work.
Dark mode
dark: true draws WeTransform light-on-dark. It is never detected from your background, and text
follows the mode, not the background:
- A dark
colors.backgroundwithoutdark: trueleaves dark text on it. Turn both on together. - Only the defaults follow the mode. Colors you pass apply as given, so also pass a
primary,text,textSubtleandsurfacereadable on dark. Without abackground, WeTransform's dark page applies. - Pass
textandtextSubtletogether: one alone leaves the other at the mode's default grey. - Nothing corrects an unreadable combination at runtime.
- The error panel shown when opening fails stays light.
customization: {
dark: true,
colors: {
primary: '#a78bfa',
background: '#0f172a',
text: '#e2e8f0',
textSubtle: '#94a3b8',
surface: '#1e293b',
},
}Agent chat
agentChat drives the Agent, the AI chat assistant inside WeTransform:
createWeTransform({
// ...
agentChat: {
open: true,
context: 'Help the user import their inventory into the Inventory template',
},
})open?: boolean: shows the Agent chat panel as soon as WeTransform loads, on every mount, whatever the end user's saved preference. It does not restart the conversation.context?: string: the Agent context, a hidden instruction. WeTransform restarts the Agent conversation from it and the Agent speaks first. The end user never sees it. It does not open the panel. At most 4096 characters.''behaves like no context.
The two are independent: pass both to land the user in a conversation where the Agent already greets them about their task.
Signature
The Agent context is covered by your signature, so it cannot be tampered with in the browser.
Sign external_id:template_handle:source_id:success_url:agent_context, empty parts dropped. The
SDK never sends success_url, so in practice you sign
external_id:template_handle:source_id:agent_context (customerId, templateHandle, sourceId,
context), empty parts dropped. Without a context, the
signature is unchanged. A signature that does not cover the context fails like any other
signature mismatch, through the error event.
When the conversation restarts
The conversation restarts from the Agent context once per SDK instance, the first time WeTransform is ready:
- closing and reopening the modal keeps the conversation (and does not spend another AI credit);
- closing before WeTransform is ready keeps the context for the next
open(); destroy()then a new instance, or a page refresh, restarts it.
Like every other option, changing agentChat on a live instance needs destroy() then a new
instance.
Runtime locale switching
setLocale('en' | 'fr')is additive and does not require destroy/recreate.- If the locale is unchanged,
setLocaleis a no-op. - If SDK status is
open, locale is sent to iframe immediately via bridge messaging. - If SDK status is
opening, locale is sent after iframe readiness is reached. setLocaleupdates auth API URL context to{VITE_SENDER_API_URL}/{locale}/send/{organizationHandle}for subsequent auth calls.- Calling
setLocaleafterdestroy()throwsCannot set locale after destroy.
Inline mounting behavior (displayAsModal: false)
When displayAsModal is false, wetTransform SDK mounts the iframe into:
- the element with id
weTransform_iframeContainerby default, or - the element resolved from
mountElementwhen you provide one.
If the target element does not exist, the SDK creates the container and appends it to document.body.
WeTransformAuthenticationConfig
customerId: stringsignature: stringtemplateHandle?: stringsourceId?: string
Events
Supported sdk.on(...) events:
openclosedestroyreadysuccessSubmitwith payload
{
customerId: string
templateHandle: string
fileUrl: string
signature: string
userFileName: string
}errorwith payload{ code: string; message: string; stack?: string; details?: Record<string, string | number | boolean> }
details carries diagnostic context when available. For network_error (the request got no
readable response: offline, blocked, CORS, page unload…) it contains url, attempts,
durationMs (of the last attempt), online, userAgent, visibility, sincePageLoadMs and
pageHidden. Include it in your error reports.
Event listeners are isolated from each other and from the SDK: an exception thrown inside a
listener does not stop other listeners or break SDK internals. It is reported as an error event
with code listener_error (exceptions thrown by error listeners themselves are logged to the
console instead, to avoid recursion).
Exports
createWeTransformWeTransformConfigWeTransformAuthenticationConfigWeTransformInitialLocationsWeTransformSDKCustomizationWeTransformAgentChatWeTransformEventMapWeTransformInstanceSupportedLocales
