@cstar.help/js 0.12.1 → 0.13.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.
@@ -1,3 +1,155 @@
1
+ // src/errors.ts
2
+ var ErrorType = {
3
+ AUTHENTICATION_ERROR: "authentication_error",
4
+ PERMISSION_ERROR: "permission_error",
5
+ INVALID_REQUEST_ERROR: "invalid_request_error",
6
+ NOT_FOUND_ERROR: "not_found_error",
7
+ CONFLICT_ERROR: "conflict_error",
8
+ RATE_LIMIT_ERROR: "rate_limit_error",
9
+ FEATURE_NOT_CONFIGURED: "feature_not_configured",
10
+ API_ERROR: "api_error",
11
+ SERVER_ERROR: "server_error"
12
+ };
13
+ var DEFAULT_DOC_URL = "https://cstar.help/developers/api/errors";
14
+ function normalizeArgs(messageOrStatus, maybeBody, defaults) {
15
+ if (typeof messageOrStatus === "string") {
16
+ return {
17
+ statusCode: defaults?.code === "rate_limit_error" ? 429 : 0,
18
+ body: {
19
+ type: defaults?.type ?? ErrorType.API_ERROR,
20
+ code: defaults?.code ?? "sdk_error",
21
+ message: messageOrStatus,
22
+ doc_url: defaults?.doc_url ?? DEFAULT_DOC_URL,
23
+ ...defaults?.feature ? { feature: defaults.feature } : {},
24
+ ...defaults?.enable_url ? { enable_url: defaults.enable_url } : {}
25
+ }
26
+ };
27
+ }
28
+ return {
29
+ statusCode: messageOrStatus,
30
+ body: maybeBody ?? {
31
+ type: defaults?.type ?? ErrorType.API_ERROR,
32
+ code: defaults?.code ?? "unknown_error",
33
+ message: defaults?.message ?? `Request failed with status ${messageOrStatus}`,
34
+ doc_url: defaults?.doc_url ?? DEFAULT_DOC_URL
35
+ }
36
+ };
37
+ }
38
+ var CStarError = class extends Error {
39
+ constructor(messageOrStatus, body) {
40
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body);
41
+ super(resolved.message);
42
+ this.name = "CStarError";
43
+ this.type = resolved.type;
44
+ this.code = resolved.code;
45
+ this.param = resolved.param;
46
+ this.docUrl = resolved.doc_url;
47
+ this.requestId = resolved.request_id;
48
+ this.statusCode = statusCode;
49
+ }
50
+ };
51
+ var CStarAuthenticationError = class extends CStarError {
52
+ constructor(messageOrStatus, body) {
53
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
54
+ type: ErrorType.AUTHENTICATION_ERROR,
55
+ code: "authentication_failed",
56
+ doc_url: `${DEFAULT_DOC_URL}#authentication-error`
57
+ });
58
+ super(statusCode, resolved);
59
+ this.name = "CStarAuthenticationError";
60
+ }
61
+ };
62
+ var CStarRateLimitError = class extends CStarError {
63
+ constructor(messageOrStatus, body, retryAfter) {
64
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
65
+ type: ErrorType.RATE_LIMIT_ERROR,
66
+ code: "rate_limit_exceeded",
67
+ doc_url: `${DEFAULT_DOC_URL}#rate-limit-error`
68
+ });
69
+ super(statusCode === 0 ? 429 : statusCode, resolved);
70
+ this.name = "CStarRateLimitError";
71
+ this.retryAfter = retryAfter ?? 60;
72
+ }
73
+ };
74
+ var CStarNotFoundError = class extends CStarError {
75
+ constructor(messageOrStatus, body) {
76
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
77
+ type: ErrorType.NOT_FOUND_ERROR,
78
+ code: "resource_not_found",
79
+ doc_url: `${DEFAULT_DOC_URL}#not-found-error`
80
+ });
81
+ super(statusCode, resolved);
82
+ this.name = "CStarNotFoundError";
83
+ }
84
+ };
85
+ var CStarValidationError = class extends CStarError {
86
+ constructor(messageOrStatus, body) {
87
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
88
+ type: ErrorType.INVALID_REQUEST_ERROR,
89
+ code: "validation_error",
90
+ doc_url: `${DEFAULT_DOC_URL}#validation-error`
91
+ });
92
+ super(statusCode, resolved);
93
+ this.name = "CStarValidationError";
94
+ }
95
+ };
96
+ var CStarPermissionError = class extends CStarError {
97
+ constructor(messageOrStatus, body) {
98
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
99
+ type: ErrorType.PERMISSION_ERROR,
100
+ code: "permission_denied",
101
+ doc_url: `${DEFAULT_DOC_URL}#permission-error`
102
+ });
103
+ super(statusCode, resolved);
104
+ this.name = "CStarPermissionError";
105
+ }
106
+ };
107
+ var CStarConflictError = class extends CStarError {
108
+ constructor(messageOrStatus, body) {
109
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
110
+ type: ErrorType.CONFLICT_ERROR,
111
+ code: "conflict",
112
+ doc_url: `${DEFAULT_DOC_URL}#conflict-error`
113
+ });
114
+ super(statusCode, resolved);
115
+ this.name = "CStarConflictError";
116
+ }
117
+ };
118
+ var CStarServerError = class extends CStarError {
119
+ constructor(messageOrStatus, body) {
120
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
121
+ type: ErrorType.SERVER_ERROR,
122
+ code: "server_error",
123
+ doc_url: `${DEFAULT_DOC_URL}#server-error`
124
+ });
125
+ super(statusCode, resolved);
126
+ this.name = "CStarServerError";
127
+ }
128
+ };
129
+ var CStarFeatureDisabledError = class extends CStarError {
130
+ constructor(messageOrStatus, body) {
131
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
132
+ type: ErrorType.FEATURE_NOT_CONFIGURED,
133
+ code: "feature_not_configured",
134
+ doc_url: `${DEFAULT_DOC_URL}#feature-not-configured`
135
+ });
136
+ super(statusCode, resolved);
137
+ this.name = "CStarFeatureDisabledError";
138
+ this.feature = resolved.feature ?? "unknown";
139
+ this.enableUrl = resolved.enable_url ?? "";
140
+ }
141
+ };
142
+ var TYPE_TO_CLASS = {
143
+ [ErrorType.AUTHENTICATION_ERROR]: CStarAuthenticationError,
144
+ [ErrorType.PERMISSION_ERROR]: CStarPermissionError,
145
+ [ErrorType.RATE_LIMIT_ERROR]: CStarRateLimitError,
146
+ [ErrorType.NOT_FOUND_ERROR]: CStarNotFoundError,
147
+ [ErrorType.INVALID_REQUEST_ERROR]: CStarValidationError,
148
+ [ErrorType.CONFLICT_ERROR]: CStarConflictError,
149
+ [ErrorType.SERVER_ERROR]: CStarServerError,
150
+ [ErrorType.FEATURE_NOT_CONFIGURED]: CStarFeatureDisabledError
151
+ };
152
+
1
153
  // src/quickhelp/index.ts
