Showcase

Website chat on the conversation bridge.

A portable pattern for visitor chat: your Lambda and embed on the edge, PingStack for the team inbox. Built on the same App Platform path as other conversation bridges — not a first-party App Store install.

You own

Lambda + embed

Visitor API, origin allowlist, reply fan-in, and the floating chat UI on your domain.

PingStack owns

App + discussions

Custom app, inbound webhook URL, signing secret, per-visitor discussions, and “Reply via Web Chat” for members.

Transport

Signed delivery URL

Preferred third-party outbound path: PingStack POSTs replies to your/delivery with X-PingStack-Signature.

In PingStack

Three steps to a working bridge

Labels match the current Add Custom App wizard. Use your PingStack environment (e.g. app.dev.pingstack.app). You need Server Owner or Admin.

  1. 1

    Add a Conversation bridge in PingStack

    Server Owner/Admin → Apps → + Add Custom App → Conversation bridge. Provider: My own endpoint. Channel kind: Website chat. Outbound: Signed delivery URL pointing at your Lambda /delivery. Bind (or create) a channel such as www-chat. Copy the inbound Webhook URL and Signing secret — the secret is shown once.

  2. 2

    Deploy the bridge Lambda

    Run examples/pingstack-web-chat/lambda/deploy.sh (Function URL + DynamoDB sessions). Set PINGSTACK_ENDPOINT_URL, PINGSTACK_SIGNING_SECRET, and ALLOWED_ORIGINS after the PingStack app exists. Browser calls hit /session/*; PingStack POSTs signed replies to /delivery.

  3. 3

    Enable the site embed

    Put the browser API on your domain (e.g. chat.azimuthsystems.net → CloudFront → Lambda). Set PUBLIC_PINGSTACK_CHAT_API_BASE + PUBLIC_TURNSTILE_SITE_KEY and rebuild. PingStack secrets never ship to the client; delivery stays on the Function URL.

Important: choose Conversation bridgeMy own endpointWebsite chat. The hosted SMS provider path (Twilio) is a different channel kind. Do not look for a first-party “Web Agent” App Store install for this pattern.

Example code

Lambda ingress (session open)

Third-party HMAC apps open a conversation with a root message, then asession.ensure post that includes parentSeq (App Conversations §10.3). The sample handler does this for you on the visitor’s first send.

// POST /session/{id}/send — first message opens a web: session
const root = await postIngress(JSON.stringify({
  body: `${visitorName} started a web chat`,
  username: visitorName,
}));

await postIngress(JSON.stringify({
  body: text,
  username: visitorName,
  session: {
    externalId: visitorId,          // becomes web:{id}
    label: `Web: ${visitorName}`,
    parentSeq: root.seq,            // required to create (§10.3)
    ensure: true,
  },
}));

Full handler:/showcase/web-chat/lambda/index.mjs(also under examples/pingstack-web-chat/ in the repo).

Embed

One script on the site

Point data-api-base at your domain’s chat API (not*.pingstack.app). Include the Turnstile site key. Swap the scriptsrc for a self-hosted copy if you prefer.

<script
  src="/chat/widget.js"
  data-api-base="https://chat.yourdomain.example"
  data-turnstile-site-key="0x4AAAA…"
  data-theme-color="#ff9026"
  data-position="bottom-right"
></script>
Assumptions

What this sample optimizes for

Deploy script wires DynamoDB sessions, origin allowlisting, and Cloudflare Turnstile on/session/start (required name + captcha). In-memory mode remains available when SESSION_TABLE is unset (local demos only).

Product note

Hosted Web Agent vs BYO

PingStack also has a hosted widget/gateway path (poll queue, CDN loader). This showcase is the framework story — same conversation primitives, your compute — so you can ship on any site without waiting on a first-party install.

Back to PlatformAsk us about a build