Embed the Widget
The widget loads from a CDN and is initialized with your project’s API key.
Script tag
Add this to your HTML, just before the closing </body> tag:
<script
src="https://cdn.bubblegram.co/widget.js"
data-key="pk_live_YOUR_KEY_HERE"
async
></script>
Your API key is shown in the project’s Embed section in the dashboard, visible after your Telegram group is linked.
Attributes
| Attribute | Required | Description |
|---|---|---|
data-key | Required | Your project’s public API key. Starts with pk_live_. |
data-api | Optional | API base URL. Defaults to https://api.bubblegram.co. |
data-email | Optional | Pre-fill the visitor’s email. Useful on server-rendered pages where the user is already signed in. Skips the email input even when “Require email” is enabled. |
JavaScript API
After the script loads, a Bubblegram object is available on window. You can control the widget programmatically:
Bubblegram.open() // open the chat panel
Bubblegram.close() // close the chat panel
Bubblegram.connect() // reconnect WebSocket (if disconnected)
Bubblegram.setEmail('[email protected]') // pre-fill email for signed-in users
The widget has no way to detect your app’s auth state on its own. Without data-email or setEmail, every visitor shows up as anonymous in your dashboard, signed in or not. Call setEmail yourself once the email is known after client-side auth. For example, in React or Next.js after the session loads:
useEffect(() => {
if (session?.user?.email) {
window.Bubblegram?.setEmail(session.user.email)
}
}, [session?.user?.email])
If this runs before widget.js has finished loading (common in SPAs, since the script tag is async), window.Bubblegram won’t exist yet and the call is silently lost. Use the queue stub shown in the React/Next.js examples below to make it safe to call anytime.
Calling setEmail with a different email than the one already set starts a fresh conversation: the current session is cleared and reinitialized, local message history is wiped, and the WebSocket reconnects, before the new email is saved. The first setEmail call for a visitor (no email set yet) doesn’t trigger this. Keep this in mind if you call setEmail again later, e.g. when a visitor switches accounts, since any messages from the old session won’t carry over.
On server-rendered pages (PHP, Rails, Django, etc.) where the email is available at render time, use the data-email attribute instead:
<script
src="https://cdn.bubblegram.co/widget.js"
data-key="pk_live_YOUR_KEY_HERE"
data-email="<?= $currentUser->email ?>"
async
></script>
For React, Next.js, or other SPAs, add the script to your root HTML file. In Vite or Create React App, that’s index.html:
<script>
window.Bubblegram = window.Bubblegram || (function () {
var q = []
var api = { q: q }
;['init', 'sendMessage', 'connect', 'open', 'close', 'setEmail', 'destroy'].forEach(function (m) {
api[m] = function () { q.push([m, Array.prototype.slice.call(arguments)]) }
})
return api
})()
</script>
<script
src="https://cdn.bubblegram.co/widget.js"
data-key="pk_live_YOUR_KEY_HERE"
async
></script>
In Next.js, use the built-in Script component in your _app.tsx or root layout. Load the stub with beforeInteractive so it exists before any of your components try to call Bubblegram:
import Script from 'next/script'
<Script id="bubblegram-stub" strategy="beforeInteractive">
{`window.Bubblegram = window.Bubblegram || (function () {
var q = []
var api = { q: q }
;['init', 'sendMessage', 'connect', 'open', 'close', 'setEmail', 'destroy'].forEach(function (m) {
api[m] = function () { q.push([m, Array.prototype.slice.call(arguments)]) }
})
return api
})()`}
</Script>
<Script
src="https://cdn.bubblegram.co/widget.js"
data-key="pk_live_YOUR_KEY_HERE"
strategy="afterInteractive"
/> Attachments
Visitors can attach a file to any widget message: images, PDFs, or a short video. It shows up in Telegram in the same topic as the rest of the conversation, and replying with a file works the same way in the other direction.
Limits
| Type | Accepted formats | Max size |
|---|---|---|
| Image | JPEG, PNG, WebP, GIF, HEIC/HEIF | 5 MB |
| application/pdf | 15 MB | |
| Video | MP4, WebM, QuickTime | 20 MB |
HEIC/HEIF photos (the default on iPhone) are converted automatically so they open in any browser. The video limit matches Telegram’s own file-fetch limit for bots, so it isn’t something we can raise on our end.
Plan requirement. Attachments are available on the starter, pro, and custom plans. On the free plan, visitors can’t attach files.