Touchpoint
Touchpoint is a drop-in conversational UI and SDK for Amazon Connect Customer — a single widget for chat, voice, and voice-mini, plus Live Sync, the native technology that keeps a voice or chat conversation in sync with your on-screen interface.
npm i @amazon-connect-touchpoint/web
Prerequisites
To run Touchpoint you need an Amazon Connect Customer instance with a contact
flow, plus a browser-facing endpoint that mints the contact credentials. Gather
the following before you call create():
| What you need | Where it comes from |
|---|---|
| StartChatContact endpoint (chat) | An API Gateway/Lambda route that calls Amazon Connect's StartChatContact and returns participant credentials. Deploy the StartChatContact API. Required for input: "text". Passed as config.chatEndpoint. |
| StartWebRTCContact endpoint (voice) | An endpoint that calls StartWebRTCContact and returns the Chime connection data. Deploy the StartWebRTCContact sample. Required for input: "voice" and input: "voiceMini". Passed as config.voiceEndpoint. |
| Instance ID | The Amazon Connect Customer instance (UUID) the contact is created in. config.instanceId. |
| Contact Flow ID | The contact flow (UUID) that handles the contact. config.contactFlowId. |
| Region | AWS region of your instance, e.g. us-west-2. config.region. |
That's everything you need for chat and voice. Live Sync is optional — it additionally requires an Agentic CX designer (ACXD) application; see below.
Setting up Amazon Connect Customer
- Create a contact flow in your Amazon Connect Customer instance that handles the contact; note the instance ID and contact flow ID.
- Stand up the browser endpoints so the page can create a contact — the StartChatContact (chat) and StartWebRTCContact (voice) endpoints above.
Adding Live Sync (optional)
Live Sync keeps the conversation in sync with your on-screen interface. It's the only feature that requires ACXD; plain chat and voice work without it. To enable it:
-
Build the ACXD application. In the ACXD Canvas, wire
Start→ a Live Sync node →Exit application. On the Live Sync node, declare the actions and scopes the assistant may use (or define them on the fly with this SDK for rapid prototyping — see Live Sync below).
-
Publish it and copy your keys. Deploy the application, then copy the deployment key and API key from its settings.
-
Route your contact flow into it so contacts reach the ACXD application, and pass the keys as
liveSync.deploymentKey/liveSync.apiKey.
The interactive playground lets you plug these values into a form and launch a live instance without writing any code.
Examples
Chat
import { create } from "@amazon-connect-touchpoint/web";
const touchpoint = await create({
config: {
// Your StartChatContact endpoint (e.g. an API Gateway route) that mints a
// participant token.
chatEndpoint: "REPLACE_WITH_START_CHAT_ENDPOINT",
instanceId: "REPLACE_WITH_INSTANCE_ID",
participantDisplayName: "REPLACE_WITH_PARTICIPANT_DISPLAY_NAME",
contactFlowId: "REPLACE_WITH_CONTACT_FLOW_ID",
region: "us-east-1",
},
input: "text",
});
Custom chat client (advanced)
By default, Touchpoint talks to the Amazon Connect Participant Service directly
from the browser using the participant token. To route every Participant Service
call through your own backend instead (so the token never reaches the browser),
extend ChatClient and pass an instance as customChatClient. ChatClient is
re-exported from amazon-connect-chatjs;
see its documentation for the methods to implement.
import { create, ChatClient } from "@amazon-connect-touchpoint/web";
class MyChatClient extends ChatClient {
createParticipantConnection(participantToken, type, acknowledgeConnection) {
/* TODO: call your backend */
}
disconnectParticipant(connectionToken) {
/* TODO: disconnect participant */
}
sendMessage(connectionToken, content, contentType, clientToken) {
/* TODO: send message */
}
sendEvent(connectionToken, contentType, content, clientToken) {
/* TODO: send event */
}
getTranscript(connectionToken, args) {
/* TODO: get transcript */
}
// Other optional properties
}
const touchpoint = await create({
config: {
customChatClient: new MyChatClient(),
chatEndpoint: "REPLACE_WITH_START_CHAT_ENDPOINT", // The chat endpoint does not need to return a `participantToken`
instanceId: "REPLACE_WITH_INSTANCE_ID",
participantDisplayName: "REPLACE_WITH_PARTICIPANT_DISPLAY_NAME",
contactFlowId: "REPLACE_WITH_CONTACT_FLOW_ID",
region: "us-east-1",
},
input: "text",
});
When no customChatClient is provided and the chat details have no
participantToken, a warning is logged to the console.
Voice mini
A compact, floating voice widget. Voice modes connect through your
StartWebRTCContact endpoint. On mobile it stacks vertically, and a drag handle
lets the customer move it out of the way of the page.
import { create } from "@amazon-connect-touchpoint/web";
const touchpoint = await create({
config: {
voiceEndpoint: "REPLACE_WITH_START_WEBRTC_ENDPOINT",
instanceId: "REPLACE_WITH_INSTANCE_ID",
contactFlowId: "REPLACE_WITH_CONTACT_FLOW_ID",
region: "us-east-1",
},
input: "voiceMini", // or "voice" for the full-screen experience
});
Layouts
windowSize controls how the expanded experience is presented (applies to chat
and full-screen voice; voiceMini is always a compact floating widget):
half(default) — overlay with the panel on the right and the rest of the page dimmed.full— overlay covering the whole viewport.floating— a detached, rounded panel that hovers over the page without dimming it, so the site stays visible and interactive.side-by-side— a panel docked to the right edge for the full height; on wider viewports the page is narrowed by the panel's width so nothing is hidden behind it and both can be used at once.
floating and side-by-side are ideal for chatting while browsing — the way
voice conversations already run alongside the page.
const touchpoint = await create({
config: {/* … */},
input: "text",
windowSize: "side-by-side",
});
Theming
Pass a partial theme to match your brand. The accent color drives prominent
elements — the send button, loader, selected states, and the voice
audio-signaling ripple — and defaults to black/white so setting a brand accent is
immediately visible. A contrasting foreground on the accent (e.g. the send arrow)
is derived automatically.
const touchpoint = await create({
config: {/* … */},
theme: {
accent: "#22c55e",
// Optional overrides: primary, secondary, background, fontFamily, …
},
});
Container styles
To place or size the expanded panel differently than the windowSize presets
allow, pass containerStyle. Touchpoint renders inside a closed shadow root, so
CSS written on the host page cannot reach it — a style object is the supported way
in, and it is applied on top of the preset's own styles.
It applies to the full conversation container only — the expanded chat and
full-screen voice experience. The launch icon and the voiceMini widget are
positioned as compact floating elements of their own and are not affected.
const touchpoint = await create({
config: {/* … */},
windowSize: "floating",
containerStyle: {
borderLeft: "1px solid #efefef",
},
});
The configuration can be supplied as JSON on the configuration attribute of the
<connect-touchpoint> element, so it is not assumed to be trusted: only
container-level properties (placement, size, spacing, borders, shadow, transform,
…) are accepted, and values that load remote resources (url(), image-set(),
element()) or try to smuggle in extra declarations are dropped with a console
warning. Use theme to restyle the content inside the container.
Message bubbles
Transcript messages can be rendered as bubbles, independently for each side:
userMessageBubble for the customer's messages and agentMessageBubble for the
assistant's. With bubbles off, messages render as plain text on the background.
Each bubble can then be restyled with userMessageBubbleStyle /
agentMessageBubbleStyle. These are only applied when the matching
*MessageBubble flag is enabled — a style object on its own does not turn
bubbles on.
const touchpoint = await create({
config: {/* … */},
input: "text",
userMessageBubble: true,
agentMessageBubble: true,
userMessageBubbleStyle: {
backgroundColor: "#e8f0fe",
color: "#102a43",
borderRadius: "20px",
},
agentMessageBubbleStyle: {
backgroundColor: "#f3f3f3",
color: "#102a43",
borderRadius: "20px",
},
});
The bubble styles use the same allowlist and sanitization as containerStyle
(the shared CustomStyle type, which includes the text color): only the
supported properties are kept, and anything containing url(),
image-set(), element(), comments, or extra declarations is dropped with a
console warning, since the configuration may arrive as untrusted JSON on the
configuration attribute of the <connect-touchpoint> element.
Icons
Every built-in icon can be replaced with the icons option. Keys are the names
of the icons Touchpoint exports as Icons (e.g. Close, Send, Mic,
Restart); values are React components. Icons you leave out keep their default,
and the override applies everywhere Touchpoint renders that icon. It can be
changed later with touchpoint.update({ icons }).
The easiest way to make an icon is createIcon, which takes the SVG content:
import { create, createIcon, html } from "@amazon-connect-touchpoint/web";
// Create icons once, outside of any render, so their identity stays stable.
const Send = createIcon(html`<path d="M3 20L21 12L3 4V10L15 12L3 14V20Z" />`);
const touchpoint = await create({
config: {/* … */},
icons: { Send },
});
createIcon handles the details the built-in icons rely on, so your SVG only
needs the shapes:
- 24×24 container. The SVG is rendered with
viewBox="0 0 24 24", so draw your paths on a 24×24 grid. Pass a second argument to change it, e.g.createIcon(children, { viewBox: "0 0 16 16" }). currentColor. Shapes are filled withcurrentColorby default, so the icon picks up the text color of the button or label it sits in, including hover and disabled states. Avoid hard-coded colors in your paths. For stroke-based artwork, setfill: "none"andstroke: "currentColor"in the second argument:createIcon(<path d="…" />, { fill: "none", stroke: "currentColor", strokeWidth: 2 }).- Sizing and
className. Thesize(in pixels) andclassNameprops are applied to the<svg>for you. Without asizethe icon fills its container. - Accessibility. The icon is hidden from assistive technology; the accessible name always comes from the surrounding button or text.
You can also pass any React component that accepts size and className
instead, for example one that renders an icon from your own design system. It is
then responsible for those props, and for using currentColor if it should
follow the surrounding text color.
Welcome screen
For text conversations, the opening assistant message is presented as an immersive welcome screen: the greeting (and any choices) is centered vertically, and the connecting/thinking state is centered where the message will appear. The input stays hidden until the first message arrives; if the message has choices, the input is pulled up beneath the greeting with the choices below. It persists until the customer sends or selects something, then the conversation continues as a normal transcript. If the first message carries modalities or a guide, the normal transcript is used instead.
It's enabled by default. Set welcomeScreen: false to render a top-anchored
transcript from the first message. When a brandIcon is set, it appears at the
top of the welcome screen; set welcomeScreenLogo: false to hide it.
const touchpoint = await create({
config: {/* … */},
input: "text",
welcomeScreen: false, // opt out of the immersive welcome screen
});
Hidden and added elements
Built-in UI can be tuned with two lists of element identifiers. Use the exported enums, or the plain strings in untyped environments.
hiddenElements omits default elements:
HiddenElement | String | Hides |
|---|---|---|
HiddenElement.HeaderRestart | "header.restart" | The restart button in the header. |
HiddenElement.WelcomeScreenLogo | "welcomescreen.logo" | The brand logo on the welcome screen. |
addedElements opts in to elements that are off by default:
AddedElement | String | Adds |
|---|---|---|
AddedElement.HeaderClose | "header.close" | A button right of the minimize button that collapses the widget. |
import {
create,
HiddenElement,
AddedElement,
} from "@amazon-connect-touchpoint/web";
const touchpoint = await create({
config: {/* … */},
input: "text",
hiddenElements: [HiddenElement.HeaderRestart],
addedElements: [AddedElement.HeaderClose], // or ["header.close"]
});
Unknown values are ignored. Both lists can be changed later with update().
welcomeScreenLogo: false is deprecated in favor of hiding
HiddenElement.WelcomeScreenLogo.
Assistant identity
When participant info is shown (showParticipantInfo: true), the automated
assistant is labeled "AI" with a default icon. Override both to match your
brand with assistantName (the displayed name) and assistantIcon (an image URL
used as the assistant's avatar). avatarShape controls whether participant
avatars are "round" (default) or "square".
const touchpoint = await create({
config: {/* … */},
input: "text",
showParticipantInfo: true,
assistantName: "Ada",
assistantIcon: "https://example.com/assistant.png",
avatarShape: "square",
});
Live Sync
Live Sync is native Amazon Connect Customer technology that synchronizes a voice (or chat) conversation with a digital, on-screen interface. Customers see options, next steps, and confirmations in real time while an AI agent guides them — no app, no channel switch — unlocking complex, multi-step tasks that voice or chat alone can't handle, where customers need to see, choose, and complete actions in a single continuous conversation.
In Touchpoint, the AI agent drives the page through custom actions: you register a named action with a JSON Schema, the agent resolves what the customer said into typed arguments, and your handler applies them to the page.
import { create } from "@amazon-connect-touchpoint/web";
const touchpoint = await create({
config: {
chatEndpoint: "REPLACE_WITH_START_CHAT_ENDPOINT",
participantDisplayName: "REPLACE_WITH_PARTICIPANT_DISPLAY_NAME",
instanceId: "REPLACE_WITH_INSTANCE_ID",
contactFlowId: "REPLACE_WITH_CONTACT_FLOW_ID",
region: "us-east-1",
},
input: "voiceMini",
liveSync: {
deploymentKey: "REPLACE_WITH_DEPLOYMENT_KEY",
apiKey: "REPLACE_WITH_API_KEY",
// `contactId` is optional — defaults to the active session's contact. Set it
// to bind a separate contact (e.g. an inbound phone call) to this page.
},
});
// Advertise the actions the AI agent may take (sent once a contact exists):
await touchpoint.sendContext({
actions: [
{
action: "set_cabin_class",
description: "Choose the cabin class",
schema: {
type: "object",
properties: {
cabin: { type: "string", enum: ["economy", "business", "first"] },
},
required: ["cabin"],
},
handler: ({ cabin }) => setCabinClass(cabin),
},
],
});
Actions can also be configured in
Agentic CX designer (ACXD)
on the Live Sync node; defining them here is useful for rapid prototyping. To
notify a Live Sync script that the user reached a step, call
touchpoint.sendStep({ stepId, scriptId, apiKey, context }).
External (no UI)
Use input: "external" to run Touchpoint with no rendered UI. It opens no
chat or voice contact of its own — it only connects Live Sync to an existing
live contact (identified by liveSync.contactId) so that contact can drive your
digital asset in real time. A common case is a phone call: the caller talks
to the agent while the page updates alongside them. No chatEndpoint or
voiceEndpoint is needed; only the Live Sync credentials.
import { create } from "@amazon-connect-touchpoint/web";
const touchpoint = await create({
config: { region: "us-east-1" },
input: "external",
liveSync: {
deploymentKey: "REPLACE_WITH_DEPLOYMENT_KEY",
apiKey: "REPLACE_WITH_API_KEY",
contactId: "REPLACE_WITH_CONTACT_ID", // the contact to sync with
},
});
// Advertise your actions, then drive the page as the agent invokes them.
await touchpoint.sendContext({ actions: [/* … */] });
Custom modalities
In addition to regular modalities, you can define fully custom modalities directly in the code snippet as React components. If the application message contains this modality, the component is rendered with payload data as well as a conversation handler that makes interacting with the conversation easy.
To make authoring these React components simple and to eliminate the need for JSX compilation, the package exports an html tagged template literal a'la developit/htm. html also comes pre-loaded with a range of components and icons Touchpoint already uses to render its UI, so you can easily create and compose markup that matches Touchpoint in style. The full list is visible in the export definition, and a more visual guide is going to be available soon.
Here is a full example:
import { create, html } from "@amazon-connect-touchpoint/web";
const touchpoint = await create({
config: {
// Your StartChatContact endpoint (e.g. an API Gateway route) that mints a
// participant token.
chatEndpoint: "REPLACE_WITH_START_CHAT_ENDPOINT",
participantDisplayName: "REPLACE_WITH_PARTICIPANT_DISPLAY_NAME",
instanceId: "REPLACE_WITH_INSTANCE_ID",
contactFlowId: "REPLACE_WITH_CONTACT_FLOW_ID",
region: "us-east-1",
},
input: "text",
modalityComponents: {
MyModality: ({ data, conversationHandler }) => {
return html`
<div>
<p>Are you sure?</p>
<IconButton
type="main"
label="Yes"
onClick=${() => {
conversationHandler.sendText("yes");
}}
icon="{Check}"
/>
<IconButton
type="ghost"
label="No"
onClick=${() => {
conversationHandler.sendText("no");
}}
icon="{Close}"
/>
</div>
`;
},
},
});
Embedded mode (advanced)
Touchpoint also registers a <connect-touchpoint> custom element. Set its
writeable touchpointConfiguration property to the same object you would pass to
create() and it starts in embedded mode: no launch button,
and the UI fills the container you place it in, so it drops into an existing page
layout.
<div class="my-grid-layout">
<main>
<!-- Main page content -->
</main>
<aside>
<connect-touchpoint></connect-touchpoint>
</aside>
</div>
<script>
document.querySelector("connect-touchpoint").touchpointConfiguration = {
config: {
// Your StartChatContact endpoint (e.g. an API Gateway route) that mints a
// participant token.
chatEndpoint: "REPLACE_WITH_START_CHAT_ENDPOINT",
participantDisplayName: "REPLACE_WITH_PARTICIPANT_DISPLAY_NAME",
instanceId: "REPLACE_WITH_INSTANCE_ID",
contactFlowId: "REPLACE_WITH_CONTACT_FLOW_ID",
region: "us-east-1",
},
input: "text",
};
</script>
The element sizes itself with low-specificity defaults — display: block; height: 350px for text and voice, display: inline-block for voiceMini —
which you can override to fit your layout:
connect-touchpoint {
height: 100%;
}
Ending a contact
When the underlying Amazon Connect contact ends — the voice call is disconnected
(including after an escalated agent resolves and hangs up) or the chat contact is
closed — Touchpoint fires onContactEnded. For voice inputs the default action
collapses the widget back to the launcher; chat keeps the ended transcript
visible. The event is cancelable, so you can take over:
const touchpoint = await create({
config: {/* … */},
input: "voice",
onContactEnded: (event) => {
// Keep the widget open and handle the end yourself:
// event.preventDefault();
console.log("contact ended");
},
});
Interactive playground
Running the project locally launches an interactive playground covering every capability (chat, voice, voice-mini, and Live Sync):
npm install
npm run dev
# then open http://localhost:5173
API reference
The full SDK specification — every configuration option, type, and modality
component — is generated from source and lives under
docs/.
Common entry points:
create(config)— create and mount a Touchpoint instance.TouchpointConfiguration— top-level options (config,input,colorMode,windowSize,theme,containerStyle,liveSync,brandIcon,onContactEnded,userMessageBubble,agentMessageBubble,userMessageBubbleStyle,agentMessageBubbleStyle,welcomeScreen,welcomeScreenLogo,hiddenElements,addedElements,showParticipantInfo,assistantName,assistantIcon,avatarShape,icons, …).ConnectConfig— Amazon Connect Customer connection details.LiveSyncConnection/LiveSyncContextInput— Live Sync connection and the actions / scopes / destinations you advertise to the AI agent.