taliiq
App
Taliiq

Web Widget

Embed Taliiq voice and chat on your website with domain allowlisting and optional JWT identity verification.

Taliiq’s Web Widget delivers real-time voice and chat from the browser. Embed a script tag, restrict origins to your domains, and optionally inject verified user identity via a server-signed JWT.

Add voice and chat to your website with one snippet1:22

Setup

Go to Build → Channels and create a Web channel.

Assign a deployed agent with voice and/or chat capabilities.

Add allowed domains as hostnames (e.g. mycompany.com). Do not include https://. For local testing, list both localhost and 127.0.0.1 if you use either host. Allow-all (*) is not supported.

Copy the anonymous embed snippet from the channel page.

Anonymous embed

<script
  src="https://widget.taliiq.com/v1/widget.js"
  data-channel-id="YOUR_CHANNEL_UUID"
  data-link-source="homepage-hero"
></script>

Visitors stay anonymous. Styling (brand color, position, icon) is configured on the channel in the dashboard.

Optional data-link-source (or data-source) tags the placement. The widget also sends the page URL, referrer, and UTM params so chat/voice sessions can identify where the visit started.

The widget footer “Powered by Taliiq” link opens taliiq.com with source=web_widget_chat or source=web_widget_voice plus origin=<embed-hostname> so landing can store attribution cookies and forward them when the visitor continues to the dashboard.

Appearance overrides

Position, theme mode, brand color, and launcher icon come from the channel settings in the dashboard. You can override them per page from the embed snippet, for example to place the widget on the left for Arabic pages or match a page's color scheme.

<script
  src="https://widget.taliiq.com/v1/widget.js"
  data-channel-id="YOUR_CHANNEL_UUID"
  data-position="auto"
  data-theme-mode="light"
  data-theme-color="#7C3AED"
  data-icon="headset"
></script>
AttributeAllowed valuesNotes
data-positionauto, bottom-right, bottom-left, middle-right, middle-left, top-right, top-leftauto uses bottom-left when the page is right-to-left (dir="rtl" on <html> or <body>) and bottom-right otherwise, and moves live when dir changes
data-theme-modelight, dark
data-theme-colorHex color: #RGB or #RRGGBBBrand color for the launcher, buttons, and accents
data-iconmessage-circle, bot, sparkles, headset, phone, mic, headphones, zapLauncher and chat header icon

Precedence: a valid override always wins over the dashboard value. Missing or invalid values are ignored and the dashboard value is used. Add data-debug="true" to see a console warning for rejected values.

Change appearance live

For single-page apps (language switch, theme toggle), call the JavaScript API. Changes apply immediately, even with the chat open or during a call, and never reset the conversation. Calls made before the widget finishes loading are applied as soon as it is ready.

// Arabic selected: move the widget to the left
window.TaliiqWidget.update({ position: 'bottom-left' });

// Dark mode toggle with a custom brand color
window.TaliiqWidget.update({ themeMode: 'dark', themeColor: '#0EA5E9', icon: 'sparkles' });

// Read what is currently applied
window.TaliiqWidget.getAppearance();
// → { position: 'bottom-left', themeMode: 'dark', themeColor: '#0EA5E9', icon: 'sparkles' }

Unknown keys and invalid values are ignored; update() never throws. Use position: 'auto' to go back to direction-based placement.

