orqo-node-sdk
v1.2.0
Published
Orqo Gateway — TypeScript SDK for plug-and-play WhatsApp integration
Maintainers
Readme
✨ Features
| Feature | Description |
|---------|-------------|
| 🚀 Plug-and-Play | Provision → Connect → Send in 5 lines of code |
| 🔒 Type-Safe | Full TypeScript coverage with strict mode — every method, every event |
| 📦 Zero Dependencies | Only globalThis.fetch — works in Node.js 18+, Bun, Deno, and Edge |
| 🔁 Retry & Resilience | Exponential backoff with configurable retry on 429/5xx |
| 🪝 Webhook Handler | HMAC-verified, typed event dispatch for Express/Hono/Fastify/Node |
| 🤖 Agent Management | Full CRUD for AI agents with Mastra engine support |
| 🔌 API Bridge | Connect any OpenAPI spec and the agent gains tools automatically |
| ⏰ Cron Jobs | Schedule recurring actions per instance |
| 🤝 Handoff | Bot ↔ Human session transfer with metadata |
| 🧠 Mastra Engine | Native or Mastra-powered agents with working memory, sub-agents, and evals |
| 📡 Event Emitter | Type-safe OrqoEventEmitter with on/once/off for all webhook events |
| ⚡ DX Helpers | askAgent(), collectStream(), quickStart(), toJid() utilities |
📦 Installation
npm install orqo-node-sdkRequirements: Node.js 18+ (or any runtime with
globalThis.fetch)
🚀 Quick Start
1. Provision an Instance
import { OrqoClient } from 'orqo-node-sdk';
const orqo = new OrqoClient({
host: 'https://gateway.orqo.ai',
adminToken: process.env.ORQO_ADMIN_TOKEN!,
});
const { instance, result } = await orqo.provision({
name: 'My Support Bot',
webhookUrl: 'https://my-app.com/webhooks/orqo',
webhookSecret: process.env.WEBHOOK_SECRET,
});
console.log(`✅ Instance ${result.instanceId} created`);2. Connect WhatsApp
// Wait for QR scan with live feedback
await instance.waitForConnection({
onQr: (qr) => console.log('📱 Scan this QR:', qr),
timeoutMs: 120_000,
});
console.log('🟢 WhatsApp connected!');3. Send a Message
await instance.sendMessage({
to: '[email protected]',
text: 'Hello from Orqo! 🚀',
});4. Receive Events
import { createWebhookHandler } from 'orqo-node-sdk';
app.post('/webhooks/orqo', createWebhookHandler({
secret: process.env.WEBHOOK_SECRET,
onMessageReceived: async (event) => {
console.log(`💬 ${event.from}: ${event.text}`);
},
onConnectionChanged: (event) => {
console.log(`📡 ${event.state}`);
},
}));5. Advanced: Skills, MCP, and OpenAPI APIs
You can extend agents dynamically by connecting external APIs and installing remote agent skills (via .skill or .zip files).
// 1. Provision with external MCP servers
const { instance } = await orqo.provision({
name: 'Advanced Bot',
config: {
mcpServers: {
"salesforce-mcp": {
url: "https://my-salesforce-mcp.server.com/sse",
env: { "SF_TOKEN": process.env.SF_TOKEN }
}
}
}
});
// 2. Install a pre-packaged Agent Skill (prompts, logic, and MCP tools)
await instance.installSkill({
remoteUrl: 'https://github.com/my-org/support-skill/archive/refs/tags/v1.zip'
});
// 3. Import any standard REST API as tools using an OpenAPI/Swagger document
await instance.connectApi({
name: "ecommerce-backend",
schemaUrl: "https://api.my-store.com/swagger.json",
headers: { "Authorization": "Bearer token123" }
});
console.log("🔥 Agent successfully supercharged with MCP, Skills, and REST APIs!");📖 API Reference
OrqoClient — Admin Operations
Requires
adminToken. Used for instance lifecycle management.
const orqo = new OrqoClient({ host, adminToken });| Method | Returns | Description |
|--------|---------|-------------|
| provision(options) | { instance, result } | Create instance + boot WhatsApp |
| listInstances() | InstanceStatus[] | List all instances |
| getInstance(id, token) | OrqoInstance | Get handle for existing instance |
| createInstance(options) | Record | Create instance (non-provision) |
| deleteInstance(id) | void | Delete an instance |
| startInstance(id) | void | Start an instance container |
| stopInstance(id) | void | Stop an instance container |
| createInstanceBackup(id)| { backupUrl, size }| Create an instance DB backup |
| listAdminAgents(id) | Agent[] | List agents globally via Admin |
| patchAdminAgent(id, agentId)| Agent | Patch agent via Admin |
| deleteAdminAgent(id, agentId)| void | Delete agent via Admin |
| reseedAdminAgents(id)| void | Reseed/Reset all agents knowledge |
| getGlobalStats() | GlobalStats | Retrieve platform global metrics |
| getAuditLogs(opts?) | AuditLogEntry[]| Retrieve global audit logs |
| getInstanceAuditLogs(id, opts?) | AuditLogEntry[] | Per-instance audit logs |
| cleanupAuditLogs(opts?) | { deleted } | Delete old audit entries |
OrqoInstance — Instance Operations
Scoped to a single instance. All methods auto-retry on 429/5xx.
| Method | Returns | Description |
|--------|---------|-------------|
| getWhatsAppStatus() | WhatsAppStatus | Connection state + QR code |
| connectWhatsApp() | InstanceStatus | Start WhatsApp socket |
| disconnectWhatsApp() | void | Disconnect WhatsApp |
| reconnectWhatsApp() | InstanceStatus | Force reconnect (stale connections) |
| pairByPhone(phone) | { code } | Phone-number pairing code |
| waitForConnection(opts?) | WhatsAppStatus | Poll until connected (with QR callback) |
Messaging
| Method | Returns | Description |
|--------|---------|-------------|
| send(phone, text, opts?) | SendMessageResult | Send a text message (JID auto-formatted) |
| sendMessage({ to, text }) | SendMessageResult | Send a text message (RAW JID required) |
| getMessages(opts?) | Record[] | Retrieve messages (with sessionKey filter) |
| listSessions(opts?) | PaginatedResult<SessionConversation> | Paginated session list with search/channel filter |
| getSessions(opts?) | SessionInfo[] | (deprecated) Simple session list |
Agents
| Method | Returns | Description |
|--------|---------|-------------|
| streamAgent(id, opts) | AsyncGenerator | Stream agent response (Server-Sent Events) |
| createAgent(config) | Agent | Create an AI agent |
| listAgents() | Agent[] | List all agents |
| getAgent(id) | Agent | Get agent by ID |
| updateAgent(id, updates) | Agent | Patch agent config |
| deleteAgent(id) | void | Remove an agent |
Skills Management
| Method | Returns | Description |
|--------|---------|-------------|
| listAvailableSkills() | Skill[] | List all bundled skills available to install |
| installSkill(name) | InstallResult | Install a bundled skill by name directly |
| installSkill({ remoteUrl }) | InstallResult | Download & install a remote .skill or .zip |
Webhooks
| Method | Returns | Description |
|--------|---------|-------------|
| addWebhook({ url, event, secret? }) | Webhook | Register a webhook |
| listWebhooks() | Webhook[] | List registered webhooks |
| removeWebhook(id) | void | Unregister a webhook |
Guardrails
| Method | Returns | Description |
|--------|---------|-------------|
| setGuardrails(config) | GuardrailConfig | Set AI response rules |
| getGuardrails() | GuardrailConfig | Get current guardrail config |
Session Handoff
| Method | Returns | Description |
|--------|---------|-------------|
| handoffToHuman(options) | HandoffResult | Transfer session to human agent |
| resumeBot(sessionId) | HandoffResult | Return session to bot control |
Cron Jobs
| Method | Returns | Description |
|--------|---------|-------------|
| listCronJobs() | Record[] | List scheduled jobs |
| createCronJob(config) | Record | Schedule a recurring job |
| deleteCronJob(id) | void | Remove a scheduled job |
| runCronJob(id) | Record | Trigger a job manually |
API Bridge
| Method | Returns | Description |
|--------|---------|-------------|
| connectApi(options) | { bridge, tools } | Connect an OpenAPI spec |
| listApiBridges() | Array | List connected bridges |
| removeApiBridge(id) | { success } | Disconnect a bridge |
| refreshApiBridge(id) | { bridge, tools } | Re-fetch spec & update tools |
Monitoring
| Method | Returns | Description |
|--------|---------|-------------|
| getStatus() | InstanceStatus | Instance health & metadata |
| getStats() | Record | Messages, sessions, uptime |
🪝 Webhook Handler
The createWebhookHandler() function provides a framework-agnostic webhook receiver with HMAC signature verification and typed event dispatch.
Express
import express from 'express';
import { createWebhookHandler } from 'orqo-node-sdk';
const app = express();
app.use(express.json());
app.post('/webhooks/orqo', createWebhookHandler({
secret: process.env.WEBHOOK_SECRET,
onConnectionChanged: (e) => {
console.log(`${e.instanceId}: ${e.state}`);
},
onMessageReceived: async (e) => {
console.log(`From ${e.from}: ${e.text}`);
// Process message...
},
onMessageProcessed: (e) => {
console.log(`AI processed: ${e.messageId}`);
},
onHandoff: (e) => {
console.log(`Handoff: session ${e.sessionId}`);
},
onEvent: (e) => {
// Catch-all for any event type
},
onVerificationFailed: (err) => {
console.error('⚠️ Invalid signature:', err.message);
},
}));Hono
import { Hono } from 'hono';
import { createWebhookHandler } from 'orqo-node-sdk';
const app = new Hono();
app.post('/webhooks/orqo', async (c) => {
const handler = createWebhookHandler({
secret: c.env.WEBHOOK_SECRET,
onMessageReceived: async (e) => {
console.log(`${e.from}: ${e.text}`);
},
});
await handler(
{ body: await c.req.json(), headers: c.req.raw.headers },
{ status: (s) => ({ json: (b) => c.json(b, s) }) },
);
return c.body(null, 200);
});Event Types
| Event | Interface | Trigger |
|-------|-----------|---------|
| connection:changed | ConnectionChangedEvent | WhatsApp connects/disconnects |
| message:received | MessageReceivedEvent | New incoming message |
| message:processed | MessageProcessedEvent | AI finishes processing |
| session:handoff | HandoffEvent | Session transferred to human |
| agent:step_finish | StepFinishEvent | Mastra agent step completes |
Security
- HMAC-SHA256 signature verification (
X-Webhook-Signatureheader) - Dual runtime: Web Crypto API (browser/edge) + Node.js
cryptofallback - Timing-safe string comparison to prevent timing attacks
🔌 API Bridge
Connect any external REST API via its OpenAPI spec. The AI agent automatically gains typed tools from the spec endpoints.
// Connect your CRM API — agent instantly gets tools like
// "crm_getContact", "crm_createLead", "crm_updateDeal"
await instance.connectApi({
name: 'salesforce',
specUrl: 'https://api.salesforce.com/openapi.json',
auth: { type: 'bearer', token: process.env.SF_TOKEN },
description: 'CRM for sales pipeline management',
});
// Connect a payments API
await instance.connectApi({
name: 'stripe',
specUrl: 'https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json',
auth: { type: 'bearer', token: process.env.STRIPE_KEY },
});
// List what's connected
const bridges = await instance.listApiBridges();
console.log(bridges.map(b => `${b.name}: ${b.tools.length} tools`));Auth Types
| Type | Fields | Example |
|------|--------|---------|
| bearer | token | { type: 'bearer', token: 'sk-...' } |
| apiKey | token, headerName | { type: 'apiKey', token: 'key', headerName: 'X-API-Key' } |
| basic | username, password | { type: 'basic', username: 'u', password: 'p' } |
| custom | headers | { type: 'custom', headers: { 'X-Custom': 'val' } } |
Security
The API Bridge includes built-in SSRF prevention:
- Blocks private/loopback/metadata IP addresses
- Validates URL protocols (http/https only)
- Limits spec size (2MB), operations (100), bridges per instance (10)
- Blocks redirect-based attacks
🛡️ Resilience
All HTTP methods include automatic retry with exponential backoff:
const orqo = new OrqoClient({
host: 'https://gateway.orqo.ai',
adminToken: 'token',
retry: {
maxAttempts: 5, // default: 3
backoffMs: 500, // default: 1000
multiplier: 2, // default: 2
retryOn: [429, 503], // default: [429, 500, 502, 503, 504]
},
});The retry config cascades from OrqoClient → OrqoInstance, so all instance methods inherit the same resilience settings.
Backoff Timeline (defaults)
Attempt 1 → immediate
Attempt 2 → wait 1000ms
Attempt 3 → wait 2000ms
Attempt 4 → wait 4000ms (if maxAttempts > 3)🤝 Complete Workflow Example
import { OrqoClient, createWebhookHandler } from 'orqo-node-sdk';
import express from 'express';
// ── 1. Setup ─────────────────────────────────────────────
const orqo = new OrqoClient({
host: process.env.ORQO_HOST!,
adminToken: process.env.ORQO_ADMIN_TOKEN!,
});
// ── 2. Provision ─────────────────────────────────────────
const { instance } = await orqo.provision({
name: 'Customer Support',
webhookUrl: 'https://my-app.com/webhooks/orqo',
webhookSecret: process.env.WEBHOOK_SECRET,
});
// ── 3. Configure Agent ───────────────────────────────────
await instance.createAgent({
name: 'Support Agent',
type: 'chat',
systemPrompt: `You are a helpful customer support agent.
Be concise and professional. If you cannot resolve
the issue, hand off to a human agent.`,
tools: ['knowledge-base', 'ticket-system'],
});
// ── 4. Set Guardrails ────────────────────────────────────
await instance.setGuardrails({
blockedTopics: ['competitor-pricing', 'internal-processes'],
maxResponseLength: 500,
filterPII: true,
});
// ── 5. Connect External APIs ─────────────────────────────
await instance.connectApi({
name: 'zendesk',
specUrl: 'https://api.zendesk.com/openapi.json',
auth: { type: 'bearer', token: process.env.ZENDESK_TOKEN },
});
// ── 6. Connect WhatsApp ──────────────────────────────────
await instance.waitForConnection({
onQr: (qr) => sendQrToAdmin(qr),
});
// ── 7. Receive Events ────────────────────────────────────
const app = express();
app.use(express.json());
app.post('/webhooks/orqo', createWebhookHandler({
secret: process.env.WEBHOOK_SECRET,
onMessageReceived: async (e) => {
console.log(`💬 ${e.pushName}: ${e.text}`);
},
onHandoff: async (e) => {
await notifyHumanAgent(e.sessionId, e.reason);
},
}));
// ── 8. Schedule Reminders ────────────────────────────────
await instance.createCronJob({
schedule: '0 9 * * 1', // Every Monday at 9am
action: 'send-weekly-summary',
payload: { channel: 'admin' },
});
app.listen(3000, () => console.log('🚀 Running on :3000'));🏗 Architecture
┌──────────────────────────────────────────────────────────────┐
│ orqo-node-sdk │
├──────────────┬─────────────────┬─────────────────────────────┤
│ OrqoClient │ OrqoInstance │ createWebhookHandler() │
│ (Admin) │ (Per-instance) │ (Event receiver) │
│ │ │ │
│ provision() │ 30 methods │ HMAC verification │
│ listInst() │ WhatsApp │ Typed event dispatch │
│ deleteInst()│ Agents │ Express/Hono/Fastify │
│ getInst() │ Webhooks │ Web Crypto + Node.js │
│ │ Guardrails │ │
│ │ Handoff │ │
│ │ API Bridge │ │
│ │ Cron Jobs │ │
│ │ Stats/Messages │ │
├──────────────┴─────────────────┴─────────────────────────────┤
│ Retry / Resilience Layer │
│ Exponential backoff • Configurable • Cascading │
├──────────────────────────────────────────────────────────────┤
│ globalThis.fetch (zero deps) │
│ Node.js 18+ • Bun • Deno • Cloudflare Workers │
└──────────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ Orqo Gateway │ │ Your HTTP Server │
│ REST API │ │ Webhook Events │
└──────────────┘ └──────────────────┘🧠 Mastra Engine
Create agents powered by Mastra with working memory, sub-agents, and structured output:
import { Engine } from 'orqo-node-sdk';
const agent = await instance
.agent('Restaurant Bot')
.engine(Engine.MASTRA)
.mastraModel('openai/gpt-4o')
.prompt('You manage orders for a restaurant.')
.tools('order_lookup', 'refund_tool')
.skills('investigador-br')
.subAgents('billing-specialist')
.workingMemory({ scope: 'contact' })
.jsonOutput({ type: 'object', properties: { action: { type: 'string' } } })
.maxToolRounds(5)
.identity('Ana', '👩💼')
.create();📡 Event Emitter
Type-safe event emitter for webhook events with on/once/off:
import { OrqoEventEmitter, createWebhookHandler } from 'orqo-node-sdk';
const emitter = new OrqoEventEmitter();
emitter
.on('message:received', (e) => console.log(`From ${e.from}: ${e.text}`))
.on('agent:step_finish', (e) => console.log(`Step: ${e.agentId}`))
.on('*', (e) => metrics.track(e.event));
// Wire into your webhook handler
app.post('/webhooks', createWebhookHandler({
secret: process.env.WEBHOOK_SECRET,
onEvent: (e) => emitter.emit(e),
}));⚡ DX Helpers
import { askAgent, collectStream, quickStart, toJid, quickSend } from 'orqo-node-sdk';
// One-shot agent question
const reply = await askAgent(instance, 'agent-1', 'What time do you open?', 'session-key');
// Collect full stream into text + tool calls
const { text, toolCalls, usage } = await collectStream(
instance.streamAgent('agent-1', { text: 'Hello', sessionKey: 'sess-1' })
);
// Full setup in one call: provision → connect → wait for QR
const instance = await quickStart(orqo, 'My Bot', {
provider: LLMProvider.OPENAI, model: 'gpt-4o-mini', apiKey: 'sk-...',
});
// Format phone to JID
const jid = toJid('+55 11 99988-7766'); // '[email protected]'📋 Type Exports
All types are exported from the main entry point:
import type {
// Config
OrqoClientConfig, OrqoInstanceConfig, RetryConfig,
// Provisioning
ProvisionOptions, ProvisionResult,
// WhatsApp
WhatsAppStatus, SendMessageOptions, SendMessageResult,
// Instance
InstanceStatus, InstanceStats, InstanceLimits,
// Agents
AgentConfig, Agent, AgentStreamOptions, AgentStreamEvent,
// Webhooks
WebhookConfig, Webhook, WebhookHandlerOptions,
// Events
WebhookEvent, ConnectionChangedEvent, MessageReceivedEvent,
MessageProcessedEvent, HandoffEvent, StepFinishEvent, OrqoWebhookEvent,
// v1.0 Types
PaginatedResult, PromptSection, SessionConversation,
DetailedStats, GlobalStats, AuditLogEntry,
} from 'orqo-node-sdk';
// Constants & classes
import {
OrqoClient, OrqoInstance, OrqoEventEmitter,
AgentBuilder, ProvisionBuilder,
Events, LLMProvider, AgentType, Engine,
askAgent, collectStream, quickStart, toJid, quickSend,
} from 'orqo-node-sdk';🔧 Runtime Compatibility
| Runtime | Version | Status | |---------|---------|--------| | Node.js | 18+ | ✅ Full support | | Bun | 1.0+ | ✅ Full support | | Deno | 1.28+ | ✅ Full support | | Cloudflare Workers | — | ✅ Full support | | Vercel Edge | — | ✅ Full support | | Browser | — | ⚠️ CORS-dependent |
🔄 Migration Guide (v0.x → v1.0)
| Change | v0.x | v1.0 |
|--------|------|------|
| Streaming | AgentStreamOptions.message | AgentStreamOptions.text |
| Sessions | getSessions() → SessionInfo[] | listSessions() → PaginatedResult<SessionConversation> |
| Builder | engine() accepted AgentTypeValue | engine() accepts EngineType |
| Stream events | No delta type | AgentStreamEvent.type includes 'delta' |
| Package version | 0.3.0 | 1.0.0 |
[!NOTE]
getSessions()is still available but deprecated. Migrate tolistSessions()for full pagination and filtering.
📄 License
MIT — Use it however you want.
