npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pineconesai/chat-sdk

v0.9.0

Published

Embeddable AI chat widget for the Pineconesai platform

Readme

Pineconesai Chat Widget — Developer Guide

Table of Contents

  1. Quick start
  2. Installation options
  3. Boot configuration
  4. API reference
  5. Events
  6. User identity & verification
  7. Theming
  8. Custom launcher
  9. Single-page apps
  10. TypeScript usage

1. Quick start

Paste this snippet before the closing </body> tag on every page where you want the widget to appear:

<script>
  (function () {
    window.PineconesAI = function () {
      (window.PineconesAI.q = window.PineconesAI.q || []).push(arguments);
    };
    var s = document.createElement('script');
    s.src = 'https://cdn.pineconesai.com/widget.js';
    s.async = true;
    document.head.appendChild(s);
  })();

  PineconesAI('boot', {
    apiKey: 'pk_live_YOUR_KEY',
  });
</script>

That's it. A floating chat button will appear in the bottom-right corner of the page.


2. Installation options

Option A — Script snippet (recommended for most sites)

Use the snippet above. The main bundle loads asynchronously after your page is interactive. Any PineconesAI(...) calls made before the bundle loads are queued and replayed automatically.

Option B — npm package

npm install @pineconesai/chat-sdk
import '@pineconesai/chat-sdk'   // auto-boots and flushes the command queue

window.PineconesAI('boot', {
  apiKey: 'pk_live_YOUR_KEY',
})

3. Boot configuration

boot() accepts a single configuration object:

PineconesAI('boot', {
  // ── Required ───────────────────────────────────────────────────────────
  apiKey: 'pk_live_YOUR_KEY',         // Your workspace public API key

  // ── API base URL (optional) ────────────────────────────────────────────
  baseUrl: 'https://api.pineconesai.com/api/v2', // Override the API host (local dev)

  // ── User identity (optional) ───────────────────────────────────────────
  user: {
    id:       'usr_42',               // Your app's user ID
    name:     'Jane Doe',
    email:    '[email protected]',
    jwt:      'eyJhbGci...',          // Customer-signed identity JWT (see section 6)
    attributes: {                     // Any custom key/value pairs
      plan:    'pro',
      company: 'Acme Corp',
    },
  },

  // ── AI behaviour (optional) ────────────────────────────────────────────
  ai: {
    context: 'You are a helpful assistant for Acme Corp, a project-management SaaS.',
    suggestedMessages: [              // Shown on the home screen when chat is empty
      'How do I get started?',
      'What does your pricing look like?',
      'Can I import data from another tool?',
    ],
    agentName: 'Acme Assistant',      // Name shown next to AI replies.
                                      // Defaults to "AI agent" ("סוכן AI" in Hebrew).
  },

  // ── Theme (optional) ───────────────────────────────────────────────────
  theme: {
    primaryColor:  '#5b4af7',         // Hex color for buttons, bubbles, header
    position:      'bottom-right',    // 'bottom-right' | 'bottom-left'
    layout:        'panel',           // 'panel' (floating card) | 'drawer' (full-height) | 'inline' (mount in a host element)
    launcherIcon:  'chat',            // 'chat' | 'robot' | 'https://…/icon.png'
    suggestedButtonStyle: 'filled',  // 'filled' (default) | 'outlined'
    headerStyle:   'solid',          // 'solid' (filled primary bar) | 'surface' (light header)

    // ── Design tokens (optional) — each maps to a --pcai-* CSS variable ──
    fontFamily:      'Inter, system-ui, sans-serif', // Base font for all widget text
    radius:          16,             // Corner radius (number = px) for panel, bubbles, controls
    backgroundColor: '#ffffff',      // Panel background
    surfaceColor:    '#f5f5f7',      // Secondary surface — AI bubbles & home cards
    borderColor:     '#e5e5ea',      // Border & divider color
    textColor:       '#1c1c1e',      // Primary text color
    mutedTextColor:  '#8e8e93',      // Muted / secondary text color
    userBubbleColor: '#5b4af7',      // User message bubble (defaults to primaryColor)
    aiBubbleColor:   '#f5f5f7',      // AI message bubble (defaults to surfaceColor)
    shadow:          '0 12px 32px rgba(0,0,0,.16)', // Panel drop shadow (CSS box-shadow)
  },

  // ── Branding (optional) ────────────────────────────────────────────────
  // Every field is optional. Omitted fields fall back to the localized default,
  // so leaving `branding` out keeps the current look unchanged.
  branding: {
    headerTitle:       'Support Chat',     // Static title; replaces the "Hi, Jane 👋" greeting
    headerSubtitle:    'How can we help?', // Defaults to the localized subtitle
    headerLogoUrl:     'https://…/logo.png', // Small logo shown in the header
    showRefreshButton: true,               // Header icon to start a new conversation
    showCloseButton:   true,               // In-panel close (X) button — shown by default; set false to hide
    inputPlaceholder:  'Ask us…',          // Composer placeholder (default: "Type a message…")
    logoAlt:           'Acme',             // Alt text for both logo images
    home: {                                // Hero card on the home screen (omit to hide)
      logoUrl:        'https://…/logo.png',
      title:          'Support Chat',
      subtitle:       'How can we help you?',
      welcomeMessage: 'Have a question? Just ask us or choose an option below.',
    },
  },

  // ── Recent conversations (optional) ────────────────────────────────────
  recentConversationsLimit: 5,        // How many to show in "Recent" (0–20). 0 hides the section.

  // ── Language (optional) ────────────────────────────────────────────────
  language: 'en',                     // 'en' | 'he' — Hebrew enables RTL layout

  // ── Launcher (optional) ────────────────────────────────────────────────
  hideDefaultLauncher:    false,      // Hide the built-in floating button
  customLauncherSelector: '#my-btn', // CSS selector for your own trigger element

  // ── Inline mount (optional) ────────────────────────────────────────────
  container: '#chat-root',            // CSS selector or HTMLElement to mount into (use with theme.layout: 'inline')
})

