JavaScript API
Control the Pengo widget from your own code. Open and close the chat, listen for events, pass visitor details, report cart value and more with pengo().
On this page
- Reference
- Open, close and toggle
- Hide and show the chat bubble
- Listen for events
- Read the unread count and open state
- Change the language
- Save visitor attributes
- Identify signed-in users
- Shut down on sign-out
- Start a new conversation
- Track events and cart value
- Show a proactive message
- Turn off the tab title flash
- Calling the API before the widget loads
- Continue in chat links
- TypeScript
The Pengo snippet adds one global function to your page: pengo(). Use it to open the chat from your own buttons, react when new messages arrive, pass details about the visitor, report cart value for proactive messages, and more.
Every command has the same shape: the command name first, then its arguments.
pengo('open');
pengo('on', 'unread', function (count) { /* … */ });
pengo('setVisitor', { plan: 'pro' }); Once the Pengo loader is on the page you can also use the shorthand form, pengo.open(), pengo.on('unread', …) and so on. The pengo('open') form works at any time, so it’s the safer choice in code that may run early.
#Reference
| Command | What it does |
|---|---|
pengo('open') | Opens the chat window. |
pengo('close') | Closes the chat window. |
pengo('toggle') | Opens the chat if it’s closed, closes it if it’s open. |
pengo('hide') | Hides the chat bubble. |
pengo('show') | Shows the chat bubble again after hide. |
pengo('on', event, callback) | Calls callback when open, close, unread or message happens. |
pengo('off', event, callback) | Removes a callback. Leave out callback to remove all callbacks for that event. |
pengo('getUnread', callback) | Gives you the number of unread messages. |
pengo('isOpen') | Returns true when the chat window is open. |
pengo('setLocale', code) | Switches the widget’s language, for example 'fr' or 'pt-BR'. |
pengo('setVisitor', attributes) | Saves attributes on the visitor. update is the same command. |
pengo('identify', user) | Recognises a signed-in user. See identity verification. |
pengo('shutdown') | Forgets the visitor on this browser. Call it when a user signs out. |
pengo('newConversation', prefill) | Opens the chat on a new conversation, with optional text in the message box. |
pengo('track', name, properties) | Records an event on the visitor. 'cart' with a value reports cart value. |
pengo('showMessage', text) | Shows a proactive message next to the chat bubble. |
pengo('titleFlash', enabled) | Turns the new-message tab title flash on or off for this page. |
#Open, close and toggle
pengo('open');
pengo('close');
pengo('toggle'); open loads the full widget straight away if it isn’t loaded yet, then opens the chat window. Opening the chat clears the unread count.
A common use is a “Chat with us” button or link anywhere on your page:
<button type="button" onclick="pengo('open')">Chat with us</button> #Hide and show the chat bubble
pengo('hide');
pengo('show'); hide hides the chat bubble on this page. A chat window that’s already open stays open, so visitors don’t lose their place. Pair it with your own button and pengo('open') if you want a custom launcher.
To hide the bubble for everyone without code, use the launcher settings described in visibility and branding.
#Listen for events
function onUnread(count) {
document.querySelector('#chat-count').textContent = count;
}
pengo('on', 'unread', onUnread);
// Later, to stop listening:
pengo('off', 'unread', onUnread); | Event | When it fires | Your callback receives |
|---|---|---|
open | The chat window opens. | Nothing. |
close | The chat window closes. | Nothing. |
unread | The unread count changes, including when it goes back to 0. | The new count, a number. |
message | A new message from your team or Pengo Intelligence reaches the widget. | An object with text and senderName. |
For message, text is a preview of the message (up to 280 characters). senderName is the assigned teammate’s name for replies from your team, or your company name for other messages. The event fires for messages that arrive while the chat frame is loaded, which it is once the visitor opens the chat or has a conversation in progress.
pengo('on', 'message', function (message) {
console.log(message.senderName + ': ' + message.text);
}); You can register callbacks before the widget loads. They’re kept and start firing once it’s ready.
#Read the unread count and open state
pengo('getUnread', function (count) {
console.log('Unread messages:', count);
});
var open = pengo('isOpen'); // true or false getUnread calls your callback with the count and also returns it. The callback form is the reliable one, because it works however early you call it. isOpen returns true or false and has no callback form. To track the open state over time, listen for the open and close events instead. See calling the API before the widget loads for what these return early on.
#Change the language
pengo('setLocale', 'de');
pengo('setLocale', 'pt-BR'); Pass a language code of two or three lowercase letters, optionally followed by a hyphen and a region, such as es or pt-BR. Codes that don’t match that format are ignored. The language you set here wins over the language chosen in Pengo’s settings and over the visitor’s browser language. See languages for the languages the widget supports.
#Save visitor attributes
pengo('setVisitor', {
plan: 'pro',
company_size: 50,
trial_ends: '2026-11-30'
});
// Same thing:
pengo('update', { plan: 'enterprise' }); Attributes show on the visitor’s profile in Pengo and are merged with what’s already saved. Each call replaces only the keys you pass. Some rules apply:
- Keys start with a letter and use letters, numbers and underscores, up to 50 characters.
- Values are saved as text, up to 500 characters each. Objects and arrays are ignored.
- Up to 50 attributes per call.
- Keys starting with
pengo_and keys such asuser_idandverifiedare reserved and ignored. - Passing
cart_valuealso updates the cart value used by proactive messages, liketrack('cart').
Attributes set with setVisitor can’t overwrite attributes that a verified identify call set, and they can’t change the name, email, phone or company of a verified user. To set those for a signed-in user, use identify.
#Identify signed-in users
pengo('identify', {
userId: 'user_123',
userHash: 'HASH_FROM_YOUR_SERVER',
name: 'Ada Lovelace',
email: 'ada@example.com',
attributes: { plan: 'pro' }
}); identify links the visitor to a user in your own system, so they see the same conversations on every device. The userHash is computed on your server with your identity secret. Without a valid hash, Pengo only saves the name, email and attributes and never links the visitor across devices.
Calling identify on every page load is fine: Pengo only sends it again when the user or the details change. The full setup is in identity verification.
#Shut down on sign-out
pengo('shutdown'); Call shutdown when a user signs out, especially on shared computers. Pengo forgets the visitor, their conversation and unread count on this browser, signs this device out of their chat, and continues as a new anonymous visitor.
#Start a new conversation
pengo('newConversation');
pengo('newConversation', 'Hi, I have a question about my order #1042'); Opens the chat on a new conversation. If you pass text, it’s placed in the message box (up to 2,000 characters) so the visitor can edit it and press send. Nothing is sent until they do.
#Track events and cart value
pengo('track', 'cart', { value: 129 });
pengo('track', 'signed_up', { plan: 'starter' }); track records an event on the visitor. Pengo saves the event name as the last_event attribute and the properties you pass as visitor attributes, with the same rules as setVisitor.
The cart event is special: its value is the visitor’s cart value, which proactive messages can target. Call it whenever the cart changes. A proactive message that targets cart value checks again right away.
#Show a proactive message
pengo('showMessage', 'Need help choosing a plan? We can help.'); When the chat is closed, the chat bubble nudges and a preview card with your message appears next to it, under your company name. The card shows up to 280 characters. When the visitor opens the chat, the same message is shown in the conversation.
#Turn off the tab title flash
pengo('titleFlash', false); // off
pengo('titleFlash', true); // back on When a new message arrives while your page’s tab is in the background, Pengo alternates the tab title with a short notice, such as the unread count and the sender’s name. titleFlash(false) turns this off for the current page, for example on pages that manage their own title.
#Calling the API before the widget loads
Pengo loads in three steps, and your calls work at every step:
- The snippet defines a small
pengo()function. Calls made now are queued. Only thepengo('command', …)form exists at this point. - The loader (
embed.js) replaces it, draws the chat bubble and keeps queueing. The shorthand methods likepengo.open()are available from here. - The full widget loads when the visitor hovers, focuses, taps or clicks the chat bubble, or once the browser is idle shortly after the page loads. Calling
openornewConversation, or arriving through a Continue in chat link, loads it straight away. It then runs every queued call in the order you made them.
So pengo('setVisitor', …), pengo('on', …) or pengo('identify', …) can go right after the snippet without waiting for anything.
Only the two commands that return a value depend on timing:
| Command | Before the loader runs | Before the full widget loads | After it loads |
|---|---|---|---|
pengo('getUnread', callback) | Returns nothing. The callback runs once the widget loads. | Returns the last count saved in this browser tab and calls the callback with it. | Returns the live count and calls the callback. |
pengo('isOpen') | Returns nothing. | Always returns false. | Returns the live state. |
If you need the open state as soon as possible, listen for the open and close events.
#Continue in chat links
When Pengo emails a visitor about their conversation, the Continue in chat link takes them back to your site with #pengo_resume= and a one-time token added to the address. The loader removes it from the address bar right away and opens the chat on that conversation, on any device.
There’s nothing to set up, as long as the Pengo snippet is on the page the link lands on. Pengo uses the page the visitor was on when that page is on your website, and your website’s address otherwise. If your single-page app rewrites the address hash when it starts, make sure it leaves #pengo_resume= in place until Pengo has loaded.
For signed-in users linked with identity verification, the link only opens the conversation in a browser that’s already signed in as that user.
#TypeScript
pengo is a global function. A minimal declaration:
declare global {
interface Window {
pengo?: (...args: unknown[]) => unknown;
}
}
window.pengo?.('open'); Common questions
Can I open the chat from my own button?
Yes. Call pengo('open') from your button's click handler. Add pengo('hide') if you also want to hide the default chat bubble.
Do I need to wait for the widget to load before calling pengo()?
No. Calls made before the widget loads wait in a queue and run in order once it's ready. Only the return values of getUnread and isOpen depend on timing.
Why does pengo.open() throw an error but pengo('open') works?
The install snippet only defines the pengo() function. The shorthand methods such as pengo.open() exist once the Pengo loader has run, so use the pengo('open') form in code that may run early.
Was this guide helpful?
Your answer helps us improve it.
Sorry about that. or contact support, and we'll fix the guide.