@kerabie/sdk 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,344 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # @kerabie/sdk
2
+
3
+ Official TypeScript SDK for the [Kerabie](https://kerabie.com) customer support platform.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @kerabie/sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { KerAbieClient } from '@kerabie/sdk';
15
+
16
+ const kerabie = new KerAbieClient({
17
+ apiKey: process.env.KERABIE_API_KEY,
18
+ orgId: 'your-org-id', // Optional; set per request or in client
19
+ baseUrl: 'https://api.kerabie.com', // Default
20
+ });
21
+ ```
22
+
23
+ ## Conversations
24
+
25
+ ```typescript
26
+ // List conversations
27
+ const { data, meta } = await kerabie.conversations.list({ status: 'OPEN', limit: 20 });
28
+
29
+ // Get a single conversation
30
+ const conv = await kerabie.conversations.get(123);
31
+
32
+ // Send a message
33
+ const msg = await kerabie.conversations.sendMessage(123, { body: 'Hello!' });
34
+
35
+ // Resolve / assign
36
+ await kerabie.conversations.resolve(123);
37
+ await kerabie.conversations.assign(123, agentId);
38
+ ```
39
+
40
+ ## Customers
41
+
42
+ ```typescript
43
+ // Upsert a customer (create or update)
44
+ const customer = await kerabie.customers.upsert({
45
+ email: 'user@example.com',
46
+ name: 'John Doe',
47
+ externalRef: 'app_user_id_123',
48
+ customAttributes: { plan: 'pro' },
49
+ });
50
+
51
+ // Get conversation history
52
+ const { data: convs } = await kerabie.customers.getConversations(customer.id);
53
+ ```
54
+
55
+ ## Campaigns
56
+
57
+ ```typescript
58
+ // Proactive messages on your chat widget
59
+ const { campaigns, meta } = await kerabie.campaigns.list({ status: 'active' });
60
+
61
+ // Saved as a draft; add isActive: true to launch it straight away
62
+ const { campaign } = await kerabie.campaigns.create({
63
+ name: 'Pricing page nudge',
64
+ message: 'Questions about plans? I can compare them for you in two minutes.',
65
+ ctaLabel: 'Compare plans',
66
+ trigger: 'time_on_page',
67
+ triggerValue: 20, // seconds
68
+ pages: '/pricing',
69
+ audience: 'all',
70
+ frequency: 'once_per_visitor',
71
+ });
72
+
73
+ await kerabie.campaigns.update(campaign.id, { isActive: true }); // launch (403 if your plan's live limit is reached)
74
+ await kerabie.campaigns.duplicate(campaign.id); // copy as a draft
75
+ const stats = await kerabie.campaigns.getStats(); // { total, active, sent, opened, clicked, replied, limit, … }
76
+ await kerabie.campaigns.delete(campaign.id);
77
+ ```
78
+
79
+ `trigger` is `page_load`, `time_on_page` (seconds), `scroll_depth` (percent) or `exit_intent`. `pages` takes comma-separated URL patterns with `*` wildcards; leave it empty for every page. How many campaigns can be live at once depends on your plan (`getStats().limit`, 0 = unlimited).
80
+
81
+ ## AI tools
82
+
83
+ ```typescript
84
+ // Custom API actions your chatbot can call (check stock, validate an address, book a slot…)
85
+ const { tools, stats } = await kerabie.aiTools.list(); // stats.limit: -1 = not on your plan, 0 = unlimited
86
+
87
+ const { tool } = await kerabie.aiTools.create({
88
+ name: 'check_availability', // snake_case; this is what the AI calls
89
+ displayName: 'Check availability',
90
+ description: 'Use this when a customer asks if a specific item is in stock.',
91
+ endpoint: 'https://api.yourstore.com/inventory/check', // must be a public address
92
+ method: 'GET',
93
+ authType: 'bearer',
94
+ authConfig: { token: process.env.STORE_API_TOKEN }, // write-only, never returned
95
+ responsePath: 'data.message',
96
+ parameters: [
97
+ { name: 'sku', type: 'string', description: 'Product SKU the customer mentioned' },
98
+ { name: 'location', type: 'string', description: 'Store, if mentioned', required: false },
99
+ ],
100
+ });
101
+
102
+ const result = await kerabie.aiTools.test(tool.id, { sku: 'A-100' }); // calls your real endpoint once
103
+ await kerabie.aiTools.update(tool.id, { enabled: false }); // switch it off
104
+ await kerabie.aiTools.duplicate(tool.id); // a copy that starts switched off
105
+ await kerabie.aiTools.delete(tool.id);
106
+ ```
107
+
108
+ How many tools you can have depends on your plan; `create` and `duplicate` throw a 403 when it is full. Endpoints that resolve to a private or local address are rejected, and redirects are not followed.
109
+
110
+
111
+ ### MCP servers as tool sources
112
+
113
+ ```typescript
114
+ // Let your chatbot use the tools of a remote MCP server
115
+ const { server } = await kerabie.mcpServers.create({
116
+ name: 'Shop tools',
117
+ description: 'Order and stock lookups for our shop. Use when a customer asks about an order or an item.',
118
+ url: 'https://mcp.yourstore.com/mcp',
119
+ authType: 'bearer',
120
+ authConfig: { token: process.env.SHOP_MCP_TOKEN },
121
+ });
122
+ console.log(server.tools.map((t) => t.name)); // what the server offers
123
+
124
+ await kerabie.mcpServers.update(server.id, { enabledTools: ['check_stock'] }); // switch tools on
125
+ await kerabie.mcpServers.test(server.id, 'check_stock', { sku: 'A-100' });
126
+ await kerabie.mcpServers.sync(server.id); // read the tools again
127
+ await kerabie.mcpServers.delete(server.id);
128
+ ```
129
+
130
+ Each switched-on MCP tool counts towards your plan's AI tool limit, the same as your own tools.
131
+
132
+ ## Contacts
133
+
134
+ ```typescript
135
+ // People you have talked to (or added), last-contacted first
136
+ const { contacts, meta } = await kerabie.contacts.list({ type: 'VIP', search: 'palmex', limit: 20 });
137
+
138
+ const stats = await kerabie.contacts.getStats(); // { total, newThisWeek, leads, vip }
139
+
140
+ // Add one by hand — throws an HttpError with status 409 if the email is already saved
141
+ const { contact } = await kerabie.contacts.create({ name: 'Chidi Okafor', email: 'chidi@palmex.com', company: 'Palmex', contactType: 'CUSTOMER' });
142
+
143
+ await kerabie.contacts.update(contact.id, { contactType: 'VIP', notes: 'Prefers WhatsApp', ownerId: 7 });
144
+
145
+ // Bulk import (max 1,000 per call). Existing emails are skipped, bad rows are reported by number.
146
+ const result = await kerabie.contacts.import([{ name: 'Sam Adeyemi', email: 'sam@tenda.ng', type: 'lead' }]);
147
+ result; // { created, duplicates, invalid: [{ row, error }], total }
148
+ ```
149
+
150
+ `type` is `LEAD`, `CUSTOMER` or `VIP`. `ownerId` is a team-member id and must belong to your organisation. Emails are stored lower-cased and must be unique per organisation.
151
+
152
+ ## Visitors
153
+
154
+ ```typescript
155
+ // Who has been on the site recently (online and offline), newest first
156
+ const { data, meta } = await kerabie.visitors.list({ status: 'all', country: 'NG', limit: 20 });
157
+ data[0].visitCount; // 14 → coming back after 30+ minutes away counts as a new visit
158
+ data[0].status; // 'online' | 'offline' | 'blocked' | 'allowed'
159
+
160
+ // On the site right now
161
+ const { data: online } = await kerabie.visitors.getActive();
162
+
163
+ // Online now, countries and returning visitors (24h), blocked IPs, top countries
164
+ const overview = await kerabie.visitors.getOverview();
165
+
166
+ // CSV of the visitors matching the filters (max 5,000 rows) — Pro plan and above
167
+ const { csv, filename, count, truncated } = await kerabie.visitors.export({ status: 'blocked' });
168
+ ```
169
+
170
+ `status` comes from your IP rules and wins over presence: an allowlisted IP reads `allowed` even while the visitor is online. `ip` is `null` when your organisation hides visitor IPs in its privacy settings. Visitor history is kept for 30 days. `visitors.export()` needs a plan with data exports; on other plans it throws an `HttpError` with status `403`.
171
+
172
+ ## Knowledge Base
173
+
174
+ ```typescript
175
+ const { data: articles } = await kerabie.kb.search('refund policy');
176
+
177
+ const stats = await kerabie.kb.getStats(); // { total, published, drafts, views, top, categories }
178
+ const insights = await kerabie.kb.getInsights(); // { mostViewed, gaps, lowestRated, stale }: what to write or fix next
179
+
180
+ const article = await kerabie.kb.createArticle({
181
+ title: 'How to reset your password',
182
+ body: '...markdown content...',
183
+ category: 'account',
184
+ tags: ['password', 'security'],
185
+ });
186
+ ```
187
+
188
+ ## Tickets
189
+
190
+ Tickets are async, form-submitted support requests — the same underlying conversation model as live chat, just created through a dedicated flow instead of a real-time widget session. Use `externalRef` to identify your own end users; repeated calls with the same `externalRef` land on the same open ticket instead of creating duplicates.
191
+
192
+ ```typescript
193
+ // Create a ticket on behalf of one of your users
194
+ const { ticketId } = await kerabie.tickets.create({
195
+ externalRef: 'user_42', // your own stable user id
196
+ subject: 'Refund request',
197
+ message: 'I was charged twice for order #1029.',
198
+ priority: 'HIGH', // LOW | NORMAL | HIGH | URGENT
199
+ name: 'Jane Doe',
200
+ email: 'jane@example.com', // optional — enables email continuation: replies to the notification land back on this ticket
201
+ });
202
+
203
+ // List / filter
204
+ const { data: tickets } = await kerabie.tickets.list({ status: 'WAITING', priority: 'HIGH' });
205
+
206
+ // Fetch a ticket with its full message thread
207
+ const ticket = await kerabie.tickets.get(ticketId);
208
+
209
+ // Reply as the end user (e.g. relaying a message from your own app)
210
+ await kerabie.tickets.reply(ticketId, 'Any update on this?');
211
+
212
+ // Update status / priority, or use the shortcuts
213
+ await kerabie.tickets.update(ticketId, { priority: 'URGENT' });
214
+ await kerabie.tickets.resolve(ticketId);
215
+ await kerabie.tickets.close(ticketId);
216
+
217
+ // Overview numbers: open, unassigned, breaching SLA, and 7-day average resolution time
218
+ const stats = await kerabie.tickets.getStats();
219
+ stats.resolutionDeltaPct; // -18 → 18% faster than the 7 days before (null if nothing to compare)
220
+
221
+ // CSV export of the tickets matching the filters (max 5,000 rows)
222
+ const { csv, filename, count, truncated } = await kerabie.tickets.export({ status: 'ACTIVE', priority: 'URGENT' });
223
+ ```
224
+
225
+ `tickets.export()` needs a plan with data exports (Pro and above); on other plans it throws an `HttpError` with status `403`. Every cell is escaped, and values that start with `=`, `+`, `-` or `@` are prefixed with a single quote so spreadsheets show them as text instead of running them as formulas.
226
+
227
+ Tickets are subject to the same monthly conversation limit as live chat on your plan (a ticket **is** a conversation) — there's no separate ticket-specific cap or plan gate on the API itself.
228
+
229
+ ## Webhooks
230
+
231
+ ```typescript
232
+ // Register a webhook
233
+ const webhook = await kerabie.webhooks.create({
234
+ url: 'https://your-server.com/hooks/kerabie',
235
+ events: ['message.sent.customer', 'conversation.created'],
236
+ secret: 'your-signing-secret',
237
+ });
238
+
239
+ // Verify incoming webhooks (Express example)
240
+ import { WebhooksResource } from '@kerabie/sdk';
241
+ import express from 'express';
242
+
243
+ const app = express();
244
+ app.use(express.raw({ type: 'application/json' }));
245
+
246
+ app.post('/hooks/kerabie', (req, res) => {
247
+ const sig = req.headers['x-kerabie-signature'] as string;
248
+ const rawBody = req.body.toString();
249
+
250
+ if (!WebhooksResource.verify(rawBody, sig, process.env.KERABIE_WEBHOOK_SECRET!)) {
251
+ return res.status(401).end();
252
+ }
253
+
254
+ const payload = WebhooksResource.parse(rawBody);
255
+ console.log('Event:', payload.event, payload.data);
256
+ res.status(200).end();
257
+ });
258
+ ```
259
+
260
+ ## Analytics
261
+
262
+ The same numbers as your Kerabie dashboard (cached for up to 5 minutes; tickets and setup for 1 minute).
263
+
264
+ ```typescript
265
+ // Conversation, response-time, satisfaction and AI numbers (days: default 30, max 365)
266
+ const summary = await kerabie.analytics.getSummary({ days: 7 });
267
+ summary.totals.conversations; // 186
268
+ summary.deltas.conversations; // 12 → +12% vs the previous 7 days (null if nothing to compare)
269
+ summary.ai.resolvedAlonePct; // 70 → % the AI handled without a human
270
+ summary.ai.agentHoursSaved; // 41 → an estimate
271
+ summary.trend; // [{ date, count, ai, human }, …] oldest first
272
+
273
+ // Team
274
+ const { agents } = await kerabie.analytics.getAgentStats({ period: '7d' });
275
+
276
+ // The deeper numbers behind the dashboard's Analytics page
277
+ const insights = await kerabie.analytics.getInsights({ days: 30 });
278
+ insights.firstResponse.medianSeconds; // 144
279
+ insights.ai.questions[0]; // { question, asked, aiResolvedPct, avgRating, page, trendPct }
280
+ insights.customers.heatmap.cells; // 7 weekdays × 12 two-hour blocks of customer messages
281
+ insights.pages[0]; // { page, views, questions, askRate, topQuestion }
282
+
283
+ // Open tickets by priority and the % still within SLA
284
+ const sla = await kerabie.analytics.getTicketSla();
285
+ sla.withinSlaPct; // 94
286
+ sla.byPriority; // [{ priority: 'URGENT', count: 3, oldestMinutes: 14 }, …]
287
+
288
+ // First-run checklist: connect a channel, train the AI, create an automation
289
+ const setup = await kerabie.analytics.getSetupStatus(); // { channel, ai, automation }
290
+ ```
291
+
292
+ `deltas.responseTime` is a percentage where a **negative** number is an improvement. `avgResponseTime` is in seconds.
293
+
294
+ ## AI take-over and customer history
295
+
296
+ ```typescript
297
+ // List with inbox tab counts, tags and an `aiHandling` flag
298
+ const { data, meta } = await kerabie.conversations.list({ status: 'WAITING' });
299
+ meta.counts; // { all, WAITING, ACTIVE, RESOLVED, CLOSED } — ignores filters
300
+ data.filter((c) => c.aiHandling); // conversations the AI is handling alone
301
+
302
+ // A customer's earlier conversations (newest first, default 5, max 20)
303
+ const { data: earlier } = await kerabie.conversations.getHistory(101, 5);
304
+
305
+ // Stop the AI and hand the conversation to a team member
306
+ await kerabie.conversations.takeOver(101, { agentId: 7 });
307
+ ```
308
+
309
+ `takeOver` throws an `HttpError` with status `400` if the AI is no longer handling the conversation (for example someone already took over), so it is safe to call from several places at once.
310
+
311
+ ## Error handling
312
+
313
+ ```typescript
314
+ import { HttpError } from '@kerabie/sdk';
315
+
316
+ try {
317
+ const conv = await kerabie.conversations.get(9999);
318
+ } catch (err) {
319
+ if (err instanceof HttpError) {
320
+ console.log(err.status); // 404
321
+ console.log(err.message); // "Conversation not found"
322
+ }
323
+ }
324
+ ```
325
+
326
+ ## TypeScript
327
+
328
+ All types are exported:
329
+
330
+ ```typescript
331
+ import type { Conversation, Message, Customer, WebhookPayload } from '@kerabie/sdk';
332
+ ```
333
+
334
+ ## License
335
+
336
+ MIT
337
+
338
+ ## Building with an AI assistant
339
+
340
+ The [`@kerabie/mcp`](https://kerabie.com/docs/sdks/mcp) server gives Claude, Cursor and other MCP clients the API reference, this SDK's types and integration guides, so generated code matches the real API. It needs no API key to help you build, and with one it can work with your account:
341
+
342
+ ```json
343
+ { "mcpServers": { "kerabie": { "command": "npx", "args": ["-y", "@kerabie/mcp"], "env": { "KERABIE_API_KEY": "ker_live_..." } } } }
344
+ ```