@cstar.help/js 0.4.0 → 0.5.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 +195 -87
- package/dist/index.cjs +856 -0
- package/dist/index.d.cts +1466 -1
- package/dist/index.d.ts +1466 -1
- package/dist/index.js +856 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Official TypeScript SDK for the [cStar](https://cstar.help) customer support pla
|
|
|
4
4
|
|
|
5
5
|
- **Zero dependencies** — works with Node.js 18+ native `fetch`
|
|
6
6
|
- **TypeScript-first** — full type definitions included
|
|
7
|
-
- **
|
|
7
|
+
- **Six entry points** — tree-shake what you don't need
|
|
8
8
|
- **ESM + CJS** — works everywhere
|
|
9
9
|
|
|
10
10
|
## Install
|
|
@@ -13,18 +13,56 @@ Official TypeScript SDK for the [cStar](https://cstar.help) customer support pla
|
|
|
13
13
|
npm install @cstar.help/js
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
---
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## Which SDK Do I Need?
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
cStar has one package with **six entry points**, each designed for a different context:
|
|
21
|
+
|
|
22
|
+
| Import | Use Case | Runs On | Auth Required | Key Type |
|
|
23
|
+
| -------------------------- | ----------------------------------------- | ----------------- | ------------- | -------------------- |
|
|
24
|
+
| `@cstar.help/js` | **Admin dashboard / backend integration** | Server only | Yes | Secret (`sk_*`) |
|
|
25
|
+
| `@cstar.help/js/chat` | Customer chat widget | Browser | Yes | Publishable (`pk_*`) |
|
|
26
|
+
| `@cstar.help/js/library` | Public knowledge base | Browser or server | No | None |
|
|
27
|
+
| `@cstar.help/js/community` | Public community forum | Browser or server | No | None |
|
|
28
|
+
| `@cstar.help/js/webhook` | Webhook signature verification | Server only | No | Signing secret |
|
|
29
|
+
| `@cstar.help/js/auth` | Authentication utilities | Server only | Yes | Secret (`sk_*`) |
|
|
30
|
+
|
|
31
|
+
### The Security Model
|
|
32
|
+
|
|
33
|
+
cStar uses **two key types** to control access — the same pattern as Stripe and Supabase:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Secret keys (sk_live_*, sk_test_*)
|
|
37
|
+
├── Full CRUD on all resources
|
|
38
|
+
├── Settings, billing, analytics, game system
|
|
39
|
+
├── Team member management, audit log
|
|
40
|
+
├── AI actions, automations, SLA rules
|
|
41
|
+
└── NEVER expose in client-side code
|
|
42
|
+
|
|
43
|
+
Publishable keys (pk_live_*, pk_test_*)
|
|
44
|
+
├── Read-only on public data (tickets, customers, articles)
|
|
45
|
+
├── Used by the chat widget for customer-facing features
|
|
46
|
+
├── Safe to include in browser bundles
|
|
47
|
+
└── Cannot write, delete, or access sensitive endpoints
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> **Rule of thumb**: If you're building a custom support dashboard for your team, use `CStarClient` with a secret key on your backend. If you're adding chat to your website, use `ChatClient` with a publishable key in the browser.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Server-Side: Admin API Client
|
|
55
|
+
|
|
56
|
+
> **⚠️ Server-side only.** `CStarClient` requires a secret key (`sk_*`) and must never be used in browser code. The SDK will warn you if it detects a browser environment.
|
|
57
|
+
|
|
58
|
+
Manage tickets, customers, articles, team members, settings, game system, and everything else.
|
|
21
59
|
|
|
22
60
|
```typescript
|
|
23
61
|
import { CStarClient } from '@cstar.help/js';
|
|
24
62
|
|
|
25
63
|
const cstar = new CStarClient({
|
|
26
|
-
apiKey:
|
|
27
|
-
teamId:
|
|
64
|
+
apiKey: process.env.CSTAR_SECRET_KEY, // sk_live_... or sk_test_...
|
|
65
|
+
teamId: process.env.CSTAR_TEAM_ID
|
|
28
66
|
});
|
|
29
67
|
|
|
30
68
|
// List open tickets
|
|
@@ -36,18 +74,131 @@ const { data: ticket } = await cstar.tickets.create({
|
|
|
36
74
|
customerId: 'cus_abc123'
|
|
37
75
|
});
|
|
38
76
|
|
|
39
|
-
// Get a customer with expanded fields
|
|
40
|
-
const { data: customer } = await cstar.customers.get('cus_abc123');
|
|
41
|
-
|
|
42
77
|
// Auto-paginate through all tickets
|
|
43
78
|
for await (const ticket of cstar.tickets.listAutoPaginating({ status: 'open' })) {
|
|
44
79
|
console.log(ticket.title);
|
|
45
80
|
}
|
|
46
81
|
```
|
|
47
82
|
|
|
48
|
-
###
|
|
83
|
+
### Full Resource Reference
|
|
84
|
+
|
|
85
|
+
**Core Resources** — Tickets, customers, and knowledge base.
|
|
86
|
+
|
|
87
|
+
| Resource | Methods |
|
|
88
|
+
| ------------------------ | ----------------------------------------------------------------------------------------------- |
|
|
89
|
+
| `cstar.tickets` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.listAutoPaginating()` |
|
|
90
|
+
| `cstar.tickets.messages` | `.list(ticketId)` `.create(ticketId, params)` |
|
|
91
|
+
| `cstar.customers` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.listAutoPaginating()` |
|
|
92
|
+
| `cstar.articles` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.listAutoPaginating()` |
|
|
93
|
+
| `cstar.categories` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` |
|
|
94
|
+
|
|
95
|
+
**Team & Configuration** — Members, settings, and customization.
|
|
96
|
+
|
|
97
|
+
| Resource | Methods |
|
|
98
|
+
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
99
|
+
| `cstar.members` | `.list()` `.get(id)` `.update(id, params)` `.del(id)` |
|
|
100
|
+
| `cstar.members.invites` | `.list()` `.create(params)` `.del(id)` |
|
|
101
|
+
| `cstar.settings` | `.get()` `.update(params)` `.getBusinessHours()` `.updateBusinessHours(params)` `.getWidget()` `.updateWidget(params)` |
|
|
102
|
+
| `cstar.settings.statuses` | `.list()` `.create(params)` `.update(key, params)` `.del(key)` `.reorder(keys)` |
|
|
103
|
+
| `cstar.settings.quickReplies` | `.list()` `.create(params)` `.update(id, params)` `.del(id)` `.reorder(ids)` |
|
|
104
|
+
| `cstar.customFields` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.reorder(ids)` |
|
|
105
|
+
| `cstar.notifications` | `.list()` `.unreadCount()` `.markRead(id)` `.markAllRead()` |
|
|
106
|
+
|
|
107
|
+
**Operations** — Analytics, bulk actions, and data management.
|
|
108
|
+
|
|
109
|
+
| Resource | Methods |
|
|
110
|
+
| ---------------------- | --------------------------------------------------------------------------------------- |
|
|
111
|
+
| `cstar.analytics` | `.overview(params?)` `.agents(params?)` `.csatStats(params?)` `.csatResponses(params?)` |
|
|
112
|
+
| `cstar.sla` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` |
|
|
113
|
+
| `cstar.bulk` | `.update(params)` `.del(params)` |
|
|
114
|
+
| `cstar.export` | `.create(params)` |
|
|
115
|
+
| `cstar.import` | `.create(params)` `.logs(params?)` `.getLog(id)` |
|
|
116
|
+
| `cstar.search` | `.query(params)` |
|
|
117
|
+
| `cstar.tags` | `.list()` `.create(name)` `.del(name)` |
|
|
118
|
+
| `cstar.activity` | `.list(params?)` |
|
|
119
|
+
| `cstar.auditLog` | `.list(params?)` |
|
|
120
|
+
| `cstar.customerGroups` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` |
|
|
121
|
+
|
|
122
|
+
**Gamification** — The full game system.
|
|
123
|
+
|
|
124
|
+
| Resource | Methods |
|
|
125
|
+
| ------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
126
|
+
| `cstar.game` | `.player(params?)` `.getPlayer(memberId)` `.leaderboard(params?)` `.myRank(params?)` |
|
|
127
|
+
| `cstar.game.boss` | `.current()` `.isActive()` `.spawn(params?)` `.damage(params)` `.defeats(params?)` `.getDefeat(id)` |
|
|
128
|
+
| `cstar.game.achievements` | `.list(params?)` `.unlocked(params)` `.get(id, params?)` |
|
|
129
|
+
| `cstar.game.quests` | `.today(params?)` `.history(params?)` |
|
|
130
|
+
| `cstar.game.puzzles` | `.today(params?)` `.complete(params)` `.streak(params)` `.leaderboard(params?)` |
|
|
131
|
+
| `cstar.game.oneUps` | `.list(params?)` `.send(params)` `.remaining(params)` |
|
|
132
|
+
| `cstar.game.highFives` | `.list(params?)` `.send(params)` `.canSend(teamId, params)` |
|
|
133
|
+
| `cstar.game.cosmetics` | `.list(params?)` `.getPreferences(params)` `.updatePreferences(params)` |
|
|
134
|
+
| `cstar.game.seasons` | `.list()` `.current()` `.stats(seasonId, params)` |
|
|
135
|
+
| `cstar.game.skills` | `.get(params)` `.allocate(params)` `.reset(params)` |
|
|
136
|
+
|
|
137
|
+
**Advanced** — AI, automations, views, billing, webhooks.
|
|
138
|
+
|
|
139
|
+
| Resource | Methods |
|
|
140
|
+
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
141
|
+
| `cstar.ai` | `.run(params)` `.autoTag(ticketId)` `.suggestReply(ticketId)` `.summarize(ticketId)` `.improve(text)` |
|
|
142
|
+
| `cstar.automations` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.toggle(id)` `.reorder(ids)` `.test(id, params)` `.executions(id)` `.allExecutions()` `.evaluate(params)` |
|
|
143
|
+
| `cstar.views` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.execute(id, params?)` |
|
|
144
|
+
| `cstar.billing` | `.subscription()` `.portal(params?)` |
|
|
145
|
+
| `cstar.webhooks` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.test(id)` |
|
|
146
|
+
| `cstar.community.posts` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` |
|
|
147
|
+
| `cstar.community.topics` | `.list()` `.get(id)` |
|
|
148
|
+
| `cstar.attachments` | `.list(params?)` `.get(id)` `.del(id)` |
|
|
149
|
+
|
|
150
|
+
### Usage Examples
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// Team analytics
|
|
154
|
+
const { data: overview } = await cstar.analytics.overview({ period: 'week' });
|
|
155
|
+
console.log(`${overview.tickets.open} open tickets this week`);
|
|
156
|
+
|
|
157
|
+
// SLA rules
|
|
158
|
+
const { data: rules } = await cstar.sla.list();
|
|
159
|
+
|
|
160
|
+
// Boss battle
|
|
161
|
+
const { data: boss } = await cstar.game.boss.current();
|
|
162
|
+
if (boss.active) {
|
|
163
|
+
console.log(`${boss.name} — ${boss.health}/${boss.maxHealth} HP`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// AI-powered reply suggestion
|
|
167
|
+
const { data: suggestion } = await cstar.ai.suggestReply('tkt_abc123');
|
|
168
|
+
|
|
169
|
+
// Bulk close resolved tickets
|
|
170
|
+
await cstar.bulk.update({
|
|
171
|
+
resource: 'tickets',
|
|
172
|
+
ids: ['tkt_1', 'tkt_2', 'tkt_3'],
|
|
173
|
+
data: { status: 'closed' }
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Export data
|
|
177
|
+
const { data: exportResult } = await cstar.export.create({
|
|
178
|
+
resources: [{ type: 'tickets', filters: { status: 'closed' } }],
|
|
179
|
+
format: 'csv'
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Saved views
|
|
183
|
+
const { data: view } = await cstar.views.create({
|
|
184
|
+
name: 'Urgent Unresolved',
|
|
185
|
+
entityType: 'tickets',
|
|
186
|
+
filters: { priority: 'urgent', status: 'open' }
|
|
187
|
+
});
|
|
188
|
+
const { data: results } = await cstar.views.execute(view.id);
|
|
189
|
+
|
|
190
|
+
// Search across everything
|
|
191
|
+
const { data: searchResults } = await cstar.search.query({
|
|
192
|
+
query: 'billing issue',
|
|
193
|
+
types: ['tickets', 'customers']
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Client-Side: Chat Widget SDK
|
|
49
200
|
|
|
50
|
-
Build custom chat experiences
|
|
201
|
+
Build custom chat experiences in the browser. Uses a publishable key — safe for frontend code.
|
|
51
202
|
|
|
52
203
|
```typescript
|
|
53
204
|
import { ChatClient } from '@cstar.help/js/chat';
|
|
@@ -66,7 +217,7 @@ const { conversations } = await chat.conversations.list();
|
|
|
66
217
|
// Send a message
|
|
67
218
|
await chat.messages.send('tkt_abc123', 'Thanks for the help!');
|
|
68
219
|
|
|
69
|
-
// Subscribe to new messages
|
|
220
|
+
// Subscribe to new messages
|
|
70
221
|
const unsubscribe = chat.onMessage('tkt_abc123', (message) => {
|
|
71
222
|
console.log(`${message.sender_name}: ${message.content}`);
|
|
72
223
|
});
|
|
@@ -75,88 +226,48 @@ const unsubscribe = chat.onMessage('tkt_abc123', (message) => {
|
|
|
75
226
|
chat.disconnect();
|
|
76
227
|
```
|
|
77
228
|
|
|
78
|
-
|
|
229
|
+
---
|
|
79
230
|
|
|
80
|
-
|
|
231
|
+
## Client-Side: Public Knowledge Base
|
|
232
|
+
|
|
233
|
+
Search and browse your public help center — **no authentication required**.
|
|
81
234
|
|
|
82
235
|
```typescript
|
|
83
236
|
import { LibraryClient } from '@cstar.help/js/library';
|
|
84
237
|
|
|
85
238
|
const library = new LibraryClient({ teamSlug: 'acme' });
|
|
86
239
|
|
|
87
|
-
// List categories
|
|
88
240
|
const categories = await library.categories();
|
|
89
|
-
|
|
90
|
-
// Search articles
|
|
91
241
|
const results = await library.search('reset password');
|
|
92
|
-
|
|
93
|
-
// Get a specific article
|
|
94
242
|
const article = await library.article('how-to-reset-password');
|
|
95
243
|
```
|
|
96
244
|
|
|
97
|
-
|
|
245
|
+
---
|
|
98
246
|
|
|
99
|
-
|
|
247
|
+
## Client-Side: Public Community
|
|
248
|
+
|
|
249
|
+
Browse your public community forum — **no authentication required**.
|
|
100
250
|
|
|
101
251
|
```typescript
|
|
102
252
|
import { CommunityClient } from '@cstar.help/js/community';
|
|
103
253
|
|
|
104
254
|
const community = new CommunityClient({ teamSlug: 'acme' });
|
|
105
255
|
|
|
106
|
-
// List discussion topics
|
|
107
256
|
const topics = await community.topics();
|
|
108
|
-
|
|
109
|
-
// List posts (with filters)
|
|
110
|
-
const { posts, count } = await community.posts({
|
|
111
|
-
topicSlug: 'feature-requests',
|
|
112
|
-
sort: 'votes'
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Get a single post with all comments
|
|
257
|
+
const { posts } = await community.posts({ topicSlug: 'feature-requests', sort: 'votes' });
|
|
116
258
|
const { post, comments } = await community.post('dark-mode-support');
|
|
117
|
-
|
|
118
|
-
// Search posts
|
|
119
|
-
const results = await community.search('billing');
|
|
120
259
|
```
|
|
121
260
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Manage community posts with your secret API key via `CStarClient`:
|
|
261
|
+
---
|
|
125
262
|
|
|
126
|
-
|
|
127
|
-
const cstar = new CStarClient({ apiKey: 'sk_live_...', teamId: 'tm_...' });
|
|
128
|
-
|
|
129
|
-
// List all posts (including hidden/unapproved)
|
|
130
|
-
const { data: posts } = await cstar.community.posts.list({ status: 'open' });
|
|
131
|
-
|
|
132
|
-
// Create a post
|
|
133
|
-
const { data: post } = await cstar.community.posts.create({
|
|
134
|
-
title: 'New Feature: Dark Mode',
|
|
135
|
-
body: 'We just shipped dark mode!',
|
|
136
|
-
topicId: 'topic_abc123'
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// Toggle vote on a post
|
|
140
|
-
const { voted, voteCount } = await cstar.community.posts.vote('post_id');
|
|
141
|
-
|
|
142
|
-
// Add a comment
|
|
143
|
-
const comment = await cstar.community.posts.createComment('post_id', {
|
|
144
|
-
body: 'Great idea!',
|
|
145
|
-
isOfficial: true
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// List topics
|
|
149
|
-
const { data: topics } = await cstar.community.topics.list();
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Webhook Verification
|
|
263
|
+
## Server-Side: Webhook Verification
|
|
153
264
|
|
|
154
265
|
Verify incoming webhook signatures on your server.
|
|
155
266
|
|
|
156
267
|
```typescript
|
|
157
268
|
import { constructEvent } from '@cstar.help/js/webhook';
|
|
158
269
|
|
|
159
|
-
// In your webhook handler
|
|
270
|
+
// In your webhook handler
|
|
160
271
|
const event = constructEvent(rawBody, signature, 'whsec_your_secret');
|
|
161
272
|
|
|
162
273
|
switch (event.type) {
|
|
@@ -169,33 +280,20 @@ switch (event.type) {
|
|
|
169
280
|
}
|
|
170
281
|
```
|
|
171
282
|
|
|
172
|
-
For edge
|
|
283
|
+
For edge runtimes (Cloudflare Workers, Deno), use the async variant:
|
|
173
284
|
|
|
174
285
|
```typescript
|
|
175
286
|
import { constructEventAsync } from '@cstar.help/js/webhook';
|
|
176
|
-
|
|
177
287
|
const event = await constructEventAsync(rawBody, signature, secret);
|
|
178
288
|
```
|
|
179
289
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
### CStarClient
|
|
183
|
-
|
|
184
|
-
| Resource | Methods |
|
|
185
|
-
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
186
|
-
| `cstar.tickets` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.listAutoPaginating()` |
|
|
187
|
-
| `cstar.tickets.messages` | `.list(ticketId)` `.create(ticketId, params)` |
|
|
188
|
-
| `cstar.customers` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.listAutoPaginating()` |
|
|
189
|
-
| `cstar.articles` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.listAutoPaginating()` |
|
|
190
|
-
| `cstar.webhooks` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.test(id)` |
|
|
191
|
-
| `cstar.community.posts` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.vote(id)` `.follow(id)` `.listComments(id)` `.createComment(id, params)` |
|
|
192
|
-
| `cstar.community.topics` | `.list()` `.get(id)` |
|
|
290
|
+
---
|
|
193
291
|
|
|
194
|
-
|
|
292
|
+
## Configuration
|
|
195
293
|
|
|
196
294
|
```typescript
|
|
197
295
|
const cstar = new CStarClient({
|
|
198
|
-
apiKey: 'sk_live_...', // Required —
|
|
296
|
+
apiKey: 'sk_live_...', // Required — must be a secret key (sk_*)
|
|
199
297
|
teamId: 'tm_...', // Required — your team ID
|
|
200
298
|
baseUrl: 'https://...', // Optional — defaults to https://www.cstar.help
|
|
201
299
|
version: '2026-03-01', // Optional — API version header
|
|
@@ -204,26 +302,32 @@ const cstar = new CStarClient({
|
|
|
204
302
|
});
|
|
205
303
|
```
|
|
206
304
|
|
|
207
|
-
|
|
305
|
+
## Test Mode
|
|
208
306
|
|
|
209
307
|
Test mode is auto-detected from your API key prefix:
|
|
210
308
|
|
|
211
309
|
```typescript
|
|
212
|
-
// Uses test data — no real customers affected
|
|
213
310
|
const cstar = new CStarClient({
|
|
214
311
|
apiKey: 'sk_test_abc123...',
|
|
215
312
|
teamId: 'tm_your_team_id'
|
|
216
313
|
});
|
|
217
314
|
|
|
218
315
|
console.log(cstar.isTestMode); // true
|
|
316
|
+
// All operations use test data — no real customers affected
|
|
219
317
|
```
|
|
220
318
|
|
|
221
|
-
|
|
319
|
+
## Error Handling
|
|
222
320
|
|
|
223
321
|
All API errors throw typed exceptions:
|
|
224
322
|
|
|
225
323
|
```typescript
|
|
226
|
-
import {
|
|
324
|
+
import {
|
|
325
|
+
CStarError,
|
|
326
|
+
CStarAuthenticationError,
|
|
327
|
+
CStarRateLimitError,
|
|
328
|
+
CStarNotFoundError,
|
|
329
|
+
CStarValidationError
|
|
330
|
+
} from '@cstar.help/js';
|
|
227
331
|
|
|
228
332
|
try {
|
|
229
333
|
await cstar.tickets.get('tkt_nonexistent');
|
|
@@ -232,15 +336,19 @@ try {
|
|
|
232
336
|
console.log('Ticket not found:', err.message);
|
|
233
337
|
} else if (err instanceof CStarValidationError) {
|
|
234
338
|
console.log('Invalid param:', err.param);
|
|
339
|
+
} else if (err instanceof CStarRateLimitError) {
|
|
340
|
+
console.log('Rate limited, retry after:', err.retryAfter, 'seconds');
|
|
341
|
+
} else if (err instanceof CStarAuthenticationError) {
|
|
342
|
+
console.log('Bad API key');
|
|
235
343
|
} else if (err instanceof CStarError) {
|
|
236
344
|
console.log('API error:', err.code, err.statusCode);
|
|
237
345
|
}
|
|
238
346
|
}
|
|
239
347
|
```
|
|
240
348
|
|
|
241
|
-
|
|
349
|
+
## Idempotency
|
|
242
350
|
|
|
243
|
-
Safely retry create operations:
|
|
351
|
+
Safely retry create/update operations:
|
|
244
352
|
|
|
245
353
|
```typescript
|
|
246
354
|
const { data: ticket } = await cstar.tickets.create(
|