deliveryapi 0.2.2 → 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/dist/index.d.cts CHANGED
@@ -1,442 +1,974 @@
1
- interface ApiResponse<T = unknown> {
2
- isSuccess: boolean;
3
- data?: T;
4
- message?: string;
5
- error?: string;
6
- statusCode?: number;
7
- errorCode?: string;
8
- }
9
- interface RequestVerificationParams {
10
- email: string;
11
- }
12
- interface VerifyAndRegisterParams {
13
- email: string;
14
- code: string;
15
- password: string;
16
- displayName: string;
17
- }
18
- interface VerifyAndRegisterResult {
19
- customToken: string;
20
- }
21
- interface EnsureUserResult {
22
- uid: string;
23
- email: string;
24
- displayName: string;
25
- isNewUser: boolean;
26
- }
27
- interface DashboardApiKey {
28
- name: string;
1
+ /**
2
+ * 택배 배송 상태 코드 (정규화된 통합 상태)
3
+ *
4
+ * 모든 택배사의 상태를 하나의 공통 코드로 변환합니다.
5
+ *
6
+ * @example
7
+ * if (result.deliveryStatus === CourierDeliveryStatus.DELIVERED) {
8
+ * console.log('배송 완료')
9
+ * }
10
+ */
11
+ declare enum CourierDeliveryStatus {
12
+ /** 접수 대기 */
13
+ PENDING = "PENDING",
14
+ /** 접수 완료 */
15
+ REGISTERED = "REGISTERED",
16
+ /** 집하 준비 */
17
+ PICKUP_READY = "PICKUP_READY",
18
+ /** 집하 완료 */
19
+ PICKED_UP = "PICKED_UP",
20
+ /** 배송 중 (간선 이동) */
21
+ IN_TRANSIT = "IN_TRANSIT",
22
+ /** 배송 출발 (배달기사 출발) */
23
+ OUT_FOR_DELIVERY = "OUT_FOR_DELIVERY",
24
+ /** 배송 완료 */
25
+ DELIVERED = "DELIVERED",
26
+ /** 배송 실패 */
27
+ FAILED = "FAILED",
28
+ /** 반송 */
29
+ RETURNED = "RETURNED",
30
+ /** 취소 */
31
+ CANCELLED = "CANCELLED",
32
+ /** 보류 */
33
+ HOLD = "HOLD",
34
+ /** 알 수 없음 */
35
+ UNKNOWN = "UNKNOWN"
36
+ }
37
+ /**
38
+ * API 에러 코드
39
+ *
40
+ * 에러 응답의 `errorCode` 필드에 포함됩니다.
41
+ * 이 코드를 기준으로 클라이언트 분기 처리를 구현하세요.
42
+ */
43
+ type ApiErrorCode = 'UNAUTHORIZED' | 'FORBIDDEN' | 'RATE_LIMITED' | 'MISSING_PARAMS' | 'INVALID_PARAMS' | 'NOT_FOUND' | 'CONFLICT' | 'EXPIRED' | 'COURIER_OTP_REQUIRED' | 'COURIER_AUTH_FAILED' | 'WEBHOOK_INVALID_SIGNATURE' | 'WEBHOOK_ENDPOINT_LIMIT' | 'INTERNAL_ERROR';
44
+ /**
45
+ * 택배 조회 아이템별 에러 코드
46
+ *
47
+ * `trace()` 응답의 `results[].error.code`에 포함됩니다.
48
+ */
49
+ type TrackingErrorCode = 'MISSING_PARAMS' | 'INVALID_TRACKING_NUMBER' | 'UNSUPPORTED_COURIER' | 'NOT_FOUND' | 'TRACKING_FAILED';
50
+ /**
51
+ * 배송 진행 내역 한 건
52
+ */
53
+ interface TrackingProgress {
54
+ /** 처리 시간 (ISO 8601) */
55
+ dateTime: string;
56
+ /** 처리 위치 (예: "서울 허브") */
57
+ location: string;
58
+ /** 택배사 원본 상태 텍스트 */
29
59
  status: string;
30
- environment: string;
31
- secretKeyCount: number;
32
- usage: {
33
- today: number;
34
- thisMonth: number;
35
- };
36
- }
37
- interface DashboardResult {
38
- user: {
39
- email: string;
40
- displayName: string;
41
- };
42
- customer: {
43
- totalRequests: number;
44
- } | null;
45
- apiKeys: DashboardApiKey[];
46
- plan: {
47
- name: string;
48
- tier: string;
49
- limits: {
50
- requestsPerMinute: number;
51
- requestsPerMonth: number;
52
- };
53
- };
54
- }
55
- interface IssueApiKeyResult {
56
- apiKey: string;
57
- secretKey: string;
58
- emailSent: boolean;
59
- }
60
- interface Post {
61
- id: string;
62
- title: string;
63
- content?: string;
64
- authorUid: string;
65
- authorName: string;
66
- isSecret: boolean;
67
- commentCount: number;
68
- viewCount: number;
69
- dateCreated: string | null;
70
- dateModified?: string | null;
71
- }
72
- interface Comment {
73
- id: string;
74
- postId?: string;
75
- content: string;
76
- authorUid: string;
77
- authorName: string;
78
- isAdmin: boolean;
79
- dateCreated: string | null;
80
- }
81
- interface ListPostsParams {
82
- cursor?: string;
83
- pageSize?: number;
84
- }
85
- interface ListPostsResult {
86
- posts: Post[];
87
- nextCursor: string | null;
88
- hasMore: boolean;
89
- }
90
- interface CreatePostParams {
91
- title: string;
92
- content: string;
93
- isSecret?: boolean;
94
- }
95
- interface UpdatePostParams {
96
- title?: string;
97
- content?: string;
98
- isSecret?: boolean;
99
- }
100
- interface ListCommentsResult {
101
- comments: Comment[];
102
- }
103
- interface CreateCommentParams {
104
- content: string;
105
- }
106
- interface ContactInquiryParams {
107
- name: string;
108
- email: string;
109
- phone?: string;
110
- projectType: string;
111
- budget?: string;
112
- timeline?: string;
113
- description: string;
114
- }
115
- interface PlanInfo {
116
- tier: string;
117
- name: string;
118
- price: number;
119
- limits: {
120
- secretKey: {
121
- maxRequestsPerMinute: number;
122
- maxRequestsPerMonth: number;
123
- };
60
+ /** 정규화된 상태 코드 */
61
+ statusCode?: CourierDeliveryStatus;
62
+ /** 상세 설명 */
63
+ description?: string;
64
+ /** 연락처 */
65
+ telno?: string;
66
+ /** 추가 연락처 */
67
+ telno2?: string;
68
+ }
69
+ /**
70
+ * 통합 택배 조회 결과 (단건)
71
+ */
72
+ interface UnifiedTrackingResponse {
73
+ /** 송장번호 */
74
+ trackingNumber: string;
75
+ /** 택배사 코드 (예: `'cj'`, `'lotte'`) */
76
+ courierCode: string;
77
+ /** 택배사 이름 (예: `'CJ대한통운'`) */
78
+ courierName: string;
79
+ /** 정규화된 배송 상태 코드 */
80
+ deliveryStatus: CourierDeliveryStatus;
81
+ /** 택배사 원본 상태 텍스트 */
82
+ deliveryStatusText: string;
83
+ /** 배송 완료 여부 */
84
+ isDelivered: boolean;
85
+ /** 발송인 이름 */
86
+ senderName?: string;
87
+ /** 수취인 이름 */
88
+ receiverName?: string;
89
+ /** 수취인 주소 */
90
+ receiverAddress?: string;
91
+ /** 상품명 */
92
+ productName?: string;
93
+ /** 수량 */
94
+ productQuantity?: number;
95
+ /** 배송 완료일 (ISO 8601, 완료 시에만 존재) */
96
+ dateDelivered?: string;
97
+ /** 배송 예정일 (ISO 8601) */
98
+ dateEstimated?: string;
99
+ /** 마지막 진행 날짜/시간 (`yyyy-MM-dd HH:mm:ss`) */
100
+ dateLastProgress: string;
101
+ /** 배송 진행 내역 (최신순) */
102
+ progresses: TrackingProgress[];
103
+ /** 조회 시점 (ISO 8601) */
104
+ queriedAt: string;
105
+ }
106
+ /**
107
+ * 단건 조회 항목
108
+ */
109
+ interface TraceItem {
110
+ /** 택배사 코드 (예: `'cj'`, `'lotte'`, `'hanjin'`) */
111
+ courierCode: string;
112
+ /** 송장번호 */
113
+ trackingNumber: string;
114
+ /**
115
+ * 클라이언트 매핑 ID (선택)
116
+ *
117
+ * 주문번호 내부 식별자를 넣으면 응답에 그대로 반환됩니다.
118
+ */
119
+ clientId?: string;
120
+ }
121
+ /**
122
+ * `tracking.trace()` 파라미터
123
+ *
124
+ * @example
125
+ * const result = await client.tracking.trace({
126
+ * items: [
127
+ * { courierCode: 'cj', trackingNumber: '1234567890', clientId: 'order_001' }
128
+ * ],
129
+ * includeProgresses: true,
130
+ * })
131
+ */
132
+ interface TraceParams {
133
+ /** 조회할 택배 목록 (1건도 배열로 전달) */
134
+ items: TraceItem[];
135
+ /**
136
+ * 배송 진행 내역 포함 여부
137
+ *
138
+ * `false`로 설정하면 `progresses`가 빈 배열로 반환됩니다.
139
+ * 상태만 확인할 때 사용하면 응답 크기가 줄어듭니다.
140
+ * @default true
141
+ */
142
+ includeProgresses?: boolean;
143
+ }
144
+ /** 아이템별 조회 에러 */
145
+ interface TraceItemError {
146
+ /** 에러 코드 */
147
+ code: TrackingErrorCode;
148
+ /** 에러 메시지 */
149
+ message: string;
150
+ /** 택배사 코드 */
151
+ courierCode: string;
152
+ /** 송장번호 */
153
+ trackingNumber: string;
154
+ /**
155
+ * 과금 여부
156
+ *
157
+ * `true`이면 이 건은 사용량으로 집계됩니다.
158
+ * `NOT_FOUND` 에러만 과금됩니다.
159
+ */
160
+ billable: boolean;
161
+ }
162
+ /** 아이템별 캐시 정보 */
163
+ interface TraceCacheInfo {
164
+ /** 캐시에서 반환된 경우 `true` */
165
+ fromCache: boolean;
166
+ /** 캐시 저장 시점 (ISO 8601, 캐시 히트 시에만 존재) */
167
+ cachedAt?: string;
168
+ }
169
+ /** 단건 택배 조회 결과 */
170
+ interface TraceResult {
171
+ /** 요청 시 전달된 `clientId` (있으면 그대로 반환) */
172
+ clientId?: string;
173
+ /** 조회 성공 여부 */
174
+ success: boolean;
175
+ /** 성공 시 배송 정보 */
176
+ data?: UnifiedTrackingResponse;
177
+ /** 실패 시 에러 정보 */
178
+ error?: TraceItemError;
179
+ /** 캐시 정보 */
180
+ cache?: TraceCacheInfo;
181
+ }
182
+ /** `tracking.trace()` 응답 */
183
+ interface TraceResponse {
184
+ /** 아이템별 결과 (요청 순서와 동일) */
185
+ results: TraceResult[];
186
+ /** 집계 요약 */
187
+ summary: {
188
+ /** 전체 요청 건수 */
189
+ total: number;
190
+ /** 성공 건수 */
191
+ successful: number;
192
+ /** 실패 건수 */
193
+ failed: number;
194
+ /** 과금 대상 건수 (성공 + `NOT_FOUND`) */
195
+ billable: number;
124
196
  };
125
197
  }
126
- interface SecretPreset {
127
- id: string;
128
- name: string;
129
- description: string;
130
- }
131
- interface AdminStats {
132
- totalHpUsers: number;
133
- totalCustomers: number;
134
- totalApiKeys: number;
135
- totalRequests: number;
136
- todaySignups: number;
137
- planCounts: Record<string, number>;
138
- }
139
- interface DailySignup {
140
- date: string;
141
- count: number;
142
- }
143
- interface AdminUser {
144
- uid: string;
145
- email: string;
198
+ /** 지원 택배사 정보 */
199
+ interface CourierInfo {
200
+ /** 택배사 코드 (예: `'cj'`, `'lotte'`) */
201
+ trackingApiCode: string;
202
+ /** 택배사 표시명 (예: `'CJ대한통운'`) */
146
203
  displayName: string;
147
- provider: string;
148
- currentPlan: string;
149
- isAdmin: boolean;
150
- dateCreated: string | null;
151
204
  }
152
- interface ListAdminUsersResult {
153
- users: AdminUser[];
205
+ /** `tracking.getCouriers()` 응답 */
206
+ interface GetCouriersResponse {
207
+ /** 지원 택배사 목록 */
208
+ couriers: CourierInfo[];
209
+ /** 전체 택배사 수 */
154
210
  total: number;
155
211
  }
156
- interface UpdateAdminUserParams {
157
- displayName?: string;
158
- currentPlan?: string;
159
- isAdmin?: boolean;
160
- }
161
- interface AdminSecretKeyDetail {
162
- secretKeyHash: string;
163
- name: string;
164
- status: string;
165
- usage: string;
166
- limits: {
167
- requestsPerMinute: number;
168
- requestsPerHour: number;
169
- requestsPerDay: number;
170
- requestsPerMonth: number;
171
- byOperation: {
172
- total: {
173
- requestsPerMinute: number;
174
- requestsPerHour: number;
175
- requestsPerDay: number;
176
- requestsPerMonth: number;
177
- };
178
- read: {
179
- requestsPerMinute: number;
180
- requestsPerHour: number;
181
- requestsPerDay: number;
182
- requestsPerMonth: number;
183
- };
184
- write: {
185
- requestsPerMinute: number;
186
- requestsPerHour: number;
187
- requestsPerDay: number;
188
- requestsPerMonth: number;
189
- };
190
- } | null;
191
- };
192
- todayCount: number;
193
- thisMonthCount: number;
194
- }
195
- interface AdminApiKeyDetail {
196
- apiKeyHash: string;
197
- apiKey: string;
198
- name: string;
199
- status: string;
200
- environment: string;
201
- planTier: string;
202
- secretKeys: AdminSecretKeyDetail[];
203
- usage: {
204
- today: number;
205
- thisMonth: number;
206
- };
207
- }
208
- interface AdminUserDetail {
209
- user: {
210
- uid: string;
211
- email: string;
212
- displayName: string;
213
- provider: string;
214
- currentPlan: string;
215
- isAdmin: boolean;
216
- customerId: string | null;
217
- dateCreated: string | null;
218
- };
219
- apiKeys: AdminApiKeyDetail[];
220
- planLimits: {
221
- name: string;
222
- maxRequestsPerMinute: number;
223
- maxRequestsPerHour: number;
224
- maxRequestsPerDay: number;
225
- maxRequestsPerMonth: number;
226
- };
212
+ /**
213
+ * `webhooks.createEndpoint()` 파라미터
214
+ *
215
+ * @example
216
+ * const endpoint = await client.webhooks.createEndpoint({
217
+ * url: 'https://my-server.com/webhook',
218
+ * name: '운영 서버',
219
+ * })
220
+ * // endpoint.webhookSecret 은 이 응답에서만 확인 가능합니다 — 안전하게 보관하세요!
221
+ */
222
+ interface CreateEndpointParams {
223
+ /**
224
+ * 웹훅을 수신할 URL
225
+ *
226
+ * `https://` 로 시작해야 합니다.
227
+ * 등록 시 서버에서 테스트 POST 요청을 전송하여 URL을 검증합니다.
228
+ */
229
+ url: string;
230
+ /** 엔드포인트 이름 (선택, 관리용) */
231
+ name?: string;
232
+ /**
233
+ * 서명 시크릿 직접 지정 (선택)
234
+ *
235
+ * 미제공 시 서버가 랜덤 시크릿을 생성합니다.
236
+ * 최소 5자 이상이어야 합니다.
237
+ */
238
+ webhookSecret?: string;
239
+ }
240
+ /** `webhooks.createEndpoint()` 응답 */
241
+ interface CreateEndpointResponse {
242
+ /** 생성된 엔드포인트 ID */
243
+ endpointId: string;
244
+ /** 등록된 URL */
245
+ url: string;
246
+ /** 엔드포인트 이름 */
247
+ name?: string;
248
+ /**
249
+ * 웹훅 서명 시크릿
250
+ *
251
+ * **이 응답에서만 평문으로 반환됩니다. 이후에는 조회 불가합니다.**
252
+ * 분실 시 `rotateSecret()`으로 재발급해야 합니다.
253
+ *
254
+ * 수신된 웹훅의 `X-Webhook-Signature` 헤더를 이 값으로 HMAC-SHA256 검증하세요.
255
+ */
256
+ webhookSecret: string;
257
+ /** 생성 시각 (ISO 8601) */
258
+ dateCreated: string;
227
259
  }
228
- interface AdminCustomer {
260
+ /** 엔드포인트 목록 아이템 */
261
+ interface EndpointInfo {
262
+ /** 엔드포인트 ID */
229
263
  id: string;
230
- email: string;
231
- currentPlan: string;
232
- status: string;
233
- activeApiKeys: number;
234
- totalRequests: number;
235
- dateCreated: string | null;
236
- }
237
- interface ListAdminCustomersResult {
238
- customers: AdminCustomer[];
239
- total: number;
240
- }
241
- interface AdminKeyItem {
242
- apiKeyHash: string;
243
- apiKey: string;
244
- name: string;
245
- status: string;
246
- environment: string;
247
- planTier: string;
248
- customerEmail: string;
249
- hpUserId: string | null;
250
- secretKeyCount: number;
251
- todayCount: number;
252
- thisMonthCount: number;
253
- dateCreated: string | null;
254
- }
255
- interface ListAdminKeysResult {
256
- keys: AdminKeyItem[];
264
+ /** 웹훅 수신 URL */
265
+ url: string;
266
+ /** 엔드포인트 이름 */
267
+ name?: string;
268
+ /**
269
+ * 엔드포인트 상태
270
+ *
271
+ * 연속 5회 이상 전송 실패 시 자동으로 `inactive` 로 전환됩니다.
272
+ */
273
+ status: 'active' | 'inactive';
274
+ /**
275
+ * 연속 실패 횟수
276
+ *
277
+ * 5회 초과 시 엔드포인트가 비활성화됩니다.
278
+ */
279
+ consecutiveFailures: number;
280
+ /** 생성 시각 (ISO 8601) */
281
+ dateCreated: string;
282
+ /** 최종 수정 시각 (ISO 8601) */
283
+ dateModified: string;
284
+ }
285
+ /** `webhooks.listEndpoints()` 응답 */
286
+ interface ListEndpointsResponse {
287
+ /** 등록된 엔드포인트 목록 */
288
+ endpoints: EndpointInfo[];
289
+ /** 전체 수 */
257
290
  total: number;
258
291
  }
259
- interface UpdateKeyPlanParams {
260
- planTier: string;
261
- }
262
- interface BulkUpdateKeyPlanParams {
263
- apiKeyHashes: string[];
264
- planTier: string;
265
- }
266
- interface KeyLimits {
267
- requestsPerMinute?: number;
268
- requestsPerHour?: number;
269
- requestsPerDay?: number;
270
- requestsPerMonth?: number;
271
- byOperation?: {
272
- total?: {
273
- requestsPerMinute?: number;
274
- requestsPerHour?: number;
275
- requestsPerDay?: number;
276
- requestsPerMonth?: number;
277
- };
278
- read?: {
279
- requestsPerMinute?: number;
280
- requestsPerHour?: number;
281
- requestsPerDay?: number;
282
- requestsPerMonth?: number;
283
- };
284
- write?: {
285
- requestsPerMinute?: number;
286
- requestsPerHour?: number;
287
- requestsPerDay?: number;
288
- requestsPerMonth?: number;
289
- };
290
- };
291
- }
292
- interface UpdateKeyLimitsParams {
293
- secretKeyHash: string;
294
- limits: KeyLimits;
295
- }
296
- interface AdminDemoKey {
297
- apiKeyHash: string;
298
- apiKey: string;
299
- secretKey?: string;
292
+ /**
293
+ * `webhooks.updateEndpoint()` 파라미터
294
+ *
295
+ * URL은 변경할 수 없습니다. 이름만 수정 가능합니다.
296
+ */
297
+ interface UpdateEndpointParams {
298
+ /** 새 엔드포인트 이름 */
300
299
  name: string;
301
- status: string;
302
- environment: string;
303
- planTier: string;
304
- dateCreated: string | null;
305
300
  }
306
- interface CreateDemoKeyParams {
307
- name?: string;
308
- planTier?: string;
309
- }
310
- interface ListAdminDemoKeysResult {
311
- keys: AdminDemoKey[];
312
- }
313
- interface TrackingLog {
314
- id: string;
315
- apiKeyHash: string;
316
- customerEmail: string;
317
- trackingNumber: string;
301
+ /**
302
+ * `webhooks.rotateSecret()` 파라미터
303
+ */
304
+ interface RotateSecretParams {
305
+ /**
306
+ * 새 시크릿 직접 지정 (선택)
307
+ *
308
+ * 미제공 시 서버가 랜덤 시크릿을 생성합니다.
309
+ */
310
+ webhookSecret?: string;
311
+ }
312
+ /** `webhooks.rotateSecret()` 응답 */
313
+ interface RotateSecretResponse {
314
+ /** 엔드포인트 ID */
315
+ endpointId: string;
316
+ /**
317
+ * 새 웹훅 서명 시크릿
318
+ *
319
+ * **이 응답에서만 평문으로 반환됩니다.**
320
+ */
321
+ webhookSecret: string;
322
+ /** 재발급 시각 (ISO 8601) */
323
+ dateRotated: string;
324
+ }
325
+ /** 구독 등록 아이템 */
326
+ interface RegisterItem {
327
+ /** 택배사 코드 (예: `'cj'`, `'lotte'`) */
318
328
  courierCode: string;
319
- status: string;
329
+ /** 송장번호 */
330
+ trackingNumber: string;
331
+ /**
332
+ * 클라이언트 매핑 ID (선택)
333
+ *
334
+ * 주문번호 등을 넣으면 웹훅 페이로드에 그대로 포함됩니다.
335
+ */
336
+ clientId?: string;
337
+ }
338
+ /**
339
+ * `webhooks.register()` 파라미터
340
+ *
341
+ * @example
342
+ * // 웹훅 구독 (14일 주기 폴링)
343
+ * const sub = await client.webhooks.register({
344
+ * endpointId: 'ep_xxxx',
345
+ * items: [{ courierCode: 'cj', trackingNumber: '1234567890', clientId: 'order_001' }],
346
+ * recurring: true,
347
+ * })
348
+ *
349
+ * @example
350
+ * // 일회성 즉시 조회 (웹훅 없이, 결과는 getSubscription()으로 폴링)
351
+ * const req = await client.webhooks.register({
352
+ * items: [{ courierCode: 'lotte', trackingNumber: '9876543210' }],
353
+ * recurring: false,
354
+ * })
355
+ * const result = await client.webhooks.getSubscription(req.requestId)
356
+ */
357
+ interface RegisterParams {
358
+ /**
359
+ * 웹훅 수신 엔드포인트 ID (선택)
360
+ *
361
+ * 미제공 시 웹훅 없이 즉시 크롤한 뒤 `getSubscription()`으로 결과를 조회합니다.
362
+ */
363
+ endpointId?: string;
364
+ /** 추적할 택배 목록 */
365
+ items: RegisterItem[];
366
+ /**
367
+ * 반복 구독 여부
368
+ *
369
+ * - `true`: 배송 완료 또는 14일까지 주기적으로 폴링하여 상태 변경 시 웹훅 발송
370
+ * - `false`: 등록 즉시 1회 크롤 후 종료
371
+ */
372
+ recurring: boolean;
373
+ /**
374
+ * 이용자 정의 메타데이터 (선택)
375
+ *
376
+ * 웹훅 페이로드의 `metadata` 필드에 그대로 포함됩니다.
377
+ */
378
+ metadata?: Record<string, string>;
379
+ }
380
+ /** `webhooks.register()` 응답 */
381
+ interface RegisterResponse {
382
+ /**
383
+ * 구독/요청 ID
384
+ *
385
+ * `getSubscription(requestId)`, `cancelSubscription(requestId)` 에 사용합니다.
386
+ */
387
+ requestId: string;
388
+ /** 등록된 아이템 수 */
389
+ itemCount: number;
390
+ /** 반복 구독 여부 */
391
+ recurring: boolean;
392
+ }
393
+ /** 구독 요약 정보 */
394
+ interface SubscriptionSummary {
395
+ /** 전체 택배 수 */
396
+ total: number;
397
+ /** 배송 완료 수 */
398
+ delivered: number;
399
+ /** 배송 진행 중 수 */
400
+ active: number;
401
+ /** 조회 실패 수 */
402
+ failed: number;
403
+ }
404
+ /** `webhooks.listSubscriptions()` 파라미터 */
405
+ interface ListSubscriptionsParams {
406
+ /**
407
+ * 페이지네이션 커서
408
+ *
409
+ * 이전 응답의 `nextCursor` 값을 전달합니다.
410
+ * 생략하면 처음부터 조회합니다.
411
+ */
412
+ cursor?: string;
413
+ /** 페이지 크기 */
414
+ limit?: number;
415
+ }
416
+ /** 구독 목록 아이템 */
417
+ interface SubscriptionListItem {
418
+ /** 구독 ID */
419
+ requestId: string;
420
+ /** 연결된 엔드포인트 ID */
421
+ endpointId?: string;
422
+ /** 반복 구독 여부 */
423
+ recurring: boolean;
424
+ /** 구독 활성 여부 */
425
+ isActive: boolean;
426
+ /** 등록된 아이템 수 */
427
+ itemCount: number;
428
+ /** 요약 */
429
+ summary: SubscriptionSummary;
430
+ /** 등록 시각 (ISO 8601) */
320
431
  dateCreated: string;
321
432
  }
322
- interface ListTrackingLogsResult {
323
- logs: TrackingLog[];
433
+ /** `webhooks.listSubscriptions()` 응답 */
434
+ interface ListSubscriptionsResponse {
435
+ /** 구독 목록 */
436
+ subscriptions: SubscriptionListItem[];
437
+ /** 전체 수 */
324
438
  total: number;
325
- page: number;
326
- totalPages: number;
439
+ /**
440
+ * 다음 페이지 커서
441
+ *
442
+ * 마지막 페이지이면 존재하지 않습니다.
443
+ */
444
+ nextCursor?: string;
445
+ }
446
+ /** 구독 상세 아이템 (개별 택배) */
447
+ interface SubscriptionItem {
448
+ /** 택배사 코드 */
449
+ courierCode: string;
450
+ /** 송장번호 */
451
+ trackingNumber: string;
452
+ /** 클라이언트 매핑 ID */
453
+ clientId?: string;
454
+ /** 현재 배송 상태 */
455
+ currentStatus: CourierDeliveryStatus;
456
+ /** 이전 배송 상태 */
457
+ previousStatus?: CourierDeliveryStatus;
458
+ /** 상태 변경 여부 */
459
+ hasChanged: boolean;
460
+ /** 배송 완료 여부 */
461
+ isDelivered: boolean;
462
+ /** 최신 배송 조회 데이터 */
463
+ trackingData?: UnifiedTrackingResponse;
464
+ /** 조회 에러 메시지 (실패 시) */
465
+ error?: string;
327
466
  }
328
- interface HomepageClientOptions {
329
- /** Returns Firebase ID Token for authenticated requests */
330
- getToken?: () => Promise<string | null>;
467
+ /** `webhooks.getSubscription()` 응답 */
468
+ interface SubscriptionDetail {
469
+ /** 구독 ID */
470
+ requestId: string;
471
+ /** 연결된 엔드포인트 ID */
472
+ endpointId?: string;
473
+ /** 반복 구독 여부 */
474
+ recurring: boolean;
475
+ /** 구독 활성 여부 */
476
+ isActive: boolean;
477
+ /** 등록된 아이템 수 */
478
+ itemCount: number;
479
+ /** 요약 */
480
+ summary: SubscriptionSummary;
481
+ /** 각 택배별 상세 상태 */
482
+ items: SubscriptionItem[];
483
+ /** 마지막 폴링 시각 (ISO 8601) */
484
+ lastPolledAt?: string;
485
+ /** 다음 폴링 예정 시각 (ISO 8601) */
486
+ nextPollAt?: string;
487
+ /** 이용자 정의 메타데이터 */
488
+ metadata?: Record<string, string>;
489
+ /** 등록 시각 (ISO 8601) */
490
+ dateCreated: string;
491
+ /** 최종 수정 시각 (ISO 8601) */
492
+ dateModified: string;
331
493
  }
332
-
333
- declare class AuthResource {
334
- private readonly baseUrl;
335
- private readonly getToken;
336
- constructor(baseUrl: string, getToken: () => Promise<string | null>);
337
- /** Step 1: 이메일 인증 코드 요청 */
338
- requestVerification(params: RequestVerificationParams): Promise<void>;
339
- /** Step 2: 인증 코드 검증 + Firebase 계정 생성 → customToken 반환 */
340
- verifyAndRegister(params: VerifyAndRegisterParams): Promise<VerifyAndRegisterResult>;
341
- /** Google 로그인 후 HP 사용자 문서 확인/생성 */
342
- ensureUser(): Promise<EnsureUserResult>;
494
+ /** `webhooks.batchResults()` 파라미터 아이템 */
495
+ interface BatchResultItem {
496
+ /** 택배사 코드 */
497
+ courierCode: string;
498
+ /** 송장번호 */
499
+ trackingNumber: string;
343
500
  }
344
-
345
- declare class DashboardResource {
346
- private readonly baseUrl;
347
- private readonly getToken;
348
- constructor(baseUrl: string, getToken: () => Promise<string | null>);
349
- /** 대시보드 데이터 조회 (API 키 목록, 사용량, 플랜 정보) */
350
- get(): Promise<DashboardResult>;
351
- /** API 발급 */
352
- issueKey(): Promise<IssueApiKeyResult>;
501
+ /**
502
+ * `webhooks.batchResults()` 파라미터
503
+ *
504
+ * 여러 송장번호의 최신 배송 정보를 한 번에 조회합니다.
505
+ * 구독 ID가 아닌 (택배사 코드 + 송장번호)로 검색합니다.
506
+ */
507
+ interface BatchResultsParams {
508
+ /** 조회할 아이템 목록 */
509
+ items: BatchResultItem[];
510
+ }
511
+ /** 배치 결과 단건 */
512
+ interface BatchResultEntry {
513
+ /** 택배사 코드 */
514
+ courierCode: string;
515
+ /** 송장번호 */
516
+ trackingNumber: string;
517
+ /** 클라이언트 매핑 ID */
518
+ clientId?: string;
519
+ /** 구독 ID */
520
+ requestId: string;
521
+ /** 현재 배송 상태 */
522
+ currentStatus: CourierDeliveryStatus;
523
+ /** 배송 완료 여부 */
524
+ isDelivered: boolean;
525
+ /** 최신 배송 데이터 */
526
+ trackingData?: UnifiedTrackingResponse;
527
+ /** 에러 메시지 (실패 시) */
528
+ error?: string;
529
+ /** 마지막 폴링 시각 (ISO 8601) */
530
+ lastPolledAt?: string;
531
+ }
532
+ /** `webhooks.batchResults()` 응답 */
533
+ interface BatchResultsResponse {
534
+ /** 결과 목록 */
535
+ results: BatchResultEntry[];
536
+ /** 전체 수 */
537
+ total: number;
353
538
  }
354
-
355
- declare class QnaResource {
356
- private readonly baseUrl;
357
- private readonly getToken;
358
- constructor(baseUrl: string, getToken: () => Promise<string | null>);
359
- /** 게시글 목록 조회 (비회원도 공개글 열람 가능) */
360
- listPosts(params?: ListPostsParams): Promise<ListPostsResult>;
361
- /** 게시글 상세 조회 */
362
- getPost(postId: string): Promise<Post>;
363
- /** 게시글 작성 (로그인 필요) */
364
- createPost(params: CreatePostParams): Promise<{
365
- id: string;
366
- }>;
367
- /** 게시글 수정 (작성자/관리자만) */
368
- updatePost(postId: string, params: UpdatePostParams): Promise<Post>;
369
- /** 게시글 삭제 (작성자/관리자만) */
370
- deletePost(postId: string): Promise<void>;
371
- /** 댓글 목록 조회 */
372
- listComments(postId: string): Promise<ListCommentsResult>;
373
- /** 댓글 작성 (로그인 필요) */
374
- createComment(postId: string, params: CreateCommentParams): Promise<Comment>;
375
- /** 댓글 삭제 (작성자/관리자만) */
376
- deleteComment(commentId: string): Promise<void>;
539
+ /**
540
+ * 웹훅 수신 페이로드
541
+ *
542
+ * 배송 상태 변경 시 등록된 엔드포인트로 POST 요청이 전송됩니다.
543
+ * `X-Webhook-Signature` 헤더를 `webhookSecret`으로 HMAC-SHA256 검증하세요.
544
+ *
545
+ * @example
546
+ * // Express 수신 예시
547
+ * app.post('/webhook', (req, res) => {
548
+ * const sig = req.headers['x-webhook-signature']
549
+ * const body = JSON.stringify(req.body)
550
+ * const expected = crypto.createHmac('sha256', webhookSecret).update(body).digest('hex')
551
+ * if (sig !== expected) return res.status(401).send('Invalid signature')
552
+ *
553
+ * const payload: WebhookPayload = req.body
554
+ * if (payload.event === 'tracking.completed') {
555
+ * console.log(`${payload.requestId} 배송 추적 완료`)
556
+ * }
557
+ * res.sendStatus(200)
558
+ * })
559
+ */
560
+ interface WebhookPayload {
561
+ /**
562
+ * 이벤트 유형
563
+ *
564
+ * - `tracking.polled`: 주기적 폴링 결과 (상태 변경 또는 최신 정보)
565
+ * - `tracking.completed`: 배송 완료 또는 구독 종료
566
+ */
567
+ event: 'tracking.polled' | 'tracking.completed';
568
+ /** 구독 ID */
569
+ requestId: string;
570
+ /** 연결된 엔드포인트 ID */
571
+ endpointId: string;
572
+ /** 이용자 정의 메타데이터 */
573
+ metadata?: Record<string, string>;
574
+ /** 각 택배별 상태 정보 */
575
+ items: SubscriptionItem[];
576
+ /** 요약 */
577
+ summary: SubscriptionSummary;
578
+ /** 이벤트 발생 시각 (ISO 8601) */
579
+ timestamp: string;
377
580
  }
378
581
 
379
- declare class AdminResource {
380
- private readonly baseUrl;
381
- private readonly getToken;
382
- constructor(baseUrl: string, getToken: () => Promise<string | null>);
383
- private token;
384
- /** 전체 통계 조회 */
385
- getStats(): Promise<AdminStats>;
386
- /** 최근 14일 일별 가입자 수 */
387
- getDailySignups(): Promise<DailySignup[]>;
388
- /** 사용자 목록 조회 */
389
- listUsers(): Promise<ListAdminUsersResult>;
390
- /** 사용자 정보 수정 */
391
- updateUser(uid: string, params: UpdateAdminUserParams): Promise<void>;
392
- /** 사용자 상세 조회 (API 키 + 사용량 포함) */
393
- getUserDetail(uid: string): Promise<AdminUserDetail>;
394
- /** customers 목록 조회 */
395
- listCustomers(): Promise<ListAdminCustomersResult>;
396
- /** API 키 목록 조회 */
397
- listKeys(): Promise<ListAdminKeysResult>;
398
- /** API 키 플랜 변경 */
399
- updateKeyPlan(apiKeyHash: string, params: UpdateKeyPlanParams): Promise<void>;
400
- /** API 플랜 일괄 변경 */
401
- bulkUpdateKeyPlan(params: BulkUpdateKeyPlanParams): Promise<void>;
402
- /** Secret Key limits 수정 */
403
- updateKeyLimits(apiKeyHash: string, params: UpdateKeyLimitsParams): Promise<void>;
404
- /** 데모 생성 */
405
- createDemoKey(params?: CreateDemoKeyParams): Promise<AdminDemoKey>;
406
- /** 데모 키 목록 조회 */
407
- listDemoKeys(): Promise<ListAdminDemoKeysResult>;
408
- /** 데모 키 비활성화 */
409
- deactivateDemoKey(apiKeyHash: string): Promise<void>;
410
- /** 데모 키 활성화 */
411
- activateDemoKey(apiKeyHash: string): Promise<void>;
412
- /** 조회 로그 목록 */
413
- listTrackingLogs(page?: number): Promise<ListTrackingLogsResult>;
582
+ /**
583
+ * API 호출 실패 시 throw 되는 에러 클래스
584
+ *
585
+ * @example
586
+ * try {
587
+ * await client.tracking.trace({ items: [...] })
588
+ * } catch (err) {
589
+ * if (err instanceof ApiError) {
590
+ * console.error(err.code) // 'RATE_LIMITED'
591
+ * console.error(err.status) // 429
592
+ * console.error(err.message) // '요청 횟수가 플랜 한도를 초과했습니다'
593
+ * }
594
+ * }
595
+ */
596
+ declare class ApiError extends Error {
597
+ /**
598
+ * 기계가 읽는 에러 코드
599
+ *
600
+ * 이 값을 기준으로 분기 처리하세요.
601
+ */
602
+ readonly code: ApiErrorCode | string;
603
+ /** HTTP 상태 코드 */
604
+ readonly status: number;
605
+ constructor(code: ApiErrorCode | string, message: string, status: number);
606
+ }
607
+ /** API Key 인증 정보 */
608
+ interface AuthCredentials {
609
+ apiKey: string;
610
+ secretKey: string;
414
611
  }
415
612
 
416
- declare class PublicResource {
417
- private readonly baseUrl;
418
- constructor(baseUrl: string);
419
- /** 요금제 목록 조회 */
420
- getPlans(): Promise<PlanInfo[]>;
421
- /** Secret Key 프리셋 목록 조회 */
422
- getSecretPresets(): Promise<SecretPreset[]>;
423
- /** 개발 의뢰 문의 전송 */
424
- sendContactInquiry(params: ContactInquiryParams): Promise<void>;
613
+ /**
614
+ * 택배 조회 리소스
615
+ *
616
+ * 송장번호로 배송 정보를 즉시 조회합니다.
617
+ * 단건/다건을 모두 지원하며, 여러 택배사를 한 번의 요청으로 조회할 수 있습니다.
618
+ *
619
+ * @example
620
+ * const client = new DeliveryAPIClient({ apiKey: '...', secretKey: '...' })
621
+ *
622
+ * // 단건 조회
623
+ * const result = await client.tracking.trace({
624
+ * items: [{ courierCode: 'cj', trackingNumber: '1234567890' }],
625
+ * })
626
+ *
627
+ * // 다건 조회 (여러 택배사 혼합 가능)
628
+ * const result = await client.tracking.trace({
629
+ * items: [
630
+ * { courierCode: 'cj', trackingNumber: '1111111111', clientId: 'order_001' },
631
+ * { courierCode: 'lotte', trackingNumber: '2222222222', clientId: 'order_002' },
632
+ * ],
633
+ * })
634
+ */
635
+ declare class TrackingResource {
636
+ private readonly auth;
637
+ constructor(auth: AuthCredentials);
638
+ /**
639
+ * 지원 택배사 목록을 조회합니다.
640
+ *
641
+ * 택배사 코드(`trackingApiCode`)는 `trace()`의 `courierCode` 파라미터에 사용합니다.
642
+ *
643
+ * @returns 지원 택배사 목록 및 총 수
644
+ *
645
+ * @example
646
+ * const { couriers } = await client.tracking.getCouriers()
647
+ * // couriers: [{ trackingApiCode: 'cj', displayName: 'CJ대한통운' }, ...]
648
+ */
649
+ getCouriers(): Promise<GetCouriersResponse>;
650
+ /**
651
+ * 송장번호로 배송 정보를 조회합니다.
652
+ *
653
+ * - 요청당 여러 건을 배열로 전달할 수 있습니다.
654
+ * - 결과는 요청 순서와 동일한 인덱스로 반환됩니다.
655
+ * - 일부 아이템이 실패해도 전체 요청이 실패하지 않습니다. `results[].success`로 건별 확인하세요.
656
+ *
657
+ * **과금 안내**: `NOT_FOUND` 에러는 과금됩니다. `results[].error.billable`로 확인하세요.
658
+ *
659
+ * @param params 조회 파라미터
660
+ * @returns 아이템별 조회 결과 및 집계 요약
661
+ *
662
+ * @throws {ApiError} API 인증 실패, 요청 한도 초과 등 전체 요청 실패 시
663
+ *
664
+ * @example
665
+ * const { results, summary } = await client.tracking.trace({
666
+ * items: [
667
+ * { courierCode: 'cj', trackingNumber: '1234567890', clientId: 'order_001' },
668
+ * { courierCode: 'lotte', trackingNumber: '9876543210', clientId: 'order_002' },
669
+ * ],
670
+ * includeProgresses: true,
671
+ * })
672
+ *
673
+ * for (const result of results) {
674
+ * if (result.success) {
675
+ * console.log(result.data?.deliveryStatus) // 'DELIVERED'
676
+ * } else {
677
+ * console.warn(result.error?.code) // 'NOT_FOUND'
678
+ * }
679
+ * }
680
+ *
681
+ * console.log(`성공: ${summary.successful} / 전체: ${summary.total}`)
682
+ */
683
+ trace(params: TraceParams): Promise<TraceResponse>;
425
684
  }
426
685
 
427
- declare class HomepageClient {
428
- readonly auth: AuthResource;
429
- readonly dashboard: DashboardResource;
430
- readonly qna: QnaResource;
431
- readonly admin: AdminResource;
432
- readonly public: PublicResource;
433
- constructor(options?: HomepageClientOptions);
686
+ /**
687
+ * 웹훅 리소스
688
+ *
689
+ * **엔드포인트 관리**: 웹훅을 수신할 URL을 등록/관리합니다.
690
+ * **구독 관리**: 특정 택배를 추적하고 상태 변경 시 웹훅을 수신합니다.
691
+ *
692
+ * @example
693
+ * const client = new DeliveryAPIClient({ apiKey: '...', secretKey: '...' })
694
+ *
695
+ * // 1. 엔드포인트 등록
696
+ * const endpoint = await client.webhooks.createEndpoint({
697
+ * url: 'https://my-server.com/webhook',
698
+ * name: '운영 서버',
699
+ * })
700
+ * // ⚠️ endpoint.webhookSecret 을 안전하게 보관하세요!
701
+ *
702
+ * // 2. 택배 추적 구독
703
+ * const sub = await client.webhooks.register({
704
+ * endpointId: endpoint.endpointId,
705
+ * items: [{ courierCode: 'cj', trackingNumber: '1234567890', clientId: 'order_001' }],
706
+ * recurring: true,
707
+ * })
708
+ *
709
+ * // 3. 구독 상태 조회
710
+ * const detail = await client.webhooks.getSubscription(sub.requestId)
711
+ */
712
+ declare class WebhooksResource {
713
+ private readonly auth;
714
+ constructor(auth: AuthCredentials);
715
+ /**
716
+ * 웹훅 엔드포인트를 등록합니다.
717
+ *
718
+ * 등록 시 서버에서 해당 URL로 테스트 POST 요청을 전송하여 연결 가능 여부를 검증합니다.
719
+ * 응답의 `webhookSecret`은 **이 응답에서만 평문으로 반환**됩니다.
720
+ * 분실 시 `rotateSecret()`으로 재발급해야 합니다.
721
+ *
722
+ * @param params 엔드포인트 등록 파라미터
723
+ * @returns 생성된 엔드포인트 정보 (webhookSecret 포함)
724
+ *
725
+ * @throws {ApiError} `WEBHOOK_ENDPOINT_LIMIT` — 엔드포인트 등록 한도 초과
726
+ *
727
+ * @example
728
+ * const endpoint = await client.webhooks.createEndpoint({
729
+ * url: 'https://my-server.com/webhook',
730
+ * name: '운영 서버',
731
+ * })
732
+ * console.log(endpoint.endpointId) // 'ep_xxxx'
733
+ * console.log(endpoint.webhookSecret) // 반드시 저장하세요!
734
+ */
735
+ createEndpoint(params: CreateEndpointParams): Promise<CreateEndpointResponse>;
736
+ /**
737
+ * 등록된 웹훅 엔드포인트 목록을 조회합니다.
738
+ *
739
+ * @returns 엔드포인트 목록 및 총 수
740
+ *
741
+ * @example
742
+ * const { endpoints } = await client.webhooks.listEndpoints()
743
+ * const active = endpoints.filter(ep => ep.status === 'active')
744
+ */
745
+ listEndpoints(): Promise<ListEndpointsResponse>;
746
+ /**
747
+ * 웹훅 엔드포인트 이름을 수정합니다.
748
+ *
749
+ * URL은 변경할 수 없습니다. URL을 변경해야 한다면 엔드포인트를 삭제 후 재등록하세요.
750
+ *
751
+ * @param endpointId 수정할 엔드포인트 ID
752
+ * @param params 수정 내용
753
+ *
754
+ * @throws {ApiError} `NOT_FOUND` — 존재하지 않는 엔드포인트
755
+ *
756
+ * @example
757
+ * await client.webhooks.updateEndpoint('ep_xxxx', { name: '스테이징 서버' })
758
+ */
759
+ updateEndpoint(endpointId: string, params: UpdateEndpointParams): Promise<void>;
760
+ /**
761
+ * 웹훅 엔드포인트를 삭제합니다.
762
+ *
763
+ * 해당 엔드포인트에 연결된 구독도 함께 삭제됩니다 (cascade).
764
+ *
765
+ * @param endpointId 삭제할 엔드포인트 ID
766
+ *
767
+ * @throws {ApiError} `NOT_FOUND` — 존재하지 않는 엔드포인트
768
+ *
769
+ * @example
770
+ * await client.webhooks.deleteEndpoint('ep_xxxx')
771
+ */
772
+ deleteEndpoint(endpointId: string): Promise<void>;
773
+ /**
774
+ * 웹훅 서명 시크릿을 재발급합니다.
775
+ *
776
+ * 기존 시크릿은 즉시 무효화됩니다.
777
+ * 새 시크릿은 **이 응답에서만 평문으로 반환**됩니다.
778
+ *
779
+ * @param endpointId 대상 엔드포인트 ID
780
+ * @param params 새 시크릿 (선택, 미제공 시 서버 생성)
781
+ * @returns 새 웹훅 시크릿
782
+ *
783
+ * @throws {ApiError} `NOT_FOUND` — 존재하지 않는 엔드포인트
784
+ *
785
+ * @example
786
+ * const { webhookSecret } = await client.webhooks.rotateSecret('ep_xxxx')
787
+ * console.log(webhookSecret) // 새 시크릿 — 반드시 저장하세요!
788
+ */
789
+ rotateSecret(endpointId: string, params?: RotateSecretParams): Promise<RotateSecretResponse>;
790
+ /**
791
+ * 택배 추적 구독을 등록합니다.
792
+ *
793
+ * **구독형** (`recurring: true`): 배송 완료 또는 최대 14일까지 주기적으로 폴링하여
794
+ * 상태 변경 시 `endpointId`로 웹훅을 발송합니다.
795
+ *
796
+ * **일회성** (`recurring: false`): 등록 즉시 1회 크롤 후 종료합니다.
797
+ * `endpointId` 없이 사용하면 결과를 `getSubscription(requestId)`으로 직접 조회할 수 있습니다.
798
+ *
799
+ * @param params 구독 등록 파라미터
800
+ * @returns 구독 ID (`requestId`) 및 기본 정보
801
+ *
802
+ * @example
803
+ * // 구독형 — 상태 변경 시 웹훅 수신
804
+ * const sub = await client.webhooks.register({
805
+ * endpointId: 'ep_xxxx',
806
+ * items: [
807
+ * { courierCode: 'cj', trackingNumber: '1234567890', clientId: 'order_001' },
808
+ * ],
809
+ * recurring: true,
810
+ * })
811
+ *
812
+ * @example
813
+ * // 일회성 즉시 조회 — 웹훅 없이 결과를 직접 폴링
814
+ * const req = await client.webhooks.register({
815
+ * items: [{ courierCode: 'lotte', trackingNumber: '9876543210' }],
816
+ * recurring: false,
817
+ * })
818
+ * const detail = await client.webhooks.getSubscription(req.requestId)
819
+ */
820
+ register(params: RegisterParams): Promise<RegisterResponse>;
821
+ /**
822
+ * 구독 목록을 조회합니다.
823
+ *
824
+ * 커서 기반 페이지네이션을 지원합니다.
825
+ * 다음 페이지가 있으면 응답의 `nextCursor`를 다음 호출의 `cursor` 파라미터로 전달하세요.
826
+ *
827
+ * @param params 페이지네이션 파라미터 (선택)
828
+ * @returns 구독 목록 및 다음 페이지 커서
829
+ *
830
+ * @example
831
+ * // 전체 구독 목록 순회
832
+ * let cursor: string | undefined
833
+ * do {
834
+ * const page = await client.webhooks.listSubscriptions({ cursor, limit: 50 })
835
+ * for (const sub of page.subscriptions) {
836
+ * console.log(sub.requestId, sub.isActive)
837
+ * }
838
+ * cursor = page.nextCursor
839
+ * } while (cursor)
840
+ */
841
+ listSubscriptions(params?: ListSubscriptionsParams): Promise<ListSubscriptionsResponse>;
842
+ /**
843
+ * 구독 상세 정보를 조회합니다.
844
+ *
845
+ * 각 택배별 현재 상태 및 최신 배송 데이터를 포함합니다.
846
+ *
847
+ * @param requestId `register()` 응답의 `requestId`
848
+ * @returns 구독 상세 (아이템별 상태 포함)
849
+ *
850
+ * @throws {ApiError} `NOT_FOUND` — 존재하지 않는 구독
851
+ *
852
+ * @example
853
+ * const detail = await client.webhooks.getSubscription('req_xxxx')
854
+ *
855
+ * for (const item of detail.items) {
856
+ * console.log(item.trackingNumber, item.currentStatus)
857
+ * }
858
+ */
859
+ getSubscription(requestId: string): Promise<SubscriptionDetail>;
860
+ /**
861
+ * 구독을 취소합니다.
862
+ *
863
+ * 취소된 구독은 더 이상 폴링되지 않으며 웹훅도 발송되지 않습니다.
864
+ *
865
+ * @param requestId 취소할 구독 ID
866
+ *
867
+ * @throws {ApiError} `NOT_FOUND` — 존재하지 않는 구독
868
+ *
869
+ * @example
870
+ * await client.webhooks.cancelSubscription('req_xxxx')
871
+ */
872
+ cancelSubscription(requestId: string): Promise<void>;
873
+ /**
874
+ * 여러 송장번호의 최신 배송 정보를 한 번에 조회합니다.
875
+ *
876
+ * 구독 ID 없이 택배사 코드 + 송장번호로 검색합니다.
877
+ * 해당 계정에 등록된 구독 중 일치하는 아이템을 반환합니다.
878
+ *
879
+ * @param params 조회할 아이템 목록
880
+ * @returns 아이템별 최신 배송 정보
881
+ *
882
+ * @example
883
+ * const { results } = await client.webhooks.batchResults({
884
+ * items: [
885
+ * { courierCode: 'cj', trackingNumber: '1111111111' },
886
+ * { courierCode: 'lotte', trackingNumber: '2222222222' },
887
+ * ],
888
+ * })
889
+ *
890
+ * for (const r of results) {
891
+ * console.log(r.currentStatus, r.isDelivered)
892
+ * }
893
+ */
894
+ batchResults(params: BatchResultsParams): Promise<BatchResultsResponse>;
434
895
  }
435
896
 
436
- declare class ApiError extends Error {
437
- readonly statusCode: number;
438
- readonly errorCode: string | undefined;
439
- constructor(statusCode: number, errorCode: string | undefined, message: string);
897
+ /** `DeliveryAPIClient` 생성 옵션 */
898
+ interface DeliveryAPIClientOptions {
899
+ /**
900
+ * API Key
901
+ *
902
+ * 대시보드에서 발급한 API Key입니다.
903
+ */
904
+ apiKey: string;
905
+ /**
906
+ * Secret Key
907
+ *
908
+ * API Key에 연결된 Secret Key입니다.
909
+ * 클라이언트 사이드(브라우저)에 노출되지 않도록 주의하세요.
910
+ */
911
+ secretKey: string;
912
+ }
913
+ /**
914
+ * DeliveryAPI 클라이언트
915
+ *
916
+ * API Key + Secret Key로 인증합니다.
917
+ * 모든 요청은 `Authorization: Bearer {apiKey}:{secretKey}` 헤더로 전송됩니다.
918
+ *
919
+ * @example
920
+ * import { DeliveryAPIClient } from 'deliveryapi'
921
+ *
922
+ * const client = new DeliveryAPIClient({
923
+ * apiKey: 'pk_live_xxxx',
924
+ * secretKey: 'sk_live_xxxx',
925
+ * })
926
+ *
927
+ * // 택배 조회
928
+ * const { results } = await client.tracking.trace({
929
+ * items: [{ courierCode: 'cj', trackingNumber: '1234567890' }],
930
+ * })
931
+ *
932
+ * // 웹훅 구독
933
+ * const sub = await client.webhooks.register({
934
+ * endpointId: 'ep_xxxx',
935
+ * items: [{ courierCode: 'cj', trackingNumber: '1234567890' }],
936
+ * recurring: true,
937
+ * })
938
+ */
939
+ declare class DeliveryAPIClient {
940
+ /**
941
+ * 택배 조회 API
942
+ *
943
+ * 송장번호로 배송 정보를 즉시 조회합니다.
944
+ *
945
+ * - `getCouriers()` — 지원 택배사 목록
946
+ * - `trace()` — 송장번호 조회 (단건/다건)
947
+ */
948
+ readonly tracking: TrackingResource;
949
+ /**
950
+ * 웹훅 API
951
+ *
952
+ * 배송 상태 변경 시 웹훅으로 알림을 받습니다.
953
+ *
954
+ * **엔드포인트 관리**:
955
+ * - `createEndpoint()` — 수신 URL 등록
956
+ * - `listEndpoints()` — 목록 조회
957
+ * - `updateEndpoint()` — 이름 수정
958
+ * - `deleteEndpoint()` — 삭제
959
+ * - `rotateSecret()` — 서명 시크릿 재발급
960
+ *
961
+ * **구독 관리**:
962
+ * - `register()` — 택배 추적 구독 등록
963
+ * - `listSubscriptions()` — 구독 목록
964
+ * - `getSubscription()` — 구독 상세
965
+ * - `cancelSubscription()` — 구독 취소
966
+ * - `batchResults()` — 다건 최신 상태 조회
967
+ */
968
+ readonly webhooks: WebhooksResource;
969
+ /** API Base URL (`https://api.deliveryapi.co.kr`) */
970
+ readonly baseUrl: string;
971
+ constructor(options: DeliveryAPIClientOptions);
440
972
  }
441
973
 
442
- export { type AdminApiKeyDetail, type AdminCustomer, type AdminDemoKey, type AdminKeyItem, type AdminSecretKeyDetail, type AdminStats, type AdminUser, type AdminUserDetail, ApiError, type ApiResponse, type BulkUpdateKeyPlanParams, type Comment, type ContactInquiryParams, type CreateCommentParams, type CreateDemoKeyParams, type CreatePostParams, type DailySignup, type DashboardApiKey, type DashboardResult, type EnsureUserResult, HomepageClient, type HomepageClientOptions, type IssueApiKeyResult, type KeyLimits, type ListAdminCustomersResult, type ListAdminDemoKeysResult, type ListAdminKeysResult, type ListAdminUsersResult, type ListCommentsResult, type ListPostsParams, type ListPostsResult, type ListTrackingLogsResult, type PlanInfo, type Post, type RequestVerificationParams, type SecretPreset, type TrackingLog, type UpdateAdminUserParams, type UpdateKeyLimitsParams, type UpdateKeyPlanParams, type UpdatePostParams, type VerifyAndRegisterParams, type VerifyAndRegisterResult };
974
+ export { ApiError, type ApiErrorCode, type BatchResultEntry, type BatchResultItem, type BatchResultsParams, type BatchResultsResponse, CourierDeliveryStatus, type CourierInfo, type CreateEndpointParams, type CreateEndpointResponse, DeliveryAPIClient, type DeliveryAPIClientOptions, type EndpointInfo, type GetCouriersResponse, type ListEndpointsResponse, type ListSubscriptionsParams, type ListSubscriptionsResponse, type RegisterItem, type RegisterParams, type RegisterResponse, type RotateSecretParams, type RotateSecretResponse, type SubscriptionDetail, type SubscriptionItem, type SubscriptionListItem, type SubscriptionSummary, type TraceCacheInfo, type TraceItem, type TraceItemError, type TraceParams, type TraceResponse, type TraceResult, type TrackingErrorCode, type TrackingProgress, type UnifiedTrackingResponse, type UpdateEndpointParams, type WebhookPayload };