Full option reference

Every option boot() accepts. apiKey is required — every other field is optional and falls back to the default shown.

| Option | Type | Default | Description | |--------|------|---------|-------------| | apiKey | string | — (required) | Your workspace public API key (pk_live_… / pk_test_…). | | baseUrl | string | https://api.pineconesai.com/api/v2 | Override the API base URL. Useful for local development. | | user.id | string | — | Your app's user ID. | | user.name | string | — | User's display name. Drives the "Hi, Jane 👋" greeting. | | user.email | string | — | User's email address. | | user.jwt | string | — | Customer-signed identity JWT (HS256, sub = user ID, with exp), minted server-side with your integration secret. Verifies a logged-in user (see §6). | | user.attributes | object | — | Any custom key/value pairs (e.g. { plan: 'pro', company: 'Acme' }). | | ai.context | string | — | System context describing your business, steering the AI's replies. | | ai.suggestedMessages | string[] | — | Prompts shown on the home screen when the chat is empty. | | ai.agentName | string | "AI agent" ("סוכן AI" in Hebrew) | Name shown next to AI replies. | | theme.primaryColor | string (hex) | brand default | Color for buttons, bubbles, and the header. | | theme.position | 'bottom-right' \| 'bottom-left' | 'bottom-right' | Which corner the launcher and panel anchor to. | | theme.layout | 'panel' \| 'drawer' \| 'inline' | 'panel' | panel = floating card; drawer = full-height side panel; inline = mount inside a host element (see container). | | theme.launcherIcon | 'chat' \| 'robot' \| string (url) | 'chat' | Built-in icon or a URL to your own image (28×28px). | | theme.suggestedButtonStyle | 'filled' \| 'outlined' | 'filled' | Style of the suggested-message buttons. | | theme.headerStyle | 'solid' \| 'surface' | 'solid' | solid = filled primary-color bar with white text; surface = light header that blends into host chrome. | | theme.fontFamily | string | system UI stack | Base font-family for all widget text (--pcai-font). | | theme.radius | string \| number | 16 | Corner radius for panel, bubbles, and controls. A number is treated as px. | | theme.backgroundColor | string | '#ffffff' | Panel background color. | | theme.surfaceColor | string | '#f5f5f7' | Secondary surface color — AI bubbles and home cards. | | theme.borderColor | string | '#e5e5ea' | Border and divider color. | | theme.textColor | string | '#1c1c1e' | Primary text color. | | theme.mutedTextColor | string | '#8e8e93' | Muted / secondary text color. | | theme.userBubbleColor | string | primaryColor | User message bubble color. | | theme.aiBubbleColor | string | surfaceColor | AI message bubble color. | | theme.shadow | string (CSS box-shadow) | subtle floating shadow | Panel drop shadow. | | branding.headerTitle | string | dynamic greeting | Static header title; replaces the "Hi, Jane 👋" greeting. | | branding.headerSubtitle | string | localized default | Subtitle under the header title. | | branding.headerLogoUrl | string (url) | — | Small square logo shown in the header (≥112×112px). | | branding.showRefreshButton | boolean | false | Show a header icon to start a new conversation. | | branding.showCloseButton | boolean | true | Show the in-panel close (X) button. | | branding.inputPlaceholder | string | "Type a message…" | Composer placeholder text. | | branding.logoAlt | string | "" | Alt text for both logo images. | | branding.home.logoUrl | string (url) | — | Logo on the home-screen hero card. | | branding.home.title | string | — | Title on the home-screen hero card. | | branding.home.subtitle | string | — | Subtitle on the home-screen hero card. | | branding.home.welcomeMessage | string | — | Welcome message on the home-screen hero card. Omit the whole home object to hide the card. | | recentConversationsLimit | number (0–20) | 5 | How many recent conversations to list. 0 hides the section. | | language | 'en' \| 'he' | 'en' | UI language. 'he' enables a right-to-left layout. | | hideDefaultLauncher | boolean | false | Hide the built-in floating button. | | customLauncherSelector | string (CSS selector) | — | CSS selector for your own trigger element. | | container | string (CSS selector) \| HTMLElement | document.body | Host element to mount the widget into. Intended for theme.layout: 'inline'. Falls back to document.body if not found. |


