@cstar.help/js 0.18.0 → 0.18.2
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 +16 -0
- package/dist/chat/index.cjs +3 -2
- package/dist/chat/index.d.cts +1 -1
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/index.js +3 -2
- package/dist/community/index.d.cts +2 -2
- package/dist/community/index.d.ts +2 -2
- package/dist/index.cjs +30 -9
- package/dist/index.d.cts +61 -14
- package/dist/index.d.ts +61 -14
- package/dist/index.js +30 -9
- package/dist/{types-DF7GprCV.d.cts → types-BHCahwev.d.cts} +3 -3
- package/dist/{types-DF7GprCV.d.ts → types-BHCahwev.d.ts} +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -480,6 +480,22 @@ const ticket = await cstar.tickets.create(
|
|
|
480
480
|
);
|
|
481
481
|
```
|
|
482
482
|
|
|
483
|
+
For 24 hours, the same request with the same key returns the first successful
|
|
484
|
+
response. The same key with a different body throws `CStarValidationError`
|
|
485
|
+
(`idempotency_key_reused`, 422). While the first request is still running, the
|
|
486
|
+
API answers `409 idempotency_key_in_use` with `Retry-After`. From 0.18.1, the client waits and retries within `maxRetries`; 0.18.0 throws
|
|
487
|
+
`CStarConflictError`.
|
|
488
|
+
|
|
489
|
+
The API ignores the key on `webhooks.create`, `webhooks.listen` and
|
|
490
|
+
`export.create`, so a key does not make their retries safe. From 0.18.2 the
|
|
491
|
+
client does not send the key on those calls and does not retry a network error
|
|
492
|
+
or 5xx; 0.18.1 and earlier still retry them, so there leave the key off (without
|
|
493
|
+
one the client does not retry them) or use a client with `maxRetries: 0`. Check
|
|
494
|
+
`webhooks.list()` before you retry `webhooks.create`. A repeated `export.create`
|
|
495
|
+
only reads your data again (nothing is stored). A repeated `webhooks.listen` is a
|
|
496
|
+
second CLI listener: remove it with `webhooks.unlisten(id)`, or it is swept after
|
|
497
|
+
5 minutes without a heartbeat.
|
|
498
|
+
|
|
483
499
|
## Requirements
|
|
484
500
|
|
|
485
501
|
- Node.js 22+ (matching the supported Supabase dependency)
|
package/dist/chat/index.cjs
CHANGED
|
@@ -222,6 +222,7 @@ var RealtimeManager = class {
|
|
|
222
222
|
// src/chat/presence.ts
|
|
223
223
|
var TYPING_THROTTLE_MS = 2e3;
|
|
224
224
|
var TYPING_CLEAR_DELAY_MS = 3e3;
|
|
225
|
+
var PRIVATE_TOPIC = { config: { private: true } };
|
|
225
226
|
var PresenceManager = class {
|
|
226
227
|
constructor(options) {
|
|
227
228
|
this.client = null;
|
|
@@ -245,7 +246,7 @@ var PresenceManager = class {
|
|
|
245
246
|
};
|
|
246
247
|
const channelName = `typing:${ticketId}`;
|
|
247
248
|
this.removeChannel(channelName);
|
|
248
|
-
const channel = this.client.channel(`ticket:${ticketId}
|
|
249
|
+
const channel = this.client.channel(`ticket:${ticketId}`, PRIVATE_TOPIC);
|
|
249
250
|
this.channels.set(channelName, channel);
|
|
250
251
|
channel.on("broadcast", { event: "typing" }, (data) => {
|
|
251
252
|
if (this.channels.get(channelName) !== channel) return;
|
|
@@ -297,7 +298,7 @@ var PresenceManager = class {
|
|
|
297
298
|
const sendChannelName = `typing-send:${ticketId}`;
|
|
298
299
|
let channel = this.channels.get(sendChannelName);
|
|
299
300
|
if (!channel) {
|
|
300
|
-
channel = this.client.channel(`ticket:${ticketId}
|
|
301
|
+
channel = this.client.channel(`ticket:${ticketId}`, PRIVATE_TOPIC);
|
|
301
302
|
channel.subscribe();
|
|
302
303
|
this.channels.set(sendChannelName, channel);
|
|
303
304
|
}
|
package/dist/chat/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CommunityCreatePostParams, a as CommunityCreatedPost, b as CommunityUpdatePostParams, c as CommunityUpdateCommentParams, d as CommunityCreatedComment, e as CommunityVoteResult, f as CommunityCreateCommentParams, g as CommunityFollowResult } from '../types-
|
|
1
|
+
import { C as CommunityCreatePostParams, a as CommunityCreatedPost, b as CommunityUpdatePostParams, c as CommunityUpdateCommentParams, d as CommunityCreatedComment, e as CommunityVoteResult, f as CommunityCreateCommentParams, g as CommunityFollowResult } from '../types-BHCahwev.cjs';
|
|
2
2
|
|
|
3
3
|
/** Configuration for the ChatClient. */
|
|
4
4
|
interface ChatConfig {
|
package/dist/chat/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CommunityCreatePostParams, a as CommunityCreatedPost, b as CommunityUpdatePostParams, c as CommunityUpdateCommentParams, d as CommunityCreatedComment, e as CommunityVoteResult, f as CommunityCreateCommentParams, g as CommunityFollowResult } from '../types-
|
|
1
|
+
import { C as CommunityCreatePostParams, a as CommunityCreatedPost, b as CommunityUpdatePostParams, c as CommunityUpdateCommentParams, d as CommunityCreatedComment, e as CommunityVoteResult, f as CommunityCreateCommentParams, g as CommunityFollowResult } from '../types-BHCahwev.js';
|
|
2
2
|
|
|
3
3
|
/** Configuration for the ChatClient. */
|
|
4
4
|
interface ChatConfig {
|
package/dist/chat/index.js
CHANGED
|
@@ -181,6 +181,7 @@ var RealtimeManager = class {
|
|
|
181
181
|
// src/chat/presence.ts
|
|
182
182
|
var TYPING_THROTTLE_MS = 2e3;
|
|
183
183
|
var TYPING_CLEAR_DELAY_MS = 3e3;
|
|
184
|
+
var PRIVATE_TOPIC = { config: { private: true } };
|
|
184
185
|
var PresenceManager = class {
|
|
185
186
|
constructor(options) {
|
|
186
187
|
this.client = null;
|
|
@@ -204,7 +205,7 @@ var PresenceManager = class {
|
|
|
204
205
|
};
|
|
205
206
|
const channelName = `typing:${ticketId}`;
|
|
206
207
|
this.removeChannel(channelName);
|
|
207
|
-
const channel = this.client.channel(`ticket:${ticketId}
|
|
208
|
+
const channel = this.client.channel(`ticket:${ticketId}`, PRIVATE_TOPIC);
|
|
208
209
|
this.channels.set(channelName, channel);
|
|
209
210
|
channel.on("broadcast", { event: "typing" }, (data) => {
|
|
210
211
|
if (this.channels.get(channelName) !== channel) return;
|
|
@@ -256,7 +257,7 @@ var PresenceManager = class {
|
|
|
256
257
|
const sendChannelName = `typing-send:${ticketId}`;
|
|
257
258
|
let channel = this.channels.get(sendChannelName);
|
|
258
259
|
if (!channel) {
|
|
259
|
-
channel = this.client.channel(`ticket:${ticketId}
|
|
260
|
+
channel = this.client.channel(`ticket:${ticketId}`, PRIVATE_TOPIC);
|
|
260
261
|
channel.subscribe();
|
|
261
262
|
this.channels.set(sendChannelName, channel);
|
|
262
263
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { h as CommunityConfig, i as CommunityTopic, j as CommunityPostListParams, k as CommunityPost, l as CommunityPostDetail, m as CommunitySearchResult, C as CommunityCreatePostParams, a as CommunityCreatedPost, b as CommunityUpdatePostParams, c as CommunityUpdateCommentParams, d as CommunityCreatedComment, e as CommunityVoteResult, f as CommunityCreateCommentParams, g as CommunityFollowResult } from '../types-
|
|
2
|
-
export { n as CommunityComment } from '../types-
|
|
1
|
+
import { h as CommunityConfig, i as CommunityTopic, j as CommunityPostListParams, k as CommunityPost, l as CommunityPostDetail, m as CommunitySearchResult, C as CommunityCreatePostParams, a as CommunityCreatedPost, b as CommunityUpdatePostParams, c as CommunityUpdateCommentParams, d as CommunityCreatedComment, e as CommunityVoteResult, f as CommunityCreateCommentParams, g as CommunityFollowResult } from '../types-BHCahwev.cjs';
|
|
2
|
+
export { n as CommunityComment } from '../types-BHCahwev.cjs';
|
|
3
3
|
export { A as ApiErrorBody, C as CStarAuthenticationError, a as CStarConflictError, b as CStarError, c as CStarFeatureDisabledError, d as CStarNotFoundError, e as CStarPermissionError, f as CStarRateLimitError, g as CStarRealtimeUnavailableError, h as CStarServerError, i as CStarValidationError, E as ErrorType, p as parseError, j as parsePublicError } from '../errors-bW8CwTn4.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { h as CommunityConfig, i as CommunityTopic, j as CommunityPostListParams, k as CommunityPost, l as CommunityPostDetail, m as CommunitySearchResult, C as CommunityCreatePostParams, a as CommunityCreatedPost, b as CommunityUpdatePostParams, c as CommunityUpdateCommentParams, d as CommunityCreatedComment, e as CommunityVoteResult, f as CommunityCreateCommentParams, g as CommunityFollowResult } from '../types-
|
|
2
|
-
export { n as CommunityComment } from '../types-
|
|
1
|
+
import { h as CommunityConfig, i as CommunityTopic, j as CommunityPostListParams, k as CommunityPost, l as CommunityPostDetail, m as CommunitySearchResult, C as CommunityCreatePostParams, a as CommunityCreatedPost, b as CommunityUpdatePostParams, c as CommunityUpdateCommentParams, d as CommunityCreatedComment, e as CommunityVoteResult, f as CommunityCreateCommentParams, g as CommunityFollowResult } from '../types-BHCahwev.js';
|
|
2
|
+
export { n as CommunityComment } from '../types-BHCahwev.js';
|
|
3
3
|
export { A as ApiErrorBody, C as CStarAuthenticationError, a as CStarConflictError, b as CStarError, c as CStarFeatureDisabledError, d as CStarNotFoundError, e as CStarPermissionError, f as CStarRateLimitError, g as CStarRealtimeUnavailableError, h as CStarServerError, i as CStarValidationError, E as ErrorType, p as parseError, j as parsePublicError } from '../errors-bW8CwTn4.js';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/index.cjs
CHANGED
|
@@ -278,7 +278,7 @@ function parseError(statusCode, body, retryAfter) {
|
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
// src/client.ts
|
|
281
|
-
var SDK_VERSION = "0.18.
|
|
281
|
+
var SDK_VERSION = "0.18.2";
|
|
282
282
|
var DEFAULT_BASE_URL = "https://www.cstar.help";
|
|
283
283
|
var DEFAULT_API_VERSION = "2026-03-01";
|
|
284
284
|
var DEFAULT_MAX_RETRIES = 3;
|
|
@@ -357,10 +357,12 @@ var HttpClient = class {
|
|
|
357
357
|
const body = options?.body ? JSON.stringify(options.body) : void 0;
|
|
358
358
|
const canRetryAmbiguousFailure = !["POST", "PATCH"].includes(method.toUpperCase()) || !!options?.idempotencyKey;
|
|
359
359
|
let lastError = null;
|
|
360
|
+
const RATE_LIMIT_RETRY_CAP_MS = 6e4;
|
|
361
|
+
let inProgressWaitMs = null;
|
|
360
362
|
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
361
363
|
if (attempt > 0 && lastError) {
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
+
const delay = inProgressWaitMs ?? (lastError instanceof CStarRateLimitError ? Math.min(lastError.retryAfter * 1e3, RATE_LIMIT_RETRY_CAP_MS) : this.getBackoffDelay(attempt));
|
|
365
|
+
inProgressWaitMs = null;
|
|
364
366
|
await this.sleep(delay);
|
|
365
367
|
}
|
|
366
368
|
let response;
|
|
@@ -400,6 +402,14 @@ var HttpClient = class {
|
|
|
400
402
|
if (requestId && errorBody?.error) {
|
|
401
403
|
errorBody.error.request_id = requestId;
|
|
402
404
|
}
|
|
405
|
+
if (response.status === 409 && options?.idempotencyKey && errorBody?.error?.code === "idempotency_key_in_use" && retryAfter !== void 0) {
|
|
406
|
+
lastError = parseError(409, errorBody, retryAfter);
|
|
407
|
+
if (attempt < this.config.maxRetries) {
|
|
408
|
+
inProgressWaitMs = Math.min(retryAfter * 1e3, RATE_LIMIT_RETRY_CAP_MS);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
throw lastError;
|
|
412
|
+
}
|
|
403
413
|
if (response.status < 500 && response.status !== 429) {
|
|
404
414
|
throw parseError(response.status, errorBody, retryAfter);
|
|
405
415
|
}
|
|
@@ -1242,6 +1252,15 @@ var WebhooksResource = class extends BaseResource {
|
|
|
1242
1252
|
constructor(client) {
|
|
1243
1253
|
super(client, "/webhooks");
|
|
1244
1254
|
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Create a webhook endpoint. Its answer carries the signing secret, so the
|
|
1257
|
+
* API keeps it out of the Idempotency-Key cache: an `idempotencyKey` is
|
|
1258
|
+
* not sent, and a network error or 5xx is not retried, since the webhook
|
|
1259
|
+
* may already exist. List webhooks before creating it again.
|
|
1260
|
+
*/
|
|
1261
|
+
async create(params, _options) {
|
|
1262
|
+
return this.client.request("POST", this.basePath, { body: params });
|
|
1263
|
+
}
|
|
1245
1264
|
/** Send a test delivery to a webhook endpoint. */
|
|
1246
1265
|
async test(webhookId, params) {
|
|
1247
1266
|
return this.client.request(
|
|
@@ -1276,11 +1295,14 @@ var WebhooksResource = class extends BaseResource {
|
|
|
1276
1295
|
async unsubscribe(subscriptionId) {
|
|
1277
1296
|
await this.client.request("DELETE", `/webhooks/subscribe/${subscriptionId}`);
|
|
1278
1297
|
}
|
|
1279
|
-
/**
|
|
1280
|
-
|
|
1298
|
+
/**
|
|
1299
|
+
* Register a CLI listener (returns listenerId and signing secret). The API
|
|
1300
|
+
* ignores Idempotency-Key here (the answer is a secret), so an
|
|
1301
|
+
* `idempotencyKey` is not sent and a network error or 5xx is not retried.
|
|
1302
|
+
*/
|
|
1303
|
+
async listen(params, _options) {
|
|
1281
1304
|
return this.client.request("POST", "/webhooks/listen", {
|
|
1282
|
-
body: params
|
|
1283
|
-
idempotencyKey: options?.idempotencyKey
|
|
1305
|
+
body: params
|
|
1284
1306
|
});
|
|
1285
1307
|
}
|
|
1286
1308
|
/** Deregister a CLI listener. */
|
|
@@ -1868,10 +1890,9 @@ var ExportResource = class {
|
|
|
1868
1890
|
constructor(client) {
|
|
1869
1891
|
this.client = client;
|
|
1870
1892
|
}
|
|
1871
|
-
async create(params,
|
|
1893
|
+
async create(params, _options) {
|
|
1872
1894
|
return this.client.request("POST", "/export", {
|
|
1873
1895
|
body: params,
|
|
1874
|
-
idempotencyKey: options?.idempotencyKey,
|
|
1875
1896
|
responseType: params.format === "csv" ? "text" : "json"
|
|
1876
1897
|
});
|
|
1877
1898
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -772,6 +772,13 @@ interface WebhookTriggerTestResult {
|
|
|
772
772
|
/** Manage webhook endpoints. */
|
|
773
773
|
declare class WebhooksResource extends BaseResource<Webhook, WebhookListParams, WebhookCreateParams, WebhookUpdateParams> {
|
|
774
774
|
constructor(client: HttpClient);
|
|
775
|
+
/**
|
|
776
|
+
* Create a webhook endpoint. Its answer carries the signing secret, so the
|
|
777
|
+
* API keeps it out of the Idempotency-Key cache: an `idempotencyKey` is
|
|
778
|
+
* not sent, and a network error or 5xx is not retried, since the webhook
|
|
779
|
+
* may already exist. List webhooks before creating it again.
|
|
780
|
+
*/
|
|
781
|
+
create(params: WebhookCreateParams, _options?: RequestOptions): Promise<Webhook>;
|
|
775
782
|
/** Send a test delivery to a webhook endpoint. */
|
|
776
783
|
test(webhookId: string, params?: {
|
|
777
784
|
event?: WebhookEventType;
|
|
@@ -784,8 +791,12 @@ declare class WebhooksResource extends BaseResource<Webhook, WebhookListParams,
|
|
|
784
791
|
restSubscribe(params: WebhookSubscribeParams, options?: RequestOptions): Promise<ApiResponse<WebhookSubscription>>;
|
|
785
792
|
/** Unsubscribe from an event (REST Hooks / Zapier protocol). Returns void (204 No Content). */
|
|
786
793
|
unsubscribe(subscriptionId: string): Promise<void>;
|
|
787
|
-
/**
|
|
788
|
-
|
|
794
|
+
/**
|
|
795
|
+
* Register a CLI listener (returns listenerId and signing secret). The API
|
|
796
|
+
* ignores Idempotency-Key here (the answer is a secret), so an
|
|
797
|
+
* `idempotencyKey` is not sent and a network error or 5xx is not retried.
|
|
798
|
+
*/
|
|
799
|
+
listen(params: WebhookListenerParams, _options?: RequestOptions): Promise<ApiResponse<WebhookListener>>;
|
|
789
800
|
/** Deregister a CLI listener. */
|
|
790
801
|
unlisten(listenerId: string): Promise<ApiResponse<{
|
|
791
802
|
deleted: true;
|
|
@@ -1581,6 +1592,7 @@ interface Mention {
|
|
|
1581
1592
|
senderName: string | null;
|
|
1582
1593
|
recipientId: string;
|
|
1583
1594
|
recipientName: string | null;
|
|
1595
|
+
/** The ticket's prefixed id (`tkt_*`), or null for a mention with no ticket. */
|
|
1584
1596
|
ticketId: string | null;
|
|
1585
1597
|
ticketTitle: string | null;
|
|
1586
1598
|
isRead: boolean;
|
|
@@ -1674,6 +1686,11 @@ declare class AttachmentsResource {
|
|
|
1674
1686
|
type CustomFieldContentType = 'ticket' | 'customer' | 'article';
|
|
1675
1687
|
/** Input type of a custom field. */
|
|
1676
1688
|
type CustomFieldType = 'text' | 'number' | 'date' | 'select' | 'checkbox';
|
|
1689
|
+
/** One choice of a `select` field. A record stores its `value`. */
|
|
1690
|
+
interface CustomFieldOption {
|
|
1691
|
+
value: string;
|
|
1692
|
+
label: string;
|
|
1693
|
+
}
|
|
1677
1694
|
/** A custom field definition (mirrors `mapFieldToApi` on the server). */
|
|
1678
1695
|
interface CustomField {
|
|
1679
1696
|
id: string;
|
|
@@ -1683,7 +1700,7 @@ interface CustomField {
|
|
|
1683
1700
|
fieldType: CustomFieldType;
|
|
1684
1701
|
defaultValue: string | null;
|
|
1685
1702
|
/** Choices for `select` fields. */
|
|
1686
|
-
options:
|
|
1703
|
+
options: CustomFieldOption[] | null;
|
|
1687
1704
|
minValue: number | null;
|
|
1688
1705
|
maxValue: number | null;
|
|
1689
1706
|
required: boolean;
|
|
@@ -1702,7 +1719,11 @@ interface CustomFieldCreateParams {
|
|
|
1702
1719
|
/** Defaults to `text`. */
|
|
1703
1720
|
fieldType?: CustomFieldType;
|
|
1704
1721
|
defaultValue?: string;
|
|
1705
|
-
|
|
1722
|
+
/** Choices for `select` fields. A string is its own label. */
|
|
1723
|
+
options?: Array<string | {
|
|
1724
|
+
value: string;
|
|
1725
|
+
label?: string;
|
|
1726
|
+
}>;
|
|
1706
1727
|
minValue?: number;
|
|
1707
1728
|
maxValue?: number;
|
|
1708
1729
|
required?: boolean;
|
|
@@ -1712,7 +1733,11 @@ interface CustomFieldUpdateParams {
|
|
|
1712
1733
|
label?: string;
|
|
1713
1734
|
fieldType?: CustomFieldType;
|
|
1714
1735
|
defaultValue?: string | null;
|
|
1715
|
-
|
|
1736
|
+
/** Replaces the choices. A string is its own label. */
|
|
1737
|
+
options?: Array<string | {
|
|
1738
|
+
value: string;
|
|
1739
|
+
label?: string;
|
|
1740
|
+
}>;
|
|
1716
1741
|
minValue?: number | null;
|
|
1717
1742
|
maxValue?: number | null;
|
|
1718
1743
|
required?: boolean;
|
|
@@ -2032,17 +2057,25 @@ interface ImportCreateParams {
|
|
|
2032
2057
|
* A truthy `source.body` paired with an array at `conversation_parts` or
|
|
2033
2058
|
* `conversation_parts.conversation_parts` is also rejected, even if that
|
|
2034
2059
|
* array is empty. Empty history without this initial-message shape,
|
|
2035
|
-
* customFields
|
|
2060
|
+
* customFields and wrapped records with `action: 'skip'` are allowed (empty
|
|
2061
|
+
* history and `metadata` are ignored with a row warning). camelCase field
|
|
2062
|
+
* names (`customerEmail`, `externalId`) work; any field the import doesn't
|
|
2063
|
+
* read is named in `warnings`.
|
|
2036
2064
|
*/
|
|
2037
2065
|
records: Record<string, unknown>[];
|
|
2038
|
-
/**
|
|
2066
|
+
/**
|
|
2067
|
+
* What a customer row does when the team already has a customer with its
|
|
2068
|
+
* email: `'skip'` (the default) leaves that customer alone and reports the
|
|
2069
|
+
* row in `warnings`, `'overwrite'` updates it, `'merge'` fills only its
|
|
2070
|
+
* empty fields. A row repeating an earlier row's email is skipped.
|
|
2071
|
+
*/
|
|
2039
2072
|
mergeStrategy?: 'skip' | 'overwrite' | 'merge';
|
|
2040
2073
|
}
|
|
2041
2074
|
/** Import result. */
|
|
2042
2075
|
interface ImportResult {
|
|
2043
2076
|
object: 'import_result';
|
|
2044
|
-
/** ID of the
|
|
2045
|
-
logId: string
|
|
2077
|
+
/** ID of the import's log row (GET /import/logs/{logId}, `import.getLog()`). */
|
|
2078
|
+
logId: string;
|
|
2046
2079
|
/** Entity type imported — `ticket`, `customer`, or `article`. */
|
|
2047
2080
|
entityType: string;
|
|
2048
2081
|
/** True when every record imported without error. */
|
|
@@ -2066,21 +2099,35 @@ interface ImportResult {
|
|
|
2066
2099
|
interface ImportLog {
|
|
2067
2100
|
id: string;
|
|
2068
2101
|
object: 'import_log';
|
|
2069
|
-
|
|
2070
|
-
|
|
2102
|
+
/** `ticket`, `customer` or `article`. */
|
|
2103
|
+
entityType: string;
|
|
2104
|
+
/** `api` for REST imports, or the platform the dashboard importer read. */
|
|
2105
|
+
sourcePlatform: string | null;
|
|
2106
|
+
sourceFilename: string | null;
|
|
2071
2107
|
totalRecords: number;
|
|
2072
2108
|
created: number;
|
|
2073
2109
|
updated: number;
|
|
2074
2110
|
skipped: number;
|
|
2075
|
-
|
|
2111
|
+
failed: number;
|
|
2112
|
+
/** The failed rows, as "Row N: ..." lines, or null. */
|
|
2113
|
+
errors: string[] | null;
|
|
2114
|
+
matchKey: string | null;
|
|
2115
|
+
mergeStrategy: Record<string, string> | null;
|
|
2116
|
+
/** When the import finished; null while it runs. */
|
|
2117
|
+
completedAt: string | null;
|
|
2076
2118
|
createdAt: string;
|
|
2077
2119
|
}
|
|
2078
2120
|
/** Parameters for listing import logs. */
|
|
2079
2121
|
interface ImportLogListParams extends PaginationParams {
|
|
2080
|
-
|
|
2122
|
+
/** Only imports of this kind. */
|
|
2123
|
+
resource?: 'tickets' | 'customers' | 'articles';
|
|
2081
2124
|
}
|
|
2082
2125
|
|
|
2083
|
-
/**
|
|
2126
|
+
/**
|
|
2127
|
+
* Export data from your team. The API ignores Idempotency-Key on exports, so
|
|
2128
|
+
* `options.idempotencyKey` is accepted but not sent, and an export that
|
|
2129
|
+
* fails with a network error or 5xx is not retried.
|
|
2130
|
+
*/
|
|
2084
2131
|
declare class ExportResource {
|
|
2085
2132
|
private readonly client;
|
|
2086
2133
|
constructor(client: HttpClient);
|
package/dist/index.d.ts
CHANGED
|
@@ -772,6 +772,13 @@ interface WebhookTriggerTestResult {
|
|
|
772
772
|
/** Manage webhook endpoints. */
|
|
773
773
|
declare class WebhooksResource extends BaseResource<Webhook, WebhookListParams, WebhookCreateParams, WebhookUpdateParams> {
|
|
774
774
|
constructor(client: HttpClient);
|
|
775
|
+
/**
|
|
776
|
+
* Create a webhook endpoint. Its answer carries the signing secret, so the
|
|
777
|
+
* API keeps it out of the Idempotency-Key cache: an `idempotencyKey` is
|
|
778
|
+
* not sent, and a network error or 5xx is not retried, since the webhook
|
|
779
|
+
* may already exist. List webhooks before creating it again.
|
|
780
|
+
*/
|
|
781
|
+
create(params: WebhookCreateParams, _options?: RequestOptions): Promise<Webhook>;
|
|
775
782
|
/** Send a test delivery to a webhook endpoint. */
|
|
776
783
|
test(webhookId: string, params?: {
|
|
777
784
|
event?: WebhookEventType;
|
|
@@ -784,8 +791,12 @@ declare class WebhooksResource extends BaseResource<Webhook, WebhookListParams,
|
|
|
784
791
|
restSubscribe(params: WebhookSubscribeParams, options?: RequestOptions): Promise<ApiResponse<WebhookSubscription>>;
|
|
785
792
|
/** Unsubscribe from an event (REST Hooks / Zapier protocol). Returns void (204 No Content). */
|
|
786
793
|
unsubscribe(subscriptionId: string): Promise<void>;
|
|
787
|
-
/**
|
|
788
|
-
|
|
794
|
+
/**
|
|
795
|
+
* Register a CLI listener (returns listenerId and signing secret). The API
|
|
796
|
+
* ignores Idempotency-Key here (the answer is a secret), so an
|
|
797
|
+
* `idempotencyKey` is not sent and a network error or 5xx is not retried.
|
|
798
|
+
*/
|
|
799
|
+
listen(params: WebhookListenerParams, _options?: RequestOptions): Promise<ApiResponse<WebhookListener>>;
|
|
789
800
|
/** Deregister a CLI listener. */
|
|
790
801
|
unlisten(listenerId: string): Promise<ApiResponse<{
|
|
791
802
|
deleted: true;
|
|
@@ -1581,6 +1592,7 @@ interface Mention {
|
|
|
1581
1592
|
senderName: string | null;
|
|
1582
1593
|
recipientId: string;
|
|
1583
1594
|
recipientName: string | null;
|
|
1595
|
+
/** The ticket's prefixed id (`tkt_*`), or null for a mention with no ticket. */
|
|
1584
1596
|
ticketId: string | null;
|
|
1585
1597
|
ticketTitle: string | null;
|
|
1586
1598
|
isRead: boolean;
|
|
@@ -1674,6 +1686,11 @@ declare class AttachmentsResource {
|
|
|
1674
1686
|
type CustomFieldContentType = 'ticket' | 'customer' | 'article';
|
|
1675
1687
|
/** Input type of a custom field. */
|
|
1676
1688
|
type CustomFieldType = 'text' | 'number' | 'date' | 'select' | 'checkbox';
|
|
1689
|
+
/** One choice of a `select` field. A record stores its `value`. */
|
|
1690
|
+
interface CustomFieldOption {
|
|
1691
|
+
value: string;
|
|
1692
|
+
label: string;
|
|
1693
|
+
}
|
|
1677
1694
|
/** A custom field definition (mirrors `mapFieldToApi` on the server). */
|
|
1678
1695
|
interface CustomField {
|
|
1679
1696
|
id: string;
|
|
@@ -1683,7 +1700,7 @@ interface CustomField {
|
|
|
1683
1700
|
fieldType: CustomFieldType;
|
|
1684
1701
|
defaultValue: string | null;
|
|
1685
1702
|
/** Choices for `select` fields. */
|
|
1686
|
-
options:
|
|
1703
|
+
options: CustomFieldOption[] | null;
|
|
1687
1704
|
minValue: number | null;
|
|
1688
1705
|
maxValue: number | null;
|
|
1689
1706
|
required: boolean;
|
|
@@ -1702,7 +1719,11 @@ interface CustomFieldCreateParams {
|
|
|
1702
1719
|
/** Defaults to `text`. */
|
|
1703
1720
|
fieldType?: CustomFieldType;
|
|
1704
1721
|
defaultValue?: string;
|
|
1705
|
-
|
|
1722
|
+
/** Choices for `select` fields. A string is its own label. */
|
|
1723
|
+
options?: Array<string | {
|
|
1724
|
+
value: string;
|
|
1725
|
+
label?: string;
|
|
1726
|
+
}>;
|
|
1706
1727
|
minValue?: number;
|
|
1707
1728
|
maxValue?: number;
|
|
1708
1729
|
required?: boolean;
|
|
@@ -1712,7 +1733,11 @@ interface CustomFieldUpdateParams {
|
|
|
1712
1733
|
label?: string;
|
|
1713
1734
|
fieldType?: CustomFieldType;
|
|
1714
1735
|
defaultValue?: string | null;
|
|
1715
|
-
|
|
1736
|
+
/** Replaces the choices. A string is its own label. */
|
|
1737
|
+
options?: Array<string | {
|
|
1738
|
+
value: string;
|
|
1739
|
+
label?: string;
|
|
1740
|
+
}>;
|
|
1716
1741
|
minValue?: number | null;
|
|
1717
1742
|
maxValue?: number | null;
|
|
1718
1743
|
required?: boolean;
|
|
@@ -2032,17 +2057,25 @@ interface ImportCreateParams {
|
|
|
2032
2057
|
* A truthy `source.body` paired with an array at `conversation_parts` or
|
|
2033
2058
|
* `conversation_parts.conversation_parts` is also rejected, even if that
|
|
2034
2059
|
* array is empty. Empty history without this initial-message shape,
|
|
2035
|
-
* customFields
|
|
2060
|
+
* customFields and wrapped records with `action: 'skip'` are allowed (empty
|
|
2061
|
+
* history and `metadata` are ignored with a row warning). camelCase field
|
|
2062
|
+
* names (`customerEmail`, `externalId`) work; any field the import doesn't
|
|
2063
|
+
* read is named in `warnings`.
|
|
2036
2064
|
*/
|
|
2037
2065
|
records: Record<string, unknown>[];
|
|
2038
|
-
/**
|
|
2066
|
+
/**
|
|
2067
|
+
* What a customer row does when the team already has a customer with its
|
|
2068
|
+
* email: `'skip'` (the default) leaves that customer alone and reports the
|
|
2069
|
+
* row in `warnings`, `'overwrite'` updates it, `'merge'` fills only its
|
|
2070
|
+
* empty fields. A row repeating an earlier row's email is skipped.
|
|
2071
|
+
*/
|
|
2039
2072
|
mergeStrategy?: 'skip' | 'overwrite' | 'merge';
|
|
2040
2073
|
}
|
|
2041
2074
|
/** Import result. */
|
|
2042
2075
|
interface ImportResult {
|
|
2043
2076
|
object: 'import_result';
|
|
2044
|
-
/** ID of the
|
|
2045
|
-
logId: string
|
|
2077
|
+
/** ID of the import's log row (GET /import/logs/{logId}, `import.getLog()`). */
|
|
2078
|
+
logId: string;
|
|
2046
2079
|
/** Entity type imported — `ticket`, `customer`, or `article`. */
|
|
2047
2080
|
entityType: string;
|
|
2048
2081
|
/** True when every record imported without error. */
|
|
@@ -2066,21 +2099,35 @@ interface ImportResult {
|
|
|
2066
2099
|
interface ImportLog {
|
|
2067
2100
|
id: string;
|
|
2068
2101
|
object: 'import_log';
|
|
2069
|
-
|
|
2070
|
-
|
|
2102
|
+
/** `ticket`, `customer` or `article`. */
|
|
2103
|
+
entityType: string;
|
|
2104
|
+
/** `api` for REST imports, or the platform the dashboard importer read. */
|
|
2105
|
+
sourcePlatform: string | null;
|
|
2106
|
+
sourceFilename: string | null;
|
|
2071
2107
|
totalRecords: number;
|
|
2072
2108
|
created: number;
|
|
2073
2109
|
updated: number;
|
|
2074
2110
|
skipped: number;
|
|
2075
|
-
|
|
2111
|
+
failed: number;
|
|
2112
|
+
/** The failed rows, as "Row N: ..." lines, or null. */
|
|
2113
|
+
errors: string[] | null;
|
|
2114
|
+
matchKey: string | null;
|
|
2115
|
+
mergeStrategy: Record<string, string> | null;
|
|
2116
|
+
/** When the import finished; null while it runs. */
|
|
2117
|
+
completedAt: string | null;
|
|
2076
2118
|
createdAt: string;
|
|
2077
2119
|
}
|
|
2078
2120
|
/** Parameters for listing import logs. */
|
|
2079
2121
|
interface ImportLogListParams extends PaginationParams {
|
|
2080
|
-
|
|
2122
|
+
/** Only imports of this kind. */
|
|
2123
|
+
resource?: 'tickets' | 'customers' | 'articles';
|
|
2081
2124
|
}
|
|
2082
2125
|
|
|
2083
|
-
/**
|
|
2126
|
+
/**
|
|
2127
|
+
* Export data from your team. The API ignores Idempotency-Key on exports, so
|
|
2128
|
+
* `options.idempotencyKey` is accepted but not sent, and an export that
|
|
2129
|
+
* fails with a network error or 5xx is not retried.
|
|
2130
|
+
*/
|
|
2084
2131
|
declare class ExportResource {
|
|
2085
2132
|
private readonly client;
|
|
2086
2133
|
constructor(client: HttpClient);
|
package/dist/index.js
CHANGED
|
@@ -237,7 +237,7 @@ function parseError(statusCode, body, retryAfter) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
// src/client.ts
|
|
240
|
-
var SDK_VERSION = "0.18.
|
|
240
|
+
var SDK_VERSION = "0.18.2";
|
|
241
241
|
var DEFAULT_BASE_URL = "https://www.cstar.help";
|
|
242
242
|
var DEFAULT_API_VERSION = "2026-03-01";
|
|
243
243
|
var DEFAULT_MAX_RETRIES = 3;
|
|
@@ -316,10 +316,12 @@ var HttpClient = class {
|
|
|
316
316
|
const body = options?.body ? JSON.stringify(options.body) : void 0;
|
|
317
317
|
const canRetryAmbiguousFailure = !["POST", "PATCH"].includes(method.toUpperCase()) || !!options?.idempotencyKey;
|
|
318
318
|
let lastError = null;
|
|
319
|
+
const RATE_LIMIT_RETRY_CAP_MS = 6e4;
|
|
320
|
+
let inProgressWaitMs = null;
|
|
319
321
|
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
320
322
|
if (attempt > 0 && lastError) {
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
+
const delay = inProgressWaitMs ?? (lastError instanceof CStarRateLimitError ? Math.min(lastError.retryAfter * 1e3, RATE_LIMIT_RETRY_CAP_MS) : this.getBackoffDelay(attempt));
|
|
324
|
+
inProgressWaitMs = null;
|
|
323
325
|
await this.sleep(delay);
|
|
324
326
|
}
|
|
325
327
|
let response;
|
|
@@ -359,6 +361,14 @@ var HttpClient = class {
|
|
|
359
361
|
if (requestId && errorBody?.error) {
|
|
360
362
|
errorBody.error.request_id = requestId;
|
|
361
363
|
}
|
|
364
|
+
if (response.status === 409 && options?.idempotencyKey && errorBody?.error?.code === "idempotency_key_in_use" && retryAfter !== void 0) {
|
|
365
|
+
lastError = parseError(409, errorBody, retryAfter);
|
|
366
|
+
if (attempt < this.config.maxRetries) {
|
|
367
|
+
inProgressWaitMs = Math.min(retryAfter * 1e3, RATE_LIMIT_RETRY_CAP_MS);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
throw lastError;
|
|
371
|
+
}
|
|
362
372
|
if (response.status < 500 && response.status !== 429) {
|
|
363
373
|
throw parseError(response.status, errorBody, retryAfter);
|
|
364
374
|
}
|
|
@@ -1201,6 +1211,15 @@ var WebhooksResource = class extends BaseResource {
|
|
|
1201
1211
|
constructor(client) {
|
|
1202
1212
|
super(client, "/webhooks");
|
|
1203
1213
|
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Create a webhook endpoint. Its answer carries the signing secret, so the
|
|
1216
|
+
* API keeps it out of the Idempotency-Key cache: an `idempotencyKey` is
|
|
1217
|
+
* not sent, and a network error or 5xx is not retried, since the webhook
|
|
1218
|
+
* may already exist. List webhooks before creating it again.
|
|
1219
|
+
*/
|
|
1220
|
+
async create(params, _options) {
|
|
1221
|
+
return this.client.request("POST", this.basePath, { body: params });
|
|
1222
|
+
}
|
|
1204
1223
|
/** Send a test delivery to a webhook endpoint. */
|
|
1205
1224
|
async test(webhookId, params) {
|
|
1206
1225
|
return this.client.request(
|
|
@@ -1235,11 +1254,14 @@ var WebhooksResource = class extends BaseResource {
|
|
|
1235
1254
|
async unsubscribe(subscriptionId) {
|
|
1236
1255
|
await this.client.request("DELETE", `/webhooks/subscribe/${subscriptionId}`);
|
|
1237
1256
|
}
|
|
1238
|
-
/**
|
|
1239
|
-
|
|
1257
|
+
/**
|
|
1258
|
+
* Register a CLI listener (returns listenerId and signing secret). The API
|
|
1259
|
+
* ignores Idempotency-Key here (the answer is a secret), so an
|
|
1260
|
+
* `idempotencyKey` is not sent and a network error or 5xx is not retried.
|
|
1261
|
+
*/
|
|
1262
|
+
async listen(params, _options) {
|
|
1240
1263
|
return this.client.request("POST", "/webhooks/listen", {
|
|
1241
|
-
body: params
|
|
1242
|
-
idempotencyKey: options?.idempotencyKey
|
|
1264
|
+
body: params
|
|
1243
1265
|
});
|
|
1244
1266
|
}
|
|
1245
1267
|
/** Deregister a CLI listener. */
|
|
@@ -1827,10 +1849,9 @@ var ExportResource = class {
|
|
|
1827
1849
|
constructor(client) {
|
|
1828
1850
|
this.client = client;
|
|
1829
1851
|
}
|
|
1830
|
-
async create(params,
|
|
1852
|
+
async create(params, _options) {
|
|
1831
1853
|
return this.client.request("POST", "/export", {
|
|
1832
1854
|
body: params,
|
|
1833
|
-
idempotencyKey: options?.idempotencyKey,
|
|
1834
1855
|
responseType: params.format === "csv" ? "text" : "json"
|
|
1835
1856
|
});
|
|
1836
1857
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Attachment metadata for community posts and comments (images only, max
|
|
1
|
+
/** Attachment metadata for community posts and comments (images only, max 4 MB each). */
|
|
2
2
|
interface CommunityAttachment {
|
|
3
3
|
filename: string;
|
|
4
4
|
content_type: string;
|
|
@@ -30,7 +30,7 @@ interface CommunityCreatePostParams {
|
|
|
30
30
|
topicId: string;
|
|
31
31
|
/** Custom field values keyed by field definition ID. */
|
|
32
32
|
customFields?: Record<string, string>;
|
|
33
|
-
/** Image attachments (max 10, images only,
|
|
33
|
+
/** Image attachments (max 10, images only, 4 MB each). */
|
|
34
34
|
attachments?: CommunityAttachment[];
|
|
35
35
|
}
|
|
36
36
|
/** Parameters for updating a post as the original author. */
|
|
@@ -53,7 +53,7 @@ interface CommunityCreateCommentParams {
|
|
|
53
53
|
body: string;
|
|
54
54
|
/** Parent comment ID for replies (optional, 1-level nesting). */
|
|
55
55
|
parentCommentId?: string | null;
|
|
56
|
-
/** Image attachments (max 5, images only,
|
|
56
|
+
/** Image attachments (max 5, images only, 4 MB each). */
|
|
57
57
|
attachments?: CommunityAttachment[];
|
|
58
58
|
}
|
|
59
59
|
/** Response from creating a post. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Attachment metadata for community posts and comments (images only, max
|
|
1
|
+
/** Attachment metadata for community posts and comments (images only, max 4 MB each). */
|
|
2
2
|
interface CommunityAttachment {
|
|
3
3
|
filename: string;
|
|
4
4
|
content_type: string;
|
|
@@ -30,7 +30,7 @@ interface CommunityCreatePostParams {
|
|
|
30
30
|
topicId: string;
|
|
31
31
|
/** Custom field values keyed by field definition ID. */
|
|
32
32
|
customFields?: Record<string, string>;
|
|
33
|
-
/** Image attachments (max 10, images only,
|
|
33
|
+
/** Image attachments (max 10, images only, 4 MB each). */
|
|
34
34
|
attachments?: CommunityAttachment[];
|
|
35
35
|
}
|
|
36
36
|
/** Parameters for updating a post as the original author. */
|
|
@@ -53,7 +53,7 @@ interface CommunityCreateCommentParams {
|
|
|
53
53
|
body: string;
|
|
54
54
|
/** Parent comment ID for replies (optional, 1-level nesting). */
|
|
55
55
|
parentCommentId?: string | null;
|
|
56
|
-
/** Image attachments (max 5, images only,
|
|
56
|
+
/** Image attachments (max 5, images only, 4 MB each). */
|
|
57
57
|
attachments?: CommunityAttachment[];
|
|
58
58
|
}
|
|
59
59
|
/** Response from creating a post. */
|