AI Agents and WhatsApp for Developers.
Build, deploy, and manage AI agents in minutes.
Plazbot is a platform that lets developers create AI agents and deploy them across multiple channels:
- AI Search Portals - ChatGPT-like search experience for your website
- WhatsApp - Automated AI conversations via the WhatsApp Business API
- Web Widgets - Embeddable chat widgets for any website
- Your own software - Full API access for custom integrations
This repository contains working examples for every feature in the Plazbot SDK.
npm install plazbotimport { Plazbot } from 'plazbot';
const plazbot = new Plazbot({
workspaceId: "YOUR_WORKSPACE_ID",
apiKey: "YOUR_API_KEY",
zone: "LA" // "EU" for Europe
});
// Create an agent
const agent = await plazbot.agent.addAgent({
name: "My Agent",
prompt: "You are a helpful assistant.",
zone: "LA",
buffer: 5
});
// Chat with the agent
const response = await plazbot.agent.onMessage({
agentId: agent.agentId,
question: "What can you do?",
sessionId: crypto.randomUUID()
});
console.log(response.answer);You can also import classes individually:
import { Agent, Portal, Message, Template, Contact } from 'plazbot';
const bot = new Agent({
workspaceId: "YOUR_WORKSPACE_ID",
apiKey: "YOUR_API_KEY",
zone: "LA"
});Get your API Key and Workspace ID from the Plazbot Dashboard:
- Log in to your account
- Go to Settings > API KEY
- Copy your
API KeyandWorkspace ID
Guides:
plazbot-sdk-examples/
index.ts # All examples in one file
agent.basic.config.json # Minimal agent configuration
agent.config.json # Full agent config (services, actions, channels)
prompt.txt # Example prompt text
git clone https://github.com/plazbot/plazbot-sdk-examples.git
cd plazbot-sdk-examples
npm installEdit index.ts and replace [YOUR_WORKSPACE_ID] and [YOUR_API_KEY] with your credentials. Then:
npm startUncomment the example functions you want to run in the main() function at the bottom of index.ts.
Create an agent from a JSON config file or inline:
// From config file
const config = JSON.parse(await fs.readFile('agent.basic.config.json', 'utf-8'));
config.prompt = "Your custom prompt here";
const agent = await plazbot.agent.addAgent(config);
console.log(agent.agentId);// Inline
const agent = await plazbot.agent.addAgent({
name: "Sales Agent",
prompt: "You are a sales assistant for our company.",
zone: "LA",
buffer: 5,
color: "blue",
question: "How can I help you?",
enable: true,
examples: [
{ value: "What are your prices?", color: "blue" },
{ value: "Schedule a demo", color: "orange" }
]
});await plazbot.agent.updateAgent(agentId, {
name: "Sales Agent Updated",
buffer: 8
});const agents = await plazbot.agent.getAgents();
const agent = await plazbot.agent.getAgentById({ id: agentId });const copy = await plazbot.agent.copyAgent({ id: agentId });await plazbot.agent.deleteAgent({ id: agentId });const response = await plazbot.agent.onMessage({
agentId: agentId,
question: "What features does Plazbot have?",
sessionId: "unique-session-id",
file: "https://example.com/document.pdf", // optional - OCR
multipleAnswers: true // optional
});
console.log(response.answer);onMessage Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
agentId |
string |
Yes | Agent identifier |
question |
string |
Yes | User's message |
sessionId |
string |
Yes | Unique session ID for conversation context |
file |
string |
No | Public URL to a file (PDF, PNG, JPG) for OCR |
multipleAnswers |
boolean |
No | Return response as multiple blocks |
Supported file types for OCR:
| Type | Supported |
|---|---|
.jpg, .png, .bmp, .gif, .tiff |
Yes |
.pdf |
Yes (embedded text or scanned images) |
.docx, .xlsx |
No |
Send stream: true to receive the response as a real-time stream of tokens instead of waiting for the complete response. Requires the agent to have useToolCalling: true (enabled by default for new agents).
const response = await fetch('https://api.plazbot.com/api/agent/on-message', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY',
'x-workspace-id': 'YOUR_WORKSPACE_ID'
},
body: JSON.stringify({
agentId: agentId,
question: "What features does Plazbot have?",
sessionId: "unique-session-id",
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const chunk = JSON.parse(line.substring(6));
if (chunk.type === 'text') {
process.stdout.write(chunk.content); // Print tokens as they arrive
} else if (chunk.type === 'tool_call') {
console.log(`\nExecuting: ${chunk.tool_name}...`);
} else if (chunk.type === 'done') {
console.log(`\nTokens: ${chunk.input_tokens} in / ${chunk.output_tokens} out`);
}
}
}SSE Chunk Types:
| Type | Description |
|---|---|
text |
Partial text token (content field) |
tool_call |
Agent is executing a tool (tool_name field) |
tool_result |
Tool execution completed |
done |
Stream finished (input_tokens, output_tokens, estimated_cost) |
error |
Error occurred (error field) |
Supported providers: OpenAI, Claude (Anthropic), Gemini (Google)
Note: The
streamparameter is optional and defaults tofalse. Existing clients are not affected.
Enable the embeddable web widget for an agent:
const result = await plazbot.agent.enableWidget({ id: agentId, enable: true });
console.log(result.script); // HTML script to embedWidget configuration is set in the agent config:
{
"enableWidget": true,
"darkWidget": false,
"nameWidget": "My Assistant",
"initialShowWidget": false,
"iconWidget": "robot"
}Update specific sections of an agent without sending the full config:
await plazbot.agent.setInstructions(agentId, {
tone: "professional",
style: "short answers",
language: "es-419",
emojis: false,
maxWords: 100
});
await plazbot.agent.setPersona(agentId, {
name: "Maximo",
role: "Virtual assistant",
speaksInFirstPerson: true,
isHuman: false
});
await plazbot.agent.setFallbacks(agentId, {
noAnswer: "Sorry, I don't have information on that topic.",
serviceError: "There was a problem. Please try again later.",
doNotUnderstand: "Could you rephrase that?"
});
await plazbot.agent.setRules(agentId, {
doNotMentionPrices: false,
doNotDiagnose: true,
doNotRespondOutsideHours: "Monday to Friday 9am to 6pm."
});
await plazbot.agent.setTags(agentId, ["sales", "ai"]);Improve a prompt using AI:
const result = await plazbot.agent.improvePrompt("you are a bot that helps people");
console.log(result.result);Upload documents so the agent can use RAG to answer questions:
// Upload file
const file = await plazbot.agent.addFile({
fileUrl: "https://example.com/docs/pricing.pdf",
reference: "Product pricing and plan details for 2025.",
tags: ["pricing", "plans"],
agentId: agentId
});
// Check processing status
const status = await plazbot.agent.validateFile({ fileId: file.fileId });
// Remove file
await plazbot.agent.deleteFile({ fileId: file.fileId, agentId: agentId });Supported formats: .pdf, .doc, .docx
The agent config JSON follows a strict schema. See agent.config.json for a full example. Here are the key fields:
| Field | Type | Description |
|---|---|---|
name |
string |
Agent name |
prompt |
string |
Custom instructions that define agent behavior |
zone |
"LA" | "EU" |
Must match account zone |
buffer |
number |
Message context window (3-10) |
color |
string |
blue, orange, green, gray, white |
question |
string |
Main question shown in portals/widgets |
enable |
boolean |
Active/inactive |
timezone |
string |
TZ database format |
examples |
array |
Quick reply suggestions (max 5) |
tags |
string[] |
Classification tags |
| Field | Description |
|---|---|
instructions.tone |
professional, friendly, serious, etc. |
instructions.style |
short answers, detailed, bulleted, etc. |
instructions.personality |
friendly, formal, curious, etc. |
instructions.language |
Language code: es-419, en-US, pt-BR, etc. |
instructions.emojis |
true / false |
instructions.maxWords |
Max words per response |
instructions.avoidTopics |
Topics the agent should avoid |
instructions.respondOnlyIfKnows |
Only answer when confident |
instructions.greeting |
Initial greeting message |
| Field | Description |
|---|---|
person.name |
Agent's display name |
person.role |
Role description |
person.speaksInFirstPerson |
Uses "I" in responses |
person.isHuman |
Identifies as AI or human |
| Field | Description |
|---|---|
fallbacks.noAnswer |
Response when agent has no information |
fallbacks.serviceError |
Response on external service failure |
fallbacks.doNotUnderstand |
Response when agent can't understand |
| Field | Description |
|---|---|
rules.doNotMentionPrices |
Prevents price discussion |
rules.doNotDiagnose |
Disables medical/technical diagnosis |
rules.doNotRespondOutsideHours |
Business hours text (works with timezone) |
Agents can call external APIs and execute internal actions based on user intent.
{
"services": [
{
"intent": "schedule_date",
"reference": "Service to schedule medical appointments",
"enabled": true,
"method": "POST",
"endpoint": "https://api.clinic.com/v1/date/schedule",
"requiredFields": [
{ "name": "nombre", "type": "string", "promptHint": "What's your name?" },
{ "name": "email", "type": "email", "promptHint": "What's your email?" },
{ "name": "fecha", "type": "date", "promptHint": "When would you like to schedule?" },
{
"name": "numeros_a_portar",
"type": "arrayObject",
"description": "List of phone numbers to port",
"promptHint": "Which numbers do you want to port? I need: plan type, carrier, and number.",
"properties": [
{ "name": "plan_type", "type": "string" },
{ "name": "carrier", "type": "string" },
{ "name": "number", "type": "string" }
]
}
],
"headers": {
"Authorization": "Bearer {{apiKey}}",
"Content-Type": "application/json"
},
"bodyTemplate": {
"nombre": "{{nombre}}",
"email": "{{email}}",
"fecha": "{{fecha}}"
},
"responseMessage": "Your appointment is confirmed for {{date}}",
"responseConditions": [
{ "condition": "$.response.status == 'success'", "message": "Confirmed for {{date}}!" },
{ "condition": "$.response.status == 'conflict'", "message": "That slot is taken." }
]
}
]
}| Field | Description |
|---|---|
intent |
Unique identifier for the service |
reference |
Short description to help AI detect when to trigger |
method |
GET or POST |
endpoint |
External API URL |
requiredFields |
Fields the agent must collect from the user |
requiredFields[].properties |
Sub-properties for arrayObject type fields |
bodyTemplate |
Request body with {{field}} interpolation |
responseMapping |
Extract data from API response (JSONPath) |
responseConditions |
Conditional responses based on API result |
| Type | Description | Example Value |
|---|---|---|
string |
Free text | "John Doe" |
email |
Valid email address | "user@email.com" |
phone |
Phone number | "+51987654321" |
date |
Date (AI-normalized) | "2024-03-15" |
datetime |
Date and time | "2024-03-15 14:30" |
number |
Decimal number | 99.99 |
integer |
Whole number | 42 |
boolean |
True or false | true |
array |
Array of strings | ["value1", "value2"] |
arrayObject |
Array of objects | [{"field": "value"}] |
Use arrayObject when you need the user to provide a list of items with multiple attributes. Define sub-properties with name and type:
{
"name": "items_to_order",
"type": "arrayObject",
"description": "List of items the customer wants to order",
"promptHint": "What items do you want to order? I need product name, quantity, and size.",
"properties": [
{ "name": "product", "type": "string" },
{ "name": "quantity", "type": "integer" },
{ "name": "size", "type": "string" }
]
}The AI will collect data and produce:
[
{ "product": "T-Shirt", "quantity": 2, "size": "M" },
{ "product": "Hoodie", "quantity": 1, "size": "L" }
]Supported sub-property types: string, number, integer, boolean
{
"actions": [
{
"intent": "transfer_to_human",
"reference": "Transfer conversation to human agent",
"enabled": true,
"responseMessage": "Transferring you to a specialist.",
"action": [
{ "type": "action.asign", "value": "human-agent-id" },
{ "type": "action.tag", "value": "needs-human-support" }
]
}
]
}Available action types:
| Type | Description |
|---|---|
action.asign |
Assign conversation to a user |
action.tag |
Add a tag to the conversation |
action.stage |
Move conversation to a stage |
action.solved |
Mark conversation as resolved |
action.agentShutDown |
Deactivate AI agent for this conversation |
action.segmentation |
Add segmentation to the contact |
action.event.add |
Create a calendar event |
action.event.update |
Update a calendar event |
action.event.delete |
Delete a calendar event |
action.event.list |
List calendar events |
Use your own OpenAI, Claude, or Gemini API key:
const agent = await plazbot.agent.addAgent({
name: "Custom AI Agent",
prompt: "You are a helpful assistant.",
zone: "LA",
buffer: 5,
useToolCalling: true,
customAIConfig: true,
aiProviders: [
{
provider: "openai", // "openai" | "claude" | "gemini"
model: "gpt-4o",
apiToken: "sk-your-key",
temperature: 0.7,
maxTokens: 1000,
isDefault: true
}
]
});Portals are AI-powered search pages with a ChatGPT-like experience.
A portal must have at least one associated agent to work.
const portal = await plazbot.portal.addPortal({
name: "Company Portal",
zone: "LA",
title: "AI Search Portal",
subtitle: "Ask anything about our products",
logo: "https://example.com/logo.png",
logodark: "https://example.com/logo-dark.png",
access: "direct",
theme: "light",
disabled: false,
brandOff: false,
});
console.log(portal.url); // Public URL| Field | Description |
|---|---|
name |
Portal name |
title |
Heading shown at the top |
subtitle |
Description text |
logo / logodark |
Logo URLs (PNG/JPG) |
access |
"direct" or "form" |
theme |
"light" or "dark" |
disabled |
Activate/deactivate |
brandOff |
Hide Plazbot branding |
// Link agent to portal
await plazbot.portal.addAgentToPortal({ portalId, id: agentId });
// Remove agent from portal
await plazbot.portal.removeAgentFromPortal({ portalId, id: agentId });
// Add links (max 5)
await plazbot.portal.addLinkToPortal({
portalId,
value: "Documentation",
url: "https://docs.plazbot.com"
});
// Clear all links
await plazbot.portal.clearLinks(portalId);
// Get portal info
const info = await plazbot.portal.getPortal(portalId);
// Update portal
await plazbot.portal.updatePortal({ id: portalId, name: "New Name", theme: "dark" });
// Delete portal
await plazbot.portal.deletePortal(portalId);Before using WhatsApp features, connect your number in the Plazbot Dashboard.
Guide: WhatsApp Configuration
// Simple message (requires active 24h conversation window)
await plazbot.message.onWhatsappMessage({
message: "Hello! Thanks for contacting us.",
to: "51912345678"
});
// Template message (works anytime)
await plazbot.message.onConversation({
to: "51912345678",
template: "welcome_plazbot"
});
// Template with variables
await plazbot.message.onConversation({
to: "51912345678",
template: "order_confirmation",
variablesBody: [
{ variable: "1", value: "John" },
{ variable: "2", value: "ORD-12345" }
]
});
// Template with file attachment
await plazbot.message.onConversation({
to: "51912345678",
template: "invoice_template",
file: {
fileUrl: "https://example.com/invoice.pdf",
fileName: "invoice.pdf"
}
});Receive incoming WhatsApp messages on your endpoint:
// Register
await plazbot.message.registerWebhook({
number: "51912345678",
webhookUrl: "https://your-server.com/webhooks/whatsapp"
});
// Remove
await plazbot.message.deleteWebhook({ number: "51912345678" });const messages = await plazbot.message.getMessages({ limit: 10 });
const history = await plazbot.message.getConversationHistory({ contactId: "id" });
const results = await plazbot.message.searchMessages({ query: "pricing" });Manage WhatsApp message templates:
// List all templates
const templates = await plazbot.template.getTemplates();
// List active templates only
const active = await plazbot.template.getActiveTemplates();
// Get template details
const template = await plazbot.template.getTemplate("template-id");Manage your contact database:
// Create
const contact = await plazbot.contact.createContact({
name: "John Doe",
cellphone: "51912345678",
email: "john@example.com",
tags: ["lead", "website"]
});
// Read
const contacts = await plazbot.contact.getContacts();
const found = await plazbot.contact.getContact("contact-id");
// Search
const byPhone = await plazbot.contact.searchByPhone("51912345678");
const byEmail = await plazbot.contact.searchByEmail("john@example.com");
// Update
await plazbot.contact.updateContact({
id: contact.id,
name: "John Doe Updated",
tags: ["lead", "premium"]
});
// Delete
await plazbot.contact.deleteContacts(["contact-id-1", "contact-id-2"]);| Code | Language |
|---|---|
es |
Spanish (General) |
es-419 |
Spanish (Latin America) |
es-ES |
Spanish (Spain) |
en |
English (General) |
en-US |
English (United States) |
en-GB |
English (United Kingdom) |
fr |
French |
pt-BR |
Portuguese (Brazil) |
de |
German |
The SDK exports all types for full IntelliSense support:
import type {
AgentConfig,
AgentData,
AgentResponse,
PortalConfig,
PortalData,
SendTemplateParams,
TemplateVariable,
WhatsAppTemplate,
ContactData,
CreateContactParams,
PlazbotOptions
} from 'plazbot';Join us to get updates, early access, and support:
Support: support@plazbot.com Sales & Partnerships: sales@plazbot.com
MIT