4. API reference

All methods follow the same call signature: PineconesAI('methodName', ...args).

Lifecycle

| Method | Description | |--------|-------------| | PineconesAI('boot', config) | Initialize the widget. Must be called first. | | PineconesAI('shutdown') | Tear down the widget and clear the session. Call on logout. |

UI control

| Method | Description | |--------|-------------| | PineconesAI('show') | Open the chat panel. | | PineconesAI('hide') | Close the chat panel. | | PineconesAI('openNewConversation') | Open the panel and start a new conversation. | | PineconesAI('openNewConversation', 'prefill text') | Same, with text pre-filled in the composer. |

User & data

| Method | Description | |--------|-------------| | PineconesAI('update', user) | Update user identity after login or profile change. The header greeting updates automatically — "Hi, Jane 👋" when a name is supplied, "Hi there 👋" for anonymous visitors. | | PineconesAI('trackEvent', name, metadata?) | Send a custom analytics event. |

File attachments

The widget has built-in file attachment support. A paperclip button appears in the message composer — users can attach up to 5 files per message, each up to 10 MB. Images are shown as thumbnails; other files appear as downloadable chips. No configuration is required to enable this feature.

Attached files are included in message.attachments when you receive messages via the onMessageReceived event (see Section 5).

Events

| Method | Description | |--------|-------------| | PineconesAI('onShow', callback) | Fires when the panel opens. Callback receives no arguments. | | PineconesAI('onHide', callback) | Fires when the panel closes. Callback receives no arguments. | | PineconesAI('onUnreadCountChange', callback) | Fires on unread count changes. Also fires immediately on registration. Callback receives the new count (number). | | PineconesAI('onMessageReceived', callback) | Fires when an incoming message is fully received (AI reply or a human rep). Callback receives a Message. | | PineconesAI('onMessageSent', callback) | Fires when the visitor sends a message. Callback receives a SentMessage (a Message without id). | | PineconesAI('onConversationCreated', callback) | Fires when a new conversation is created on the server (on the visitor's first message in a draft). Callback receives a Conversation. |


5. Events

Subscribe to widget events to keep your own UI in sync:

// Fires every time the chat panel opens
PineconesAI('onShow', function () {
  console.log('Chat opened')
})

// Fires every time the chat panel closes
PineconesAI('onHide', function () {
  console.log('Chat closed')
})

// Fires immediately with current count, then on every change
PineconesAI('onUnreadCountChange', function (count) {
  document.getElementById('my-badge').textContent = count > 0 ? count : ''
})

// Fires when an incoming message is fully received (AI reply or human rep)
PineconesAI('onMessageReceived', function (message) {
  console.log('New message:', message.id, message.content)

  // Distinguish the AI agent from a human support rep
  if (message.senderType === 'human') {
    console.log('Reply from', message.senderName)
  }

  // message.attachments is an array of { id, name, url, mimeType, size }
  if (message.attachments?.length) {
    console.log('Attachments:', message.attachments)
  }
})

// Fires when the visitor sends a message (no id — the persisted id is not
// returned to the SDK)
PineconesAI('onMessageSent', function (message) {
  console.log('Visitor sent:', message.content)
})

// Fires when a new conversation is created on the server (on the first message
// of a draft). On a brand-new conversation this fires before onMessageSent.
PineconesAI('onConversationCreated', function (conversation) {
  console.log('New conversation:', conversation.id, conversation.title)
})

Event payloads

| Event | Callback argument | |-------|-------------------| | onShow | none | | onHide | none | | onUnreadCountChange | count: number | | onMessageReceived | message: Message (shape below) | | onMessageSent | message: SentMessage — a Message without id | | onConversationCreated | conversation: Conversation ({ id, title, lastMessage, unreadCount, updatedAt }) |

Message shape:

| Field | Type | Description | |-------|------|-------------| | id | string | Unique message ID. | | role | 'user' \| 'assistant' | Who the message is from. Incoming messages are 'assistant'. | | content | string | The message text. | | createdAt | number | Creation timestamp (epoch ms). | | attachments | Attachment[] (optional) | Files attached to the message. | | senderType | 'ai' \| 'human' (optional) | Only on assistant messages — distinguishes the AI agent from a human rep. | | senderName | string (optional) | First name of the human representative (when senderType === 'human'). |

Attachment shape:

| Field | Type | Description | |-------|------|-------------| | id | string | Unique attachment ID. | | name | string | Original file name. | | url | string | Download URL. | | mimeType | string | MIME type (e.g. image/png). | | size | number | File size in bytes. |


6. User identity & verification

The widget can run in one of two modes. Pick based on whether the visitor is logged into your app:

| Mode | When to use | What you pass | |------|-------------|---------------| | Unauthenticated (anonymous) | Logged-out visitors, marketing pages | Just apiKey — no user | | Authenticated (verified user) | Logged-in users whose chat history must be protected | apiKey + user with a server-signed user.jwt |

Both modes always boot with your public apiKey (pk_live_… / pk_test_…), which is safe to embed in the browser. Your integration secret is only ever used on your server — never ship it to the client.


Option 1 — Unauthenticated (anonymous)

If you don't pass a user object, the widget generates a random visitorId stored in localStorage. Conversation history persists across page reloads for the same browser, but is not tied to any account.

PineconesAI('boot', {
  apiKey: 'pk_live_YOUR_KEY',
})

Anonymous sessions are allowed only when the integration does not enforce Identity Verification. With enforcement on, a session without a valid user.jwt — including anonymous visitors — is rejected (see Enforcing verification below).


Option 2 — Authenticated (verified user)

Pass a user object once the visitor is logged into your app. Because a browser can claim any user.id, include a server-signed JWT (user.jwt) so visitors can't impersonate one another and read each other's history.

Mint a short-lived JWT on your server, signed with your integration app_secret, and pass it as user.jwt. The identity lives in the token — put it in a user_id claim (sub is also accepted). Your secret never reaches the browser — only the per-user token does.

Server side (Node.js example):

const jwt = require('jsonwebtoken')

const token = jwt.sign(
  { user_id: user.id },                     // the identity — no need to pass it separately
  process.env.PINECONESAI_APP_SECRET,       // integration secret — server only
  { algorithm: 'HS256', expiresIn: '1h' },  // exp is required (recommend ≥ 5 min)
)

Client side:

PineconesAI('boot', {
  apiKey: 'pk_live_YOUR_KEY',
  user: {
    name:  user.name,   // unprotected fields — for display / low-fidelity data
    email: user.email,
    jwt:   token,       // from your server — carries the verified identity
  },
})

The backend verifies the signature against your app_secret and checks exp, then takes the identity from the token's user_id (or sub) claim. You don't need to pass user.id separately when a jwt is supplied.

Never expose your integration secret in client-side code. Always generate the jwt server-side.

Protecting individual fields ("authenticated only")

By default the widget still trusts name/email/attributes sent in the boot() body — convenient, but a determined visitor could pass someone else's email. To lock a field down, mark it Protected ("authenticated only") in the admin app under Chat integration → User Context (toggle the Protected checkbox next to email, name, or any custom attribute). For every protected field, the backend uses the value from the verified JWT claim and ignores whatever the body sends; if the token doesn't carry it, the field is dropped (no unauthenticated fallback).

Put each protected field in the JWT as a top-level claim (Fin-style):

const token = jwt.sign(
  {
    user_id: user.id,        // the identity
    email:   user.email,     // protected → trusted from here, body ignored
    name:    user.name,
    plan:    user.plan,      // any custom attribute can be protected too
  },
  process.env.PINECONESAI_APP_SECRET,
  { algorithm: 'HS256', expiresIn: '1h' },
)

Fields not marked protected keep working from the boot() body, so this is opt-in and backward-compatible. user.name in the body is still used for the client-side greeting ("Hi, Jane 👋") regardless — it's display-only and never trusted for identity when name is protected.

Updating identity mid-session

If the visitor logs in after the widget has booted, call update() with the same fields (including the proof) instead of re-booting:

PineconesAI('update', {
  name:  currentUser.displayName,
  email: currentUser.email,
  jwt:   token,   // from your server — carries the verified identity
})

Call shutdown() on logout so the session and local state are cleared:

PineconesAI('shutdown')

Enforcing verification per integration

An integration can require Identity Verification (toggle it in the admin panel). When enabled, every session must carry a valid user.jwt — the widget always boots with the public apiKey plus a verified user (Option 2):

  • A session without a valid user.jwt — including an anonymous/logged-out visitor — is rejected with 403 identity_verification_required.
  • An invalid or expired token is rejected with 401 identity_verification_failed.

So on an integration with verification enabled, only logged-in users (for whom your server can mint a user.jwt) can open the widget. Leave verification off if you need anonymous/logged-out visitors to chat.


7. Theming

Customise the widget's appearance via the theme option in boot():

PineconesAI('boot', {
  apiKey: 'pk_live_YOUR_KEY',
  theme: {
    primaryColor: '#e85d04',       // Your brand color
    position:     'bottom-left',   // Move the launcher to the left
    layout:       'drawer',        // Full-height side drawer instead of a floating card
    launcherIcon: 'robot',         // Built-in robot icon
  },
})

Layout options:

| Value | Result | |-------|--------| | 'panel' (default) | Floating card anchored above the launcher | | 'drawer' | Full-height panel flush to the screen edge, sliding in from the side set by position (fills the screen on narrow viewports) | | 'inline' | Panel mounted inside a host element (see container) — always open, no floating launcher |

Launcher icon options:

| Value | Result | |-------|--------| | 'chat' (default) | Speech bubble icon | | 'robot' | Robot face icon | | 'https://…/icon.png' | Your own image (28×28px recommended) |

Header style

theme.headerStyle controls how the header bar looks:

| Value | Result | |-------|--------| | 'solid' (default) | Filled primary-color bar with white text | | 'surface' | Light header using the background/text/border tokens — blends into a host app's chrome |

Design tokens

For finer control, theme exposes a set of design tokens that each map to a --pcai-* CSS variable. All are optional; any token you omit keeps its built-in default, so existing integrations are unaffected. These are especially useful for matching the widget to a host application in the inline layout.

PineconesAI('boot', {
  apiKey: 'pk_live_YOUR_KEY',
  theme: {
    fontFamily:      'Inter, system-ui, sans-serif',
    radius:          12,               // number → px
    backgroundColor: '#ffffff',
    surfaceColor:    '#f5f5f7',
    borderColor:     '#e5e5ea',
    textColor:       '#1c1c1e',
    mutedTextColor:  '#8e8e93',
    userBubbleColor: '#5b4af7',
    aiBubbleColor:   '#f5f5f7',
    shadow:          '0 12px 32px rgba(0,0,0,.16)',
  },
})

| Token | Default | Description | |-------|---------|-------------| | fontFamily | system UI stack | Base font-family for all widget text. | | radius | 16 | Corner radius (number = px) for panel, bubbles, controls. | | backgroundColor | #ffffff | Panel background color. | | surfaceColor | #f5f5f7 | Secondary surface — AI bubbles and home cards. | | borderColor | #e5e5ea | Border and divider color. | | textColor | #1c1c1e | Primary text color. | | mutedTextColor | #8e8e93 | Muted / secondary text color. | | userBubbleColor | primaryColor | User message bubble color. | | aiBubbleColor | surfaceColor | AI message bubble color. | | shadow | subtle floating shadow | Panel drop shadow (CSS box-shadow). |

Inline layout

Set theme.layout: 'inline' with a container to embed the widget inside your own page element instead of floating over it. There's no launcher — the panel fills the host element and stays open.

PineconesAI('boot', {
  apiKey: 'pk_live_YOUR_KEY',
  container: '#chat-root',       // CSS selector or HTMLElement
  theme: { layout: 'inline', headerStyle: 'surface' },
})
<div id="chat-root" style="width: 400px; height: 600px;"></div>

If the container element isn't found, the widget falls back to document.body.

Branding & home screen

The branding option restyles the widget's text and imagery to match your product — a static header title, your logo, a hero/welcome card on the home screen, and a custom input placeholder:

PineconesAI('boot', {
  apiKey: 'pk_live_YOUR_KEY',
  theme: { primaryColor: '#2563eb', suggestedButtonStyle: 'outlined' },
  branding: {
    headerTitle: 'Support Chat',
    headerLogoUrl: 'https://acme.com/logo.png',
    showRefreshButton: true,
    showCloseButton: true,
    inputPlaceholder: 'Ask us…',
    home: {
      logoUrl: 'https://acme.com/logo.png',
      title: 'Support Chat',
      subtitle: 'How can we help you?',
      welcomeMessage: 'Have a question? Just ask us or choose an option below.',
    },
  },
})

Every branding field is optional — any field you omit falls back to the localized default (or, for headerTitle, the dynamic "Hi, Jane 👋" greeting). So an empty/absent branding block leaves the widget looking exactly as it does today.

Logo images (headerLogoUrl, home.logoUrl) are URLs. Use a square image — at least 112×112px for crisp rendering on retina displays. Set branding.logoAlt for the images' alt text (defaults to empty/decorative).


8. Custom launcher

Hide the default button and use your own

PineconesAI('boot', {
  apiKey: 'pk_live_YOUR_KEY',
  hideDefaultLauncher: true,
  customLauncherSelector: '#open-chat-btn',
})
<button id="open-chat-btn">Talk to us</button>

Clicking the element matching the selector will toggle the widget open/closed automatically.

Programmatic control

You can also open and close the widget from anywhere in your own code:

// Open on a specific user action
document.getElementById('help-link').addEventListener('click', () => {
  PineconesAI('show')
})

// Open with a pre-filled message
document.getElementById('upgrade-btn').addEventListener('click', () => {
  PineconesAI('openNewConversation', 'I want to upgrade my plan.')
})

9. Single-page apps

React

Boot once when your app mounts and shut down on unmount:

import { useEffect } from 'react'

export function ChatWidget({ user }) {
  useEffect(() => {
    PineconesAI('boot', {
      apiKey: 'pk_live_YOUR_KEY',
      // `user.jwt` is minted on your server (see §6). Omit the whole `user`
      // object for anonymous visitors.
      user: { id: user.id, name: user.name, email: user.email, jwt: user.chatJwt },
    })

    return () => {
      PineconesAI('shutdown')
    }
  }, [])

  // Update identity when the user object changes
  useEffect(() => {
    if (user) {
      PineconesAI('update', { id: user.id, name: user.name, email: user.email, jwt: user.chatJwt })
    }
  }, [user.id])

  return null
}

Vue

// main.js or a plugin
app.mixin({
  mounted() {
    if (this.$options.name === 'App') {
      PineconesAI('boot', { apiKey: 'pk_live_YOUR_KEY' })
    }
  },
  beforeUnmount() {
    if (this.$options.name === 'App') {
      PineconesAI('shutdown')
    }
  },
})

Route changes

Call update() on each navigation to refresh page context:

router.afterEach((to) => {
  PineconesAI('update', {})   // pings the server and checks for new messages
})

10. TypeScript usage

When importing via npm, all public types are exported:

import type { BootConfig, UserConfig, ThemeConfig, BrandingConfig, AIConfig, Message, Conversation, Session } from '@pineconesai/chat-sdk'

const config: BootConfig = {
  apiKey: 'pk_live_YOUR_KEY',
  user: {
    id:    '42',
    email: '[email protected]',
  },
  theme: {
    primaryColor: '#5b4af7',
  },
}

PineconesAI('boot', config)

PineconesAI('onMessageReceived', (message: Message) => {
  console.log(message.content)
  // message.attachments is typed on the Message; message.senderType is 'ai' | 'human'
})

API keys

| Key format | Use | |------------|-----| | pk_live_… | Production — real users, real data | | pk_test_… | Development & testing |

Get your keys from the Pineconesai Admin Panel.