Web addresses (https://…, www.…), email addresses, and markdown links ([label](https://…)) in agent chat replies render as clickable links. Web links open in a new tab; emails open the visitor's mail app. Only http, https, and mailto links are ever created, anything else (for example javascript:) stays plain text. Visitor messages are never turned into links.

Host page events

The widget dispatches a taliiq:session-end event on window when a conversation ends, so your page can react (show an offer, open a survey, fire analytics):

detail.kindFires whenOther fields
voiceA connected voice call ends (visitor hangs up or the call disconnects). Calls that never connected do not fire.durationSec, channelId
chatThe chat panel is closed, or the chat is ended from the menu, after new agent replies since the last event.assistantReplies, channelId
<script>
  window.addEventListener('taliiq:session-end', (event) => {
    const { kind, channelId } = event.detail;
    if (kind === 'voice' && event.detail.durationSec >= 20) {
      // e.g. show a follow-up offer
    }
  });
</script>

The event carries no transcript or personal data. Listener errors never affect the call or chat.

Identity verification (JWT)

To attach a known customer to the session, your server mints a short-lived JWT signed with the channel Identity Secret. Taliiq verifies the JWT with that same secret and trusts only the claims inside it.

JWT shape (HS256)

ClaimRequiredDescription
user_idYesStable customer id in your system
expYesExpiry (Unix timestamp)
iatRecommendedIssued-at
user_emailNoEmail
user_nameNoDisplay name
metadataNoJSON object of extra attributes

Rules:

  • Anonymous (no data-user-token / identity_token) works without a JWT.
  • When a token is present, it must verify with the channel identity secret.
  • Never put the Identity Secret in HTML, the widget script, or client-side JavaScript.
  • Reveal or rotate the secret under Identity verification on the web channel edit page — you can copy it anytime for plugins.

Identified embed

<script
  src="https://widget.taliiq.com/v1/widget.js"
  data-channel-id="YOUR_CHANNEL_UUID"
  data-user-token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
></script>

Server / plugin pattern

Store channel id + identity secret in your admin settings. On page render, mint a JWT and print data-user-token.

<?php
// WordPress / WooCommerce / Magento-shaped example (pseudocode)
$now = time();
$payload = [
  'user_id' => (string) $current_user->ID,
  'user_email' => $current_user->user_email,
  'user_name' => $current_user->display_name,
  'iat' => $now,
  'exp' => $now + 3600,
];
// Use any JWT library (firebase/php-jwt, lcobucci/jwt, …)
$identity_jwt = JWT::encode($payload, $taliiq_identity_secret, 'HS256');
?>
<script
  src="https://widget.taliiq.com/v1/widget.js"
  data-channel-id="<?php echo esc_attr($taliiq_channel_id); ?>"
  data-user-token="<?php echo esc_attr($identity_jwt); ?>"
></script>

Official WooCommerce, Magento, and WordPress plugins will use this same contract.

Security checklist

  • List explicit hostnames only (example.com, localhost, 127.0.0.1). Allow-all (*) is not supported.
  • Keep the Identity Secret on the server / in plugin settings only.
  • Use short JWT TTLs (e.g. 1 hour) and rotate the secret if it leaks.
  • If your organization uses zero data retention, verified user context is stripped from sessions.
  • If your site sends a Permissions-Policy header, make sure it allows microphone for your own origin (e.g. microphone=(self)). Voice calls and chat dictation both need getUserMedia, and a policy that blocks it (microphone=()) fails silently, no permission prompt, no error, the browser just never asks and the feature quietly does nothing.
  • If your site sends a Content-Security-Policy header, allowlist these origins or the widget fails silently (chat may still work while voice specifically breaks, since the realtime voice library loads on demand only when a call starts):
    • script-src: https://widget.taliiq.com https://cdn.jsdelivr.net
    • connect-src: https://widget.taliiq.com https://api.taliiq.com https://runtime.taliiq.com wss://runtime.taliiq.com https://room.taliiq.com wss://room.taliiq.com
    • media-src: blob: (voice call audio playback)

Troubleshooting

SymptomLikely cause
Browser CORS error calling the APIOrigin hostname not in the channel’s allowed domains, or localhost vs 127.0.0.1 mismatch
403 Origin header is requiredRequest missing browser Origin (common for non-browser clients)
403 Origin not authorizedHostname not allowlisted, or channel still has legacy * (edit and set real domains)
403 Invalid identity tokenWrong secret, wrong algorithm (must be HS256), or malformed JWT
403 Identity token has expiredIncrease exp when minting, or mint closer to page load
Voice call rings then drops back to chat, or dictate does nothing, with no browser permission prompt ever shownHost page's Permissions-Policy blocks microphone. Check for a header like microphone=() and change it to microphone=(self)
Chat works but voice calls fail silently, browser console shows a blocked script/connect requestHost page's Content-Security-Policy is missing one of the origins in the Security checklist above

Offline failsafe

If the voice agent does not connect in time, configure Offline Failsafe for that web channel.

See Channels overview for SIP and outbound options.