2
154
  var DEFAULT_BASE_URL = "https://www.cstar.help";
3
155
  var QuickHelpClient = class {
@@ -7,6 +159,16 @@ var QuickHelpClient = class {
7
159
  }
8
160
  this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
9
161
  this.teamId = config.teamId;
162
+ this.publishableKey = config.publishableKey ?? null;
163
+ this.mode = this.publishableKey?.startsWith("pk_test_") ? "test" : "live";
164
+ if (this.publishableKey && !/^pk_(test|live)_/.test(this.publishableKey)) {
165
+ throw new Error(
166
+ "[cStar] QuickHelpClient: publishableKey must start with `pk_test_` or `pk_live_`. Secret keys (`sk_*`) cannot be used in browser/anonymous contexts."
167
+ );
168
+ }
169
+ }
170
+ authHeaders() {
171
+ return this.publishableKey ? { Authorization: `Bearer ${this.publishableKey}` } : {};
10
172
  }
11
173
  /**
12
174
  * Fetch the sanitized public config (title, subtitle, placeholder,
@@ -17,7 +179,7 @@ var QuickHelpClient = class {
17
179
  */
18
180
  async config() {
19
181
  const url = `${this.baseUrl}/api/quickhelp/config/${encodeURIComponent(this.teamId)}`;
20
- const res = await globalThis.fetch(url, { method: "GET" });
182
+ const res = await globalThis.fetch(url, { method: "GET", headers: this.authHeaders() });
21
183
  if (res.status === 404) return null;
22
184
  if (!res.ok) {
23
185
  const body2 = await res.json().catch(() => null);
@@ -36,7 +198,7 @@ var QuickHelpClient = class {
36
198
  const url = `${this.baseUrl}/api/quickhelp/ask`;
37
199
  const res = await globalThis.fetch(url, {
38
200
  method: "POST",
39
- headers: { "Content-Type": "application/json" },
201
+ headers: { "Content-Type": "application/json", ...this.authHeaders() },
40
202
  body: JSON.stringify({
41
203
  team_id: this.teamId,
42
204
  session_id: params.sessionId ?? null,
@@ -45,6 +207,16 @@ var QuickHelpClient = class {
45
207
  });
46
208
  const body = await res.json().catch(() => null);
47
209
  if (!res.ok || !body?.success || !body.data) {
210
+ if (res.status === 503 || body?.error?.code === "feature_not_configured") {
211
+ throw new CStarFeatureDisabledError(res.status, {
212
+ type: "feature_not_configured",
213
+ code: "feature_not_configured",
214
+ message: body?.error?.message ?? "Quick Help is not available on this team. Enable an AI Agent (Settings \u2192 AI Agents) and configure Quick Help (Settings \u2192 Quick Help) before calling .ask().",
215
+ doc_url: "https://cstar.help/developers/api/errors#feature-not-configured",
216
+ feature: "quickHelp",
217
+ enable_url: `${this.baseUrl}/team/${encodeURIComponent(this.teamId)}/settings?section=quickHelp`
218
+ });
219
+ }
48
220
  const message = body?.error?.message ?? `Quick Help ask failed: ${res.status}`;
49
221
  const err = new QuickHelpError(message, {
50
222
  status: res.status,
@@ -21,9 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var webhook_exports = {};
22
22
  __export(webhook_exports, {
23
23
  WebhookSignatureVerificationError: () => WebhookSignatureVerificationError,
24
- constructEvent: () => constructEvent,
25
24
  constructEventAsync: () => constructEventAsync,
26
- verifySignature: () => verifySignature,
27
25
  verifySignatureAsync: () => verifySignatureAsync
28
26
  });
29
27
  module.exports = __toCommonJS(webhook_exports);
@@ -33,14 +31,6 @@ var WebhookSignatureVerificationError = class extends Error {
33
31
  this.name = "WebhookSignatureVerificationError";
34
32
  }
35
33
  };
36
- function constructEvent(rawBody, signature, secret) {
37
- if (!verifySignature(rawBody, signature, secret)) {
38
- throw new WebhookSignatureVerificationError(
39
- "Webhook signature verification failed. Ensure you are using the raw request body and the correct endpoint secret."
40
- );
41
- }
42
- return JSON.parse(rawBody);
43
- }
44
34
  async function constructEventAsync(rawBody, signature, secret) {
45
35
  const valid = await verifySignatureAsync(rawBody, signature, secret);
46
36
  if (!valid) {
@@ -50,12 +40,6 @@ async function constructEventAsync(rawBody, signature, secret) {
50
40
  }
51
41
  return JSON.parse(rawBody);
52
42
  }
53
- function verifySignature(rawBody, signature, secret) {
54
- const crypto2 = require("crypto");
55
- const expected = "sha256=" + crypto2.createHmac("sha256", secret).update(rawBody).digest("hex");
56
- if (signature.length !== expected.length) return false;
57
- return crypto2.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
58
- }
59
43
  async function verifySignatureAsync(rawBody, signature, secret) {
60
44
  const encoder = new TextEncoder();
61
45
  const key = await crypto.subtle.importKey(
@@ -77,8 +61,6 @@ async function verifySignatureAsync(rawBody, signature, secret) {
77
61
  // Annotate the CommonJS export names for ESM import in node:
78
62
  0 && (module.exports = {
79
63
  WebhookSignatureVerificationError,
80
- constructEvent,
81
64
  constructEventAsync,
82
- verifySignature,
83
65
  verifySignatureAsync
84
66
  });
@@ -0,0 +1,440 @@
1
+ /** Every webhook event type cStar can dispatch. */
2
+ type WebhookEventType = 'article.created' | 'article.deleted' | 'article.published' | 'article.updated' | 'boss.damaged' | 'boss.defeated' | 'boss.spawned' | 'community_post.commented' | 'community_post.created' | 'community_post.deleted' | 'community_post.status_changed' | 'community_post.updated' | 'community_post.voted' | 'customer.created' | 'customer.deleted' | 'customer.updated' | 'customer_group.created' | 'customer_group.deleted' | 'customer_group.updated' | 'player.achievement_unlocked' | 'player.level_up' | 'player.quest_completed' | 'player.streak_milestone' | 'player.xp_gained' | 'social.high_five_sent' | 'social.one_up_sent' | 'survey.submitted' | 'ticket.assigned' | 'ticket.closed' | 'ticket.created' | 'ticket.deleted' | 'ticket.message_added' | 'ticket.updated';
3
+ /** Payload for `article.created` events. */
4
+ interface ArticleCreatedPayload {
5
+ article: {
6
+ id: string;
7
+ title: string;
8
+ slug: string;
9
+ is_public: boolean;
10
+ created_at: string;
11
+ };
12
+ }
13
+ /** Payload for `article.deleted` events. */
14
+ interface ArticleDeletedPayload {
15
+ id: string;
16
+ }
17
+ /** Payload for `article.published` events. */
18
+ interface ArticlePublishedPayload {
19
+ article: {
20
+ id: string;
21
+ title: string;
22
+ slug: string;
23
+ is_public: boolean;
24
+ published_at: string;
25
+ };
26
+ }
27
+ /** Payload for `article.updated` events. */
28
+ interface ArticleUpdatedPayload {
29
+ article: {
30
+ id: string;
31
+ title: string;
32
+ slug: string;
33
+ is_public: boolean;
34
+ };
35
+ }
36
+ /** Payload for `boss.damaged` events. */
37
+ interface BossDamagedPayload {
38
+ boss: {
39
+ id: string;
40
+ current_hp: number;
41
+ };
42
+ damage: number;
43
+ by: {
44
+ agent_id: string;
45
+ name: string;
46
+ };
47
+ }
48
+ /** Payload for `boss.defeated` events. */
49
+ interface BossDefeatedPayload {
50
+ boss: {
51
+ id: string;
52
+ name: string;
53
+ };
54
+ final_blow_by: {
55
+ agent_id: string;
56
+ name: string;
57
+ };
58
+ team_xp_awarded: number;
59
+ }
60
+ /** Payload for `boss.spawned` events. */
61
+ interface BossSpawnedPayload {
62
+ boss: {
63
+ id: string;
64
+ name: string;
65
+ level: number;
66
+ max_hp: number;
67
+ };
68
+ }
69
+ /** Payload for `community_post.commented` events. */
70
+ interface CommunityPostCommentedPayload {
71
+ comment: {
72
+ id: string;
73
+ post_id: string;
74
+ content: string;
75
+ author_id: string;
76
+ created_at: string;
77
+ };
78
+ }
79
+ /** Payload for `community_post.created` events. */
80
+ interface CommunityPostCreatedPayload {
81
+ post: {
82
+ id: string;
83
+ prefixed_id: string;
84
+ title: string;
85
+ status: string;
86
+ visibility: string;
87
+ topic: string;
88
+ created_at: string;
89
+ };
90
+ }
91
+ /** Payload for `community_post.deleted` events. */
92
+ interface CommunityPostDeletedPayload {
93
+ post: {
94
+ id: string;
95
+ prefixed_id?: string;
96
+ title?: string;
97
+ };
98
+ }
99
+ /** Payload for `community_post.status_changed` events. */
100
+ interface CommunityPostStatusChangedPayload {
101
+ post: {
102
+ id: string;
103
+ title: string;
104
+ status: string;
105
+ visibility: string;
106
+ };
107
+ }
108
+ /** Payload for `community_post.updated` events. */
109
+ interface CommunityPostUpdatedPayload {
110
+ post: {
111
+ id: string;
112
+ title: string;
113
+ status: string;
114
+ visibility: string;
115
+ };
116
+ }
117
+ /** Payload for `community_post.voted` events. */
118
+ interface CommunityPostVotedPayload {
119
+ post: {
120
+ id: string;
121
+ vote_count: number;
122
+ };
123
+ voter: {
124
+ id: string;
125
+ };
126
+ direction: string;
127
+ }
128
+ /** Payload for `customer.created` events. */
129
+ interface CustomerCreatedPayload {
130
+ customer: {
131
+ id: string;
132
+ name: string;
133
+ email: string;
134
+ status: string;
135
+ tags: Array<string>;
136
+ created_at: string;
137
+ };
138
+ }
139
+ /** Payload for `customer.deleted` events. */
140
+ interface CustomerDeletedPayload {
141
+ id: string;
142
+ }
143
+ /** Payload for `customer.updated` events. */
144
+ interface CustomerUpdatedPayload {
145
+ customer: {
146
+ id: string;
147
+ name: string;
148
+ email: string;
149
+ status: string;
150
+ tags: Array<string>;
151
+ };
152
+ changes: {
153
+ tags: {
154
+ from: Array<string>;
155
+ to: Array<string>;
156
+ };
157
+ };
158
+ }
159
+ /** Payload for `customer_group.created` events. */
160
+ interface CustomerGroupCreatedPayload {
161
+ group: {
162
+ id: string;
163
+ name: string;
164
+ color: string;
165
+ member_count: number;
166
+ };
167
+ }
168
+ /** Payload for `customer_group.deleted` events. */
169
+ interface CustomerGroupDeletedPayload {
170
+ customer_group: {
171
+ id: string;
172
+ };
173
+ }
174
+ /** Payload for `customer_group.updated` events. */
175
+ interface CustomerGroupUpdatedPayload {
176
+ group: {
177
+ id: string;
178
+ name: string;
179
+ color: string;
180
+ member_count: number;
181
+ };
182
+ }
183
+ /** Payload for `player.achievement_unlocked` events. */
184
+ interface PlayerAchievementUnlockedPayload {
185
+ player: {
186
+ agent_id: string;
187
+ name: string;
188
+ };
189
+ achievement: {
190
+ id: string;
191
+ name: string;
192
+ xp: number;
193
+ };
194
+ }
195
+ /** Payload for `player.level_up` events. */
196
+ interface PlayerLevelUpPayload {
197
+ player: {
198
+ agent_id: string;
199
+ name: string;
200
+ };
201
+ level: {
202
+ from: number;
203
+ to: number;
204
+ };
205
+ title_unlocked: string;
206
+ }
207
+ /** Payload for `player.quest_completed` events. */
208
+ interface PlayerQuestCompletedPayload {
209
+ player: {
210
+ agent_id: string;
211
+ name: string;
212
+ };
213
+ quest: {
214
+ id: string;
215
+ name: string;
216
+ xp: number;
217
+ };
218
+ }
219
+ /** Payload for `player.streak_milestone` events. */
220
+ interface PlayerStreakMilestonePayload {
221
+ player: {
222
+ agent_id: string;
223
+ name: string;
224
+ };
225
+ streak_days: number;
226
+ milestone: number;
227
+ }
228
+ /** Payload for `player.xp_gained` events. */
229
+ interface PlayerXpGainedPayload {
230
+ player: {
231
+ agent_id: string;
232
+ name: string;
233
+ };
234
+ amount: number;
235
+ reason: string;
236
+ ticket_id: string;
237
+ }
238
+ /** Payload for `social.high_five_sent` events. */
239
+ interface SocialHighFiveSentPayload {
240
+ from_team: {
241
+ id: string;
242
+ name: string;
243
+ };
244
+ to_team: {
245
+ id: string;
246
+ name: string;
247
+ };
248
+ message: string;
249
+ }
250
+ /** Payload for `social.one_up_sent` events. */
251
+ interface SocialOneUpSentPayload {
252
+ from: {
253
+ agent_id: string;
254
+ name: string;
255
+ };
256
+ to: {
257
+ agent_id: string;
258
+ name: string;
259
+ };
260
+ ticket_id: string;
261
+ message: string;
262
+ }
263
+ /** Payload for `survey.submitted` events. */
264
+ interface SurveySubmittedPayload {
265
+ survey: {
266
+ id: string;
267
+ ticket_id: string;
268
+ score: number;
269
+ comment: string;
270
+ submitted_at: string;
271
+ };
272
+ customer: {
273
+ id: string;
274
+ email: string;
275
+ };
276
+ }
277
+ /** Payload for `ticket.assigned` events. */
278
+ interface TicketAssignedPayload {
279
+ ticket: {
280
+ id: string;
281
+ prefixed_id: string;
282
+ };
283
+ assignee: {
284
+ id: string;
285
+ name: string;
286
+ email: string;
287
+ };
288
+ previous_assignee: null;
289
+ }
290
+ /** Payload for `ticket.closed` events. */
291
+ interface TicketClosedPayload {
292
+ ticket: {
293
+ id: string;
294
+ prefixed_id: string;
295
+ title: string;
296
+ status: string;
297
+ priority: string;
298
+ channel: string;
299
+ resolved_at: string;
300
+ };
301
+ changes?: Record<string, {
302
+ from: string;
303
+ to: string;
304
+ }>;
305
+ }
306
+ /** Payload for `ticket.created` events. */
307
+ interface TicketCreatedPayload {
308
+ ticket: {
309
+ id: string;
310
+ prefixed_id: string;
311
+ title: string;
312
+ status: string;
313
+ priority: string;
314
+ channel: string;
315
+ customer_id: string;
316
+ created_at: string;
317
+ };
318
+ customer: {
319
+ id: string;
320
+ name: string;
321
+ email: string;
322
+ };
323
+ }
324
+ /** Payload for `ticket.deleted` events. */
325
+ interface TicketDeletedPayload {
326
+ id: string;
327
+ }
328
+ /** Payload for `ticket.message_added` events. */
329
+ interface TicketMessageAddedPayload {
330
+ ticket: {
331
+ id: string;
332
+ dashboard_url: string | null;
333
+ };
334
+ message: {
335
+ id: string;
336
+ sender: string;
337
+ sender_name: string | null;
338
+ content: string;
339
+ created_at: string;
340
+ };
341
+ customer?: {
342
+ id: string;
343
+ name: string | null;
344
+ email: string | null;
345
+ };
346
+ }
347
+ /** Payload for `ticket.updated` events. */
348
+ interface TicketUpdatedPayload {
349
+ ticket: {
350
+ id: string;
351
+ prefixed_id: string;
352
+ title: string;
353
+ status: string;
354
+ priority: string;
355
+ channel: string;
356
+ };
357
+ changes?: Record<string, {
358
+ from: string;
359
+ to: string;
360
+ } | {
361
+ from: Array<string>;
362
+ to: Array<string>;
363
+ }>;
364
+ }
365
+ /** Maps each event type to its payload interface — used internally for the discriminated union. */
366
+ interface WebhookEventDataMap {
367
+ 'article.created': ArticleCreatedPayload;
368
+ 'article.deleted': ArticleDeletedPayload;
369
+ 'article.published': ArticlePublishedPayload;
370
+ 'article.updated': ArticleUpdatedPayload;
371
+ 'boss.damaged': BossDamagedPayload;
372
+ 'boss.defeated': BossDefeatedPayload;
373
+ 'boss.spawned': BossSpawnedPayload;
374
+ 'community_post.commented': CommunityPostCommentedPayload;
375
+ 'community_post.created': CommunityPostCreatedPayload;
376
+ 'community_post.deleted': CommunityPostDeletedPayload;
377
+ 'community_post.status_changed': CommunityPostStatusChangedPayload;
378
+ 'community_post.updated': CommunityPostUpdatedPayload;
379
+ 'community_post.voted': CommunityPostVotedPayload;
380
+ 'customer.created': CustomerCreatedPayload;
381
+ 'customer.deleted': CustomerDeletedPayload;
382
+ 'customer.updated': CustomerUpdatedPayload;
383
+ 'customer_group.created': CustomerGroupCreatedPayload;
384
+ 'customer_group.deleted': CustomerGroupDeletedPayload;
385
+ 'customer_group.updated': CustomerGroupUpdatedPayload;
386
+ 'player.achievement_unlocked': PlayerAchievementUnlockedPayload;
387
+ 'player.level_up': PlayerLevelUpPayload;
388
+ 'player.quest_completed': PlayerQuestCompletedPayload;
389
+ 'player.streak_milestone': PlayerStreakMilestonePayload;
390
+ 'player.xp_gained': PlayerXpGainedPayload;
391
+ 'social.high_five_sent': SocialHighFiveSentPayload;
392
+ 'social.one_up_sent': SocialOneUpSentPayload;
393
+ 'survey.submitted': SurveySubmittedPayload;
394
+ 'ticket.assigned': TicketAssignedPayload;
395
+ 'ticket.closed': TicketClosedPayload;
396
+ 'ticket.created': TicketCreatedPayload;
397
+ 'ticket.deleted': TicketDeletedPayload;
398
+ 'ticket.message_added': TicketMessageAddedPayload;
399
+ 'ticket.updated': TicketUpdatedPayload;
400
+ }
401
+ /** A webhook event delivered to your endpoint. */
402
+ interface WebhookEvent<T extends WebhookEventType = WebhookEventType> {
403
+ /** Unique event ID (idempotency key). */
404
+ id: string;
405
+ /** Event type. */
406
+ type: T;
407
+ /** When the event was created (ISO 8601). */
408
+ created_at: string;
409
+ /** Team that owns this event. */
410
+ team_id: string;
411
+ /** Event-specific payload. */
412
+ data: WebhookEventDataMap[T];
413
+ }
414
+
415
+ /** Thrown when webhook signature verification fails. */
416
+ declare class WebhookSignatureVerificationError extends Error {
417
+ constructor(message: string);
418
+ }
419
+ /**
420
+ * Verify a webhook signature and parse the event.
421
+ *
422
+ * Uses the Web Crypto API — works in browsers, Cloudflare Workers, Vercel Edge,
423
+ * Deno, Bun, and Node.js 18+. This is the recommended API for all runtimes.
424
+ *
425
+ * For a synchronous Node-only variant, import from `@cstar.help/js/webhook/node`.
426
+ *
427
+ * @param rawBody - Raw request body as a string (NOT parsed JSON)
428
+ * @param signature - Value of the `X-Signature` header
429
+ * @param secret - Your webhook endpoint's secret (`whsec_*`)
430
+ * @returns Parsed and verified webhook event
431
+ * @throws {WebhookSignatureVerificationError} If verification fails
432
+ */
433
+ declare function constructEventAsync(rawBody: string, signature: string, secret: string): Promise<WebhookEvent>;
434
+ /**
435
+ * Verify an HMAC-SHA256 signature using the Web Crypto API.
436
+ * Constant-time comparison to prevent timing attacks.
437
+ */
438
+ declare function verifySignatureAsync(rawBody: string, signature: string, secret: string): Promise<boolean>;
439
+
440
+ export { type WebhookEvent, WebhookSignatureVerificationError, constructEventAsync, verifySignatureAsync };