Chazt can be embedded as dedicated pages inside ERP, CRM, retail, POS, booking, banking, and other business software. Add Chazt screens to your product's own navigation—for example Contacts, Inbox, Campaigns, or Campaign analytics—without sending users to a separate Chazt application.
The current widget is a page embed. It does not create a floating launcher. Each embedded screen fills the container supplied by the vendor application and is responsive.
Available screens
Choose the screen with the data-view attribute.
data-view | Suggested location in vendor software | What users can do |
|---|---|---|
workspace | A complete Chazt area | Navigate between every screen permitted by the token |
overview | Dashboard | View contact, conversation, campaign, and delivery totals |
contacts | Customers or CRM | Search, page through, add, edit, and delete contacts and manage group assignments |
groups | Segments or audiences | Search, create, rename, review members, and remove contacts from groups |
inbox | Support or communications | Read conversations and send one-to-one messages |
templates | Messaging setup | Create, review, edit, synchronize, and delete supported WhatsApp templates, including media headers |
campaigns | Marketing | Review campaigns, scheduled and actual run dates, delivery totals, paginated recipient results, and create supported template campaigns |
notifications | Activity or audit log | Review recent campaign and notification delivery results |
analytics | Reports | View sent, delivered, read, and failed rates by campaign |
Use workspace when Chazt should have one main entry in the vendor's
navigation. Use a specific view when the vendor already has separate navigation
items for contacts, messaging, marketing, or reports.
Before you embed
Create an organization widget token in the Chazt AI Gateway admin.
For each token:
- Select the Chazt organization that owns the contacts and messages.
- Add the exact production origin of the vendor application, such as
https://app.vendor.example. - Select only the permissions needed by the embedded screen.
- Set an expiry date and a clear token name.
- Select Create Token & Run Widget, then copy the token and generated embed code. The full token is shown only when it is created.
Create a separate token for each customer organization. Never use one shared organization token for every customer of a multi-tenant vendor platform.
Embed your first screen
Create a container on the vendor page and load the production script after it:
<div id="chazt-contacts"></div>
<script
src="https://api.chazt.com/widget/v1/chazt-widget.js?v=4"
data-token="wgt_live_your_token_here"
data-view="contacts"
data-container="#chazt-contacts"
data-height="720px"
async
></script>
This renders the Contacts screen inside #chazt-contacts. Change
data-view to another supported screen without changing the script URL.
The page must be served from an allowed HTTP or HTTPS origin. Opening the HTML
directly from a local file (file://) is not supported.
Recommended vendor navigation
A vendor can place different Chazt screens at different routes in its product:
| Vendor route | Embed view | Typical permissions |
|---|---|---|
/customers/chazt | contacts | contact.read, contact.write, group.read |
/customers/groups | groups | group.read, contact.read; add group.write and contact.write to remove members |
/communications/inbox | inbox | inbox.read, conversation.read, message.send |
/marketing/templates | templates | template.read; add template.write to create, edit, synchronize, or delete |
/marketing/campaigns | campaigns | campaign.read, campaign.write, template.read, group.read |
/activity/notifications | notifications | notification.read |
/reports/campaigns | analytics | analytics.read, campaign.read |
The same organization token can be used across those routes if it has all the required permissions. For stronger separation, create a least-privileged token for each functional area—for example, a read-only reporting token and a separate campaign-management token.
Manage contacts and group membership
The Contacts screen displays 20 contacts per page by default and shows paging controls above and below the table. Contacts are sorted alphabetically with a stable order between pages. Searching resets the list to page 1.
The API accepts page and per_page; per_page is capped at 50:
curl "https://api.chazt.com/widget/v1/contacts?page=2&per_page=20&search=mike" \
-H "Authorization: Widget wgt_live_your_token" \
-H "X-Chazt-Widget-Origin: https://app.vendor.example"
Every paginated response includes:
{
"meta": {
"total": 1000,
"page": 2,
"per_page": 20,
"total_pages": 50
}
}
Deleting a contact requires contact.write:
curl -X DELETE \
"https://api.chazt.com/widget/v1/contacts/CONTACT_UUID" \
-H "Authorization: Widget wgt_live_your_token" \
-H "X-Chazt-Widget-Origin: https://app.vendor.example"
Deletion is a soft delete. The contact disappears from contact and group lists, while existing conversation history is retained.
The Groups screen provides a paginated member list. Reading members requires
both group.read and contact.read:
curl "https://api.chazt.com/widget/v1/groups/GROUP_UUID/contacts?page=1&per_page=20" \
-H "Authorization: Widget wgt_live_your_token" \
-H "X-Chazt-Widget-Origin: https://app.vendor.example"
Removing a member requires group.write and contact.write:
curl -X DELETE \
"https://api.chazt.com/widget/v1/groups/GROUP_UUID/contacts/CONTACT_UUID" \
-H "Authorization: Widget wgt_live_your_token" \
-H "X-Chazt-Widget-Origin: https://app.vendor.example"
This removes only that group membership; it does not delete the contact or its conversation history. A user can also remove memberships from the Contacts screen by editing a contact and deselecting groups.
Create templates with media and website buttons
The embedded Templates screen supports no header, a text header, or an image, video, or document header. WhatsApp template media is supported in the header; it cannot be inserted into the body or footer.
Supported sample files are:
- JPEG or PNG images up to 5 MB
- MP4 or 3GP videos up to 16 MB
- PDF documents up to 16 MB
Creating templates requires template.write. First upload the review sample as
multipart/form-data:
curl -X POST "https://api.chazt.com/widget/v1/templates/media" \
-H "Authorization: Widget wgt_live_your_token" \
-H "X-Chazt-Widget-Origin: https://app.vendor.example" \
-F "header_format=IMAGE" \
-F "header_media=@offer.png"
Chazt checks the declared MIME type, file size, and file signature before creating a Meta resumable-upload session. The response contains the temporary handle needed for template submission:
{
"message": "Header media uploaded",
"data": {
"header_format": "IMAGE",
"header_handle": "meta_upload_handle",
"name": "offer.png",
"type": "image/png",
"size": 245760
}
}
Submit that handle with the new template:
curl -X POST "https://api.chazt.com/widget/v1/templates" \
-H "Authorization: Widget wgt_live_your_token" \
-H "X-Chazt-Widget-Origin: https://app.vendor.example" \
-H "Content-Type: application/json" \
-d '{
"name": "weekend_offer",
"category": "MARKETING",
"language": "en_US",
"parameter_format": "POSITIONAL",
"header_format": "IMAGE",
"header_handle": "meta_upload_handle",
"body_text": "Save this weekend.",
"quick_reply_buttons": ["View offer"]
}'
The uploaded file is the sample Meta reviews with the template. When sending a campaign that uses an approved media template, provide the public HTTP or HTTPS URL of the actual image, video, or document to send. Templates remain pending until Meta approves them.
Add a website link button
A quick-reply button sends a reply back to the business; it does not open a webpage. To open an order tracker, account page, offer, or help page, add a website URL button instead.
The widget supports up to two website buttons. A fixed button sends every recipient to the same complete URL:
{
"text": "Track order status",
"url": "https://erp.vendor.example/orders/track"
}
A dynamic button has one send-time value. Put {{1}} at the very end of the
URL and provide a realistic example for Meta review:
{
"text": "Track order status",
"url": "https://erp.vendor.example/orders/track/{{1}}",
"example": "ORDER-1001"
}
Include website buttons in url_buttons when creating or updating a template:
{
"name": "order_tracking",
"category": "UTILITY",
"language": "en_US",
"parameter_format": "POSITIONAL",
"body_text": "Your order is ready to track.",
"url_buttons": [
{
"text": "Track order status",
"url": "https://erp.vendor.example/orders/track/{{1}}",
"example": "ORDER-1001"
}
]
}
When the approved template is selected in the embedded Campaigns screen, Chazt
asks for the value that replaces {{1}}. Enter only the suffix—for example
ORDER-1001—not the complete URL.
The embedded campaign form currently applies that value to every recipient in the campaign. For customer-specific order links, use a fixed tracking page that identifies the signed-in customer, send separate campaigns, or integrate a per-recipient field mapping through the server-side campaign API. Use opaque, short-lived signed values instead of placing customer details in URLs.
Send a campaign to customers selected in the ERP
Vendors do not need to export a spreadsheet or ask users to import contacts before every campaign. The ERP can synchronize the selected customers and open the Campaigns screen with that exact audience already selected.
The normal user flow is:
- The user filters or selects customers in the ERP.
- The ERP sends those customers to Chazt with stable ERP customer IDs.
- Chazt creates or updates the contacts and returns an audience UUID.
- The ERP opens the embedded Campaigns screen with that audience preselected.
- The user chooses an approved template, reviews the audience, and sends or schedules the campaign.
1. Create a synchronization token
Create a token with contact.sync. Use a separate token for server-side ERP
synchronization when possible. The browser Campaigns screen normally uses a
token with campaign.read, campaign.write, template.read, and group.read.
The synchronization token must still have the vendor application's exact
origin in its allowed origins. Send that value in
X-Chazt-Widget-Origin from the ERP server.
2. Synchronize the selected ERP customers
Send up to 1,000 customers in one request:
curl -X POST "https://api.chazt.com/widget/v1/contacts/sync" \
-H "Authorization: Widget wgt_live_your_sync_token" \
-H "X-Chazt-Widget-Origin: https://app.vendor.example" \
-H "Content-Type: application/json" \
-d '{
"source": "vendor-erp",
"contacts": [
{
"external_id": "CUST-10025",
"first_name": "Mike",
"last_name": "Jones",
"phone": "+15615557689",
"email": "mikejones@example.com",
"group_names": ["Retail customers"],
"street": "10 Market Street",
"city": "Miami",
"state": "Florida",
"zip": "33101",
"country": "US",
"marketing_consent": true,
"source_updated_at": "2026-08-29T10:30:00Z"
}
],
"audience": {
"external_id": "inactive-customers-2026-08-29",
"name": "Inactive customers · 29 Aug 2026",
"replace_members": true,
"temporary": true
}
}'
source identifies the ERP integration and must stay consistent. Each
external_id must be the customer's stable ID in that ERP. Repeating the same
request updates the existing contact instead of creating a duplicate.
source_updated_at prevents an older ERP event from replacing newer contact
data. A contact needs a phone number or email address. Phone numbers intended
for WhatsApp should be sent in international format.
When replace_members is true, the supplied contacts become the complete
membership of the audience. This is useful when the user changes an ERP filter
and retries the campaign action. Customers with marketing_consent: false are
synchronized but excluded from the returned campaign audience.
Temporary audiences expire after seven days when expires_at is omitted. Send
temporary: false for a reusable ERP segment, or provide an explicit future
expires_at value.
The response contains per-contact results and the UUID needed by the embed:
{
"message": "ERP contacts synchronized",
"source": "vendor-erp",
"summary": {
"received": 1,
"created": 1,
"updated": 0,
"unchanged": 0,
"skipped": 0,
"failed": 0,
"consent_excluded": 0
},
"audience": {
"uuid": "7d4c71c9-6b82-45f7-a146-34db18e2070f",
"external_id": "inactive-customers-2026-08-29",
"name": "Inactive customers · 29 Aug 2026",
"contact_count": 1,
"temporary": true,
"expires_at": "2026-09-05T10:30:00.000Z"
},
"results": [
{
"external_id": "CUST-10025",
"status": "created",
"uuid": "9fe689b9-5a54-4898-af75-65e3a02048b3"
}
]
}
The endpoint can return a successful synchronization with individual failed
rows. Check summary.failed and store or display the corresponding results
messages.
3. Open Campaigns with the ERP audience
Use audience.uuid from the synchronization response:
<div id="chazt-campaigns"></div>
<script
src="https://api.chazt.com/widget/v1/chazt-widget.js?v=4"
data-token="wgt_live_your_campaign_token"
data-view="campaigns"
data-audience="7d4c71c9-6b82-45f7-a146-34db18e2070f"
data-lock-audience="true"
data-container="#chazt-campaigns"
data-height="760px"
async
></script>
data-lock-audience="true" prevents the user from accidentally changing the
campaign to all contacts or another group. If the audience has expired, the
screen asks the ERP to synchronize it again.
For single-page applications, an existing instance can receive a new audience:
const campaigns = window.ChaztWidgets.find(
(widget) => widget.id === "erp-campaigns",
);
campaigns.setAudience(syncResponse.audience.uuid, true);
campaigns.setView("campaigns");
Keep contacts current automatically
The campaign-click synchronization above is sufficient for an initial
integration. Vendors can remove even that delay by calling the same endpoint in
the background when an ERP customer is created or updated. Use ERP webhooks
when available; otherwise send customers changed since the last successful
sync. Stable source, external_id, and source_updated_at values make retries
safe.
Multiple screens on one page
Every script creates an independent widget instance. Give each screen its own container and optional instance name:
<section>
<h2>Recent notifications</h2>
<div id="chazt-notifications"></div>
</section>
<section>
<h2>Campaign performance</h2>
<div id="chazt-analytics"></div>
</section>
<script
src="https://api.chazt.com/widget/v1/chazt-widget.js?v=4"
data-token="wgt_live_your_token_here"
data-view="notifications"
data-container="#chazt-notifications"
data-instance="notification-log"
data-height="620px"
async
></script>
<script
src="https://api.chazt.com/widget/v1/chazt-widget.js?v=4"
data-token="wgt_live_your_token_here"
data-view="analytics"
data-container="#chazt-analytics"
data-instance="campaign-report"
data-height="620px"
async
></script>
Script attributes
| Attribute | Required | Description |
|---|---|---|
src | Yes | Use https://api.chazt.com/widget/v1/chazt-widget.js?v=4 |
data-token | Yes | Organization widget token created in the AI Gateway admin |
data-view | No | Screen to show; defaults to workspace |
data-container | No | CSS selector for the host container; otherwise the widget is inserted before its script |
data-height | No | Frame height such as 720px, 80vh, 45rem, or 100%; defaults to 720px |
data-instance | No | Stable identifier for this instance; generated automatically when omitted |
data-auto-height | No | Set to true to follow content height, clamped between 360 and 1,400 pixels |
data-audience | No | Contact-group UUID returned by the ERP contact synchronization endpoint |
data-lock-audience | No | Set to true to prevent changing the preselected campaign audience |
The widget uses the full width of its container. Control width, spacing, and page placement with the vendor application's normal layout styles.
Permission reference
Permissions are enforced by the Chazt API, not only hidden in the interface.
| Permission | Allows |
|---|---|
contact.read | List and search contacts |
contact.write | Add, edit, and delete contacts and change their group assignments |
contact.sync | Idempotently synchronize ERP contacts and prepare campaign audiences |
group.read | List and search contact groups and, with contact.read, list group members |
group.write | Create and rename groups and, with contact.write, remove group members |
inbox.read | List inbox conversations |
conversation.read | Read messages in a conversation |
message.send | Send a one-to-one message |
template.read | List approved templates |
template.write | Create, edit, synchronize, and delete supported templates, including media-header templates |
template.send | Send a supported approved template |
campaign.read | List campaigns, scheduled and actual run dates, delivery totals, and recipient-level delivery status and failure information |
campaign.write | Create a supported campaign |
notification.read | List notification delivery events and errors |
analytics.read | View organization and campaign delivery analytics |
A screen also needs the supporting permissions for its actions. For example,
inbox.read displays the inbox list, while conversation.read is needed to
open a conversation and message.send is needed to reply.
Use the token for the logged-in customer
In a multi-tenant product, resolve the widget token for the authenticated vendor tenant before adding the script to the page. One safe pattern is to create the script after the tenant token is returned:
<div id="chazt-inbox"></div>
<script>
async function mountChaztInbox(tenantId) {
const token = await getChaztWidgetTokenForTenant(tenantId);
const script = document.createElement("script");
script.src = "https://api.chazt.com/widget/v1/chazt-widget.js?v=4";
script.dataset.token = token;
script.dataset.view = "inbox";
script.dataset.container = "#chazt-inbox";
script.dataset.instance = "tenant-inbox";
script.dataset.height = "720px";
script.async = true;
document.body.appendChild(script);
}
</script>
Do not put another customer's token in the page, preload tokens for inactive tenants, or store widget tokens in browser analytics and logs.
Browser API and tenant switching
Instances are registered in window.ChaztWidgets. window.ChaztWidget points
to the most recently created instance for simple integrations.
// Find a named instance.
const inbox = window.ChaztWidgets.find(
(widget) => widget.id === "tenant-inbox",
);
// Replace the credential after an authenticated tenant switch.
inbox.setToken("wgt_live_new_tenant_token");
// Change or reload the current screen.
inbox.setView("notifications");
inbox.refresh();
// Remove the iframe and its event listener.
inbox.destroy();
| Method | Purpose |
|---|---|
setToken(token) | Replace the active organization token in memory |
setView(view) | Change to another supported view |
setAudience(uuid, locked) | Select an ERP campaign audience and optionally lock it |
refresh() | Revalidate the token and reload the current view |
destroy() | Remove the embedded frame and widget instance from the page |
When the active tenant changes, replace or destroy the existing instance before showing the new tenant's data.
Campaign and template behavior
The embedded Templates screen lists every template status for tokens with
template.write. Users can create text or media-header templates, add quick
replies and static or dynamic website buttons, edit supported templates,
synchronize status from WhatsApp, and delete templates. Advanced authentication
templates and unsupported button types must still be managed in the full Chazt
application.
The embedded Campaigns screen supports approved templates with positional or
named text values, dynamic website-button values, quick-reply values, and
image, video, or document header URLs. The campaign table includes both
scheduled_at and run_at:
scheduled_atis when the campaign is intended to start. For Send now, it is the time the campaign was queued.run_atis the actual time execution began, derived from the first delivery log. It remains empty until the campaign worker starts processing recipients.
Select View details on a campaign to review every campaign recipient. The details screen provides contact search, delivery-status filtering, pagination, queued and last-updated times, and the WhatsApp error code, reason, and details for failed deliveries when the provider supplies them. When WhatsApp reports only a failed status, the screen clearly states that no detailed provider reason was returned.
Vendors that need the same information outside the embedded screen can call:
GET /widget/v1/campaigns/{campaign_uuid}/deliveries?page=1&per_page=20
GET /widget/v1/campaigns/{campaign_uuid}/deliveries?status=failed&search=customer
The endpoint requires campaign.read, is restricted to the token's
organization, and returns the campaign totals together with a paginated data
array. Each item contains the contact, effective delivery status, timestamps,
provider message ID, and a sanitized failure object when applicable.
Campaigns, recipient delivery records, and delivery metrics are sorted newest first and paginated.
Origin and token security
An embedded widget token is a scoped, revocable browser credential. It must be available to the browser to run the embedded screen, so it is not a server-side secret.
- Allow only exact HTTP or HTTPS origins, such as
https://app.vendor.example. - Include the scheme and port when applicable. Do not include a URL path.
- Wildcard origins are rejected for newly created tokens.
- Give each token the smallest useful permission set and an expiry date.
- Keep the token in memory; do not save it to local storage unless the vendor's security model explicitly requires and protects that storage.
- Do not include full tokens in logs, screenshots, support tickets, source control, analytics events, or error-reporting payloads.
- Revoke a token immediately if it is exposed or the integration is removed.
The widget transfers the token to its Chazt iframe with a browser
postMessage. The iframe validates the parent origin and keeps the token in
memory.
Content Security Policy
If the vendor application has a restrictive Content Security Policy, allow the Chazt loader and frame:
script-src 'self' https://api.chazt.com;
frame-src https://api.chazt.com;
Merge these sources into the vendor's existing policy instead of replacing the complete policy.
Complete test page
Replace the placeholder token and add the test page's exact origin to the token:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Chazt Contacts Test</title>
<style>
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
background: #f8fafc;
}
.page {
max-width: 1200px;
margin: 0 auto;
}
</style>
</head>
<body>
<main class="page">
<h1>Contacts</h1>
<div id="chazt-contacts"></div>
</main>
<script
src="https://api.chazt.com/widget/v1/chazt-widget.js?v=4"
data-token="wgt_live_your_token_here"
data-view="contacts"
data-container="#chazt-contacts"
data-height="720px"
async
></script>
</body>
</html>
Serve this page from a local web server, for example
http://localhost:3000, and add that exact origin to the token's allowed
origins.
Migrate from the floating launcher
Older examples loaded the script without a container and used
window.CHAZT_WIDGET_TOKEN, open(), close(), or toggle(). Those examples
created a floating launcher and should be replaced.
To migrate:
- Add a container to the product page.
- Use the current versioned script URL with
?v=4. - Move the token to
data-token. - Set
data-viewanddata-container. - Remove launcher position, open, close, and toggle logic.
- Create a new token with the permissions required by the selected screens.
Tokens created for the old inbox-only widget may not contain the newer contact, group, campaign, notification, or analytics permissions.
Troubleshooting
The screen is blank or does not appear
- Confirm the script URL returns
200. - Confirm
data-containermatches an element that exists on the page. - Confirm the page's Content Security Policy permits the Chazt script and frame.
- Hard-refresh the page to clear a previously cached widget script.
Origin is not allowed
- Compare the browser origin with the token configuration exactly.
- Include
http://orhttps://and any non-default port. - Add separate development, staging, and production origins when needed.
- Serve local tests over HTTP instead of opening an HTML file directly.
A screen says permission is missing
- Confirm the token includes the permission for the selected view.
- Include supporting action permissions, such as
conversation.readandmessage.sendfor a fully functional inbox. - Create a replacement token when an older token does not include the required permissions.
The wrong organization appears
- Verify the vendor tenant-to-token mapping.
- Replace or destroy the widget instance after every tenant switch.
- Confirm the token was created for the intended Chazt organization.
A token no longer works
- Confirm it is active, has not expired, and has not been revoked.
- Confirm its allowed origin and permissions.
- Create a replacement token and remove the old token from the vendor configuration.
Production endpoint
Use the versioned production loader:
https://api.chazt.com/widget/v1/chazt-widget.js?v=4