@land-catalyst/batch-data-sdk 1.1.12

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/client.js ADDED
@@ -0,0 +1,757 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BatchDataClient = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const logger_interface_1 = require("./logger.interface");
9
+ const builders_1 = require("./builders");
10
+ /**
11
+ * BatchData API Client
12
+ *
13
+ * Pure API client that makes HTTP calls to BatchData.io endpoints.
14
+ * No business logic - just handles HTTP communication with the API.
15
+ */
16
+ class BatchDataClient {
17
+ constructor(options) {
18
+ // Validate API key is present
19
+ if (!options.apiKey) {
20
+ throw new Error("BatchData API key is required. Please provide apiKey in options.");
21
+ }
22
+ this.v1BaseUrl = options.v1BaseUrl || "https://api.batchdata.com/api/v1";
23
+ this.v2BaseUrl = options.v2BaseUrl || "https://api.batchdata.com/api/v2";
24
+ this.v3BaseUrl = options.v3BaseUrl || "https://api.batchdata.com/api/v3";
25
+ this.logger = options.logger || new logger_interface_1.ConsoleLogger();
26
+ this.webhooks = options.webhooks;
27
+ this.axiosInstance = axios_1.default.create({
28
+ timeout: options.timeout || 30000,
29
+ headers: {
30
+ "Content-Type": "application/json",
31
+ Accept: "application/json",
32
+ Authorization: `Bearer ${options.apiKey}`,
33
+ },
34
+ });
35
+ // Set up middleware/interceptors if provided
36
+ if (options.middleware) {
37
+ this.setupMiddleware(options.middleware);
38
+ }
39
+ }
40
+ /**
41
+ * Set up axios interceptors from middleware configuration
42
+ * @param middleware The middleware configuration
43
+ */
44
+ setupMiddleware(middleware) {
45
+ // Request interceptors
46
+ if (middleware.onRequest && middleware.onRequest.length > 0) {
47
+ middleware.onRequest.forEach((middlewareFn) => {
48
+ this.axiosInstance.interceptors.request.use((config) => middlewareFn(config), undefined // Request errors are handled by response error interceptors
49
+ );
50
+ });
51
+ }
52
+ // Response interceptors
53
+ if (middleware.onResponse && middleware.onResponse.length > 0) {
54
+ middleware.onResponse.forEach((middlewareFn) => {
55
+ this.axiosInstance.interceptors.response.use((response) => middlewareFn(response), undefined // Errors are handled by error interceptors
56
+ );
57
+ });
58
+ }
59
+ // Error interceptors
60
+ if (middleware.onError && middleware.onError.length > 0) {
61
+ middleware.onError.forEach((middlewareFn) => {
62
+ this.axiosInstance.interceptors.response.use(undefined, // Success responses are handled by response interceptors
63
+ async (error) => {
64
+ const result = await middlewareFn(error);
65
+ // If middleware returns a response, treat it as recovery
66
+ if (result && "status" in result && "data" in result) {
67
+ return result;
68
+ }
69
+ // Otherwise, re-throw the error
70
+ throw result;
71
+ });
72
+ });
73
+ }
74
+ }
75
+ /**
76
+ * Apply default webhook URLs to an async request if they're not already set.
77
+ * This helper ensures that if webhook URLs are configured in the client,
78
+ * they will be used for async requests that don't explicitly specify webhook URLs.
79
+ *
80
+ * @param request The async request to modify
81
+ * @param endpointType The type of async endpoint (e.g., 'propertyLookup', 'propertySearch')
82
+ * @returns The request with webhook URLs applied (if needed)
83
+ */
84
+ applyDefaultWebhookUrls(request, endpointType) {
85
+ // Determine which webhook URLs to use
86
+ let webhookUrl;
87
+ let errorWebhookUrl;
88
+ // First, try endpoint-specific webhook config
89
+ if (endpointType && this.webhooks?.[endpointType]) {
90
+ webhookUrl = this.webhooks[endpointType]?.url;
91
+ errorWebhookUrl = this.webhooks[endpointType]?.errorUrl;
92
+ }
93
+ // Fall back to default webhook config
94
+ if (!webhookUrl && this.webhooks?.default) {
95
+ webhookUrl = this.webhooks.default.url;
96
+ errorWebhookUrl = errorWebhookUrl || this.webhooks.default.errorUrl;
97
+ }
98
+ // If no webhook URLs are configured, return original request
99
+ if (!webhookUrl && !errorWebhookUrl) {
100
+ return request;
101
+ }
102
+ // If request has no options, create an empty options object
103
+ const options = request.options || {};
104
+ // Apply webhook URL if not already set
105
+ if (!options.webhookUrl && webhookUrl) {
106
+ options.webhookUrl = webhookUrl;
107
+ }
108
+ // Apply error webhook URL if not already set
109
+ if (!options.errorWebhookUrl && errorWebhookUrl) {
110
+ options.errorWebhookUrl = errorWebhookUrl;
111
+ }
112
+ // If no options were modified and request had no options, return original
113
+ if (!request.options && !options.webhookUrl && !options.errorWebhookUrl) {
114
+ return request;
115
+ }
116
+ // Return a new request object with updated options
117
+ return {
118
+ ...request,
119
+ options: {
120
+ ...request.options,
121
+ ...options,
122
+ },
123
+ };
124
+ }
125
+ /**
126
+ * Apply default webhook URLs to a property subscription request if they're not already set.
127
+ * This helper ensures that if webhook URLs are configured in the client,
128
+ * they will be used for property subscriptions that don't explicitly specify webhook URLs.
129
+ *
130
+ * @param request The property subscription request to modify
131
+ * @returns The request with webhook URLs applied (if needed)
132
+ */
133
+ applyPropertySubscriptionWebhooks(request) {
134
+ // Determine which webhook URLs to use
135
+ let webhookUrl;
136
+ let errorWebhookUrl;
137
+ // First, try property subscription-specific webhook config
138
+ if (this.webhooks?.propertySubscription) {
139
+ webhookUrl = this.webhooks.propertySubscription.url;
140
+ errorWebhookUrl = this.webhooks.propertySubscription.errorUrl;
141
+ }
142
+ // Fall back to default webhook config
143
+ if (!webhookUrl && this.webhooks?.default) {
144
+ webhookUrl = this.webhooks.default.url;
145
+ errorWebhookUrl = errorWebhookUrl || this.webhooks.default.errorUrl;
146
+ }
147
+ // If no webhook URLs are configured, return original request
148
+ if (!webhookUrl && !errorWebhookUrl) {
149
+ return request;
150
+ }
151
+ // If deliveryConfig is not set or is not a webhook type, create/update it
152
+ const deliveryConfig = request.deliveryConfig || {
153
+ type: "webhook",
154
+ };
155
+ // Only apply defaults if deliveryConfig is a webhook type
156
+ if (deliveryConfig.type === "webhook") {
157
+ // Apply webhook URL if not already set
158
+ if (!deliveryConfig.url && webhookUrl) {
159
+ deliveryConfig.url = webhookUrl;
160
+ }
161
+ // Apply error webhook URL if not already set
162
+ if (!deliveryConfig.errorUrl && errorWebhookUrl) {
163
+ deliveryConfig.errorUrl = errorWebhookUrl;
164
+ }
165
+ }
166
+ // Return a new request object with updated deliveryConfig
167
+ return {
168
+ ...request,
169
+ deliveryConfig: {
170
+ ...deliveryConfig,
171
+ },
172
+ };
173
+ }
174
+ /**
175
+ * Add a request middleware function that executes before requests are sent.
176
+ * Useful for adding headers, logging, metrics, etc.
177
+ *
178
+ * @param middleware The middleware function
179
+ * @returns The interceptor ID (can be used to remove the interceptor later)
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * const client = new BatchDataClient({ apiKey: "..." });
184
+ * client.addRequestMiddleware((config) => {
185
+ * console.log(`Making request to ${config.url}`);
186
+ * config.headers["X-Custom-Header"] = "value";
187
+ * return config;
188
+ * });
189
+ * ```
190
+ */
191
+ addRequestMiddleware(middleware) {
192
+ return this.axiosInstance.interceptors.request.use((config) => middleware(config), undefined);
193
+ }
194
+ /**
195
+ * Add a response middleware function that executes after successful responses.
196
+ * Useful for logging, metrics, response transformation, etc.
197
+ *
198
+ * @param middleware The middleware function
199
+ * @returns The interceptor ID (can be used to remove the interceptor later)
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * const client = new BatchDataClient({ apiKey: "..." });
204
+ * client.addResponseMiddleware((response) => {
205
+ * console.log(`Received response: ${response.status}`);
206
+ * return response;
207
+ * });
208
+ * ```
209
+ */
210
+ addResponseMiddleware(middleware) {
211
+ return this.axiosInstance.interceptors.response.use((response) => middleware(response), undefined);
212
+ }
213
+ /**
214
+ * Add an error middleware function that executes when requests fail.
215
+ * Useful for error handling, retry logic, error transformation, etc.
216
+ *
217
+ * @param middleware The middleware function
218
+ * @returns The interceptor ID (can be used to remove the interceptor later)
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * const client = new BatchDataClient({ apiKey: "..." });
223
+ * client.addErrorMiddleware(async (error) => {
224
+ * if (error.response?.status === 429) {
225
+ * // Retry logic here
226
+ * }
227
+ * throw error; // Re-throw to propagate
228
+ * });
229
+ * ```
230
+ */
231
+ addErrorMiddleware(middleware) {
232
+ return this.axiosInstance.interceptors.response.use(undefined, async (error) => {
233
+ const result = await middleware(error);
234
+ // If middleware returns a response, treat it as recovery
235
+ if (result && "status" in result && "data" in result) {
236
+ return result;
237
+ }
238
+ // Otherwise, re-throw the error
239
+ throw result;
240
+ });
241
+ }
242
+ /**
243
+ * Remove a request interceptor by ID
244
+ * @param id The interceptor ID returned from addRequestMiddleware
245
+ */
246
+ removeRequestMiddleware(id) {
247
+ this.axiosInstance.interceptors.request.eject(id);
248
+ }
249
+ /**
250
+ * Remove a response interceptor by ID
251
+ * @param id The interceptor ID returned from addResponseMiddleware or addErrorMiddleware
252
+ */
253
+ removeResponseMiddleware(id) {
254
+ this.axiosInstance.interceptors.response.eject(id);
255
+ }
256
+ /**
257
+ * Wrapper for axios calls that handles error logging consistently
258
+ * @param axiosCall The axios call to execute
259
+ * @param errorMessagePrefix Prefix for the error message (e.g., "Failed to search properties")
260
+ * @param url The URL being called (for error details)
261
+ * @returns Object containing the response data and status
262
+ */
263
+ async handleApiCall(axiosCall, errorMessagePrefix, url) {
264
+ try {
265
+ const response = await axiosCall();
266
+ return response;
267
+ }
268
+ catch (error) {
269
+ const axiosError = error;
270
+ const errorDetails = {
271
+ url,
272
+ status: axiosError.response?.status,
273
+ statusText: axiosError.response?.statusText,
274
+ data: axiosError.response?.data,
275
+ headers: axiosError.response?.headers,
276
+ };
277
+ this.logger.error(`${errorMessagePrefix}: ${axiosError.response?.status} - ${axiosError.message}`, axiosError, errorDetails);
278
+ throw error;
279
+ }
280
+ }
281
+ /**
282
+ * Create a property subscription (v2 API)
283
+ * POST /api/v2/property-subscription
284
+ */
285
+ async createPropertySubscription(request) {
286
+ const url = `${this.v2BaseUrl}/property-subscription`;
287
+ // Apply default webhook URLs if configured
288
+ const requestWithDefaults = this.applyPropertySubscriptionWebhooks(request);
289
+ this.logger.debug(`Creating property subscription: POST ${url}`, {
290
+ query: requestWithDefaults.searchCriteria.query,
291
+ });
292
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, requestWithDefaults), "Failed to create property subscription", url);
293
+ this.logger.debug(`Property subscription created: ${data.id}`, {
294
+ status,
295
+ });
296
+ return data;
297
+ }
298
+ /**
299
+ * Search for properties (v1 API)
300
+ * POST /api/v1/property/search
301
+ *
302
+ * @param searchCriteria Search criteria
303
+ * @param options Request options
304
+ * @param options.take Number of properties to return (0 for count-only)
305
+ * @param options.skip Number of properties to skip for pagination
306
+ * @returns Property search API response
307
+ */
308
+ async searchProperties(request) {
309
+ const url = `${this.v1BaseUrl}/property/search`;
310
+ this.logger.debug(`Searching properties: POST ${url}`, {
311
+ query: request.searchCriteria.query,
312
+ take: request.options?.take,
313
+ skip: request.options?.skip,
314
+ });
315
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to search properties", url);
316
+ this.logger.debug(`Property search completed`, {
317
+ status,
318
+ hasData: !!data,
319
+ });
320
+ return data;
321
+ }
322
+ /**
323
+ * Get the count of properties matching the search criteria
324
+ * @param searchCriteria Search criteria to match properties
325
+ * @returns Number of matching properties, or null if the endpoint is unavailable
326
+ */
327
+ async getPropertyCount(searchRequest) {
328
+ const { searchCriteria, options } = searchRequest;
329
+ const lookupOptions = options
330
+ ? builders_1.PropertyLookupOptionsBuilder.from(options).take(0)
331
+ : new builders_1.PropertyLookupOptionsBuilder();
332
+ const response = await this.searchProperties({
333
+ searchCriteria,
334
+ options: lookupOptions.build(),
335
+ });
336
+ return response.results?.meta?.results?.resultsFound ?? null;
337
+ }
338
+ /**
339
+ * Verify and normalize addresses (v1 API)
340
+ * POST /api/v1/address/verify
341
+ *
342
+ * @param request Address verification request
343
+ * @returns Address verification API response
344
+ */
345
+ async verifyAddress(request) {
346
+ const url = `${this.v1BaseUrl}/address/verify`;
347
+ this.logger.debug(`Verifying addresses: POST ${url}`, {
348
+ requestCount: request.requests.length,
349
+ });
350
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to verify addresses", url);
351
+ this.logger.debug(`Address verification completed`, {
352
+ status,
353
+ addressCount: data.results?.addresses?.length,
354
+ });
355
+ return data;
356
+ }
357
+ /**
358
+ * Get address autocomplete suggestions (v1 API)
359
+ * POST /api/v1/address/autocomplete
360
+ *
361
+ * Note: This endpoint requires a Referer header per API documentation.
362
+ * For server-side usage, a default Referer is set.
363
+ *
364
+ * @param request Address autocomplete request
365
+ * @returns Address autocomplete API response
366
+ */
367
+ async autocompleteAddress(request) {
368
+ const url = `${this.v1BaseUrl}/address/autocomplete`;
369
+ this.logger.debug(`Autocompleting address: POST ${url}`, {
370
+ query: request.searchCriteria.query,
371
+ take: request.options?.take,
372
+ });
373
+ // Autocomplete endpoint requires Referer header per API documentation
374
+ // Set a default Referer for server-side SDK usage
375
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request, {
376
+ headers: {
377
+ Referer: "https://api.batchdata.com",
378
+ },
379
+ }), "Failed to autocomplete address", url);
380
+ this.logger.debug(`Address autocomplete completed`, {
381
+ status,
382
+ suggestionCount: data.result?.suggestions?.length,
383
+ });
384
+ return data;
385
+ }
386
+ /**
387
+ * Geocode an address to coordinates (v1 API)
388
+ * POST /api/v1/address/geocode
389
+ *
390
+ * @param request Address geocode request
391
+ * @returns Address geocode API response
392
+ */
393
+ async geocodeAddress(request) {
394
+ const url = `${this.v1BaseUrl}/address/geocode`;
395
+ this.logger.debug(`Geocoding address: POST ${url}`, {
396
+ requestCount: request.requests.length,
397
+ });
398
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to geocode address", url);
399
+ this.logger.debug(`Address geocode completed`, {
400
+ status,
401
+ addressCount: data.result?.addresses?.length,
402
+ });
403
+ return data;
404
+ }
405
+ /**
406
+ * Reverse geocode coordinates to an address (v1 API)
407
+ * POST /api/v1/address/reverse-geocode
408
+ *
409
+ * @param request Address reverse geocode request
410
+ * @returns Address reverse geocode API response
411
+ */
412
+ async reverseGeocodeAddress(request) {
413
+ const url = `${this.v1BaseUrl}/address/reverse-geocode`;
414
+ this.logger.debug(`Reverse geocoding coordinates: POST ${url}`, {
415
+ latitude: request.request.latitude,
416
+ longitude: request.request.longitude,
417
+ });
418
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to reverse geocode address", url);
419
+ this.logger.debug(`Address reverse geocode completed`, {
420
+ status,
421
+ addressCount: data.result?.addresses?.length,
422
+ });
423
+ return data;
424
+ }
425
+ /**
426
+ * Lookup property details by address, propertyId, hash, or APN (v1 API)
427
+ * POST /api/v1/property/lookup/all-attributes
428
+ *
429
+ * @param request Property lookup request
430
+ * @returns Property lookup API response
431
+ */
432
+ async lookupProperty(request) {
433
+ const url = `${this.v1BaseUrl}/property/lookup/all-attributes`;
434
+ this.logger.debug(`Looking up property: POST ${url}`, {
435
+ requestCount: request.requests.length,
436
+ });
437
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to lookup property", url);
438
+ this.logger.debug(`Property lookup completed`, {
439
+ status,
440
+ propertyCount: data.results?.properties?.length,
441
+ });
442
+ return data;
443
+ }
444
+ /**
445
+ * Lookup property details asynchronously (v1 API)
446
+ * POST /api/v1/property/lookup/async
447
+ *
448
+ * @param request Property lookup async request
449
+ * @returns Property lookup async API response
450
+ */
451
+ async lookupPropertyAsync(request) {
452
+ const url = `${this.v1BaseUrl}/property/lookup/async`;
453
+ // Apply default webhook URLs if configured
454
+ const requestWithDefaults = this.applyDefaultWebhookUrls(request, "propertyLookup");
455
+ this.logger.debug(`Looking up property async: POST ${url}`, {
456
+ requestCount: requestWithDefaults.requests.length,
457
+ });
458
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, requestWithDefaults), "Failed to lookup property async", url);
459
+ this.logger.debug(`Property lookup async completed`, {
460
+ status,
461
+ asyncStatus: data.status?.text,
462
+ });
463
+ return data;
464
+ }
465
+ /**
466
+ * Search for properties asynchronously (v1 API)
467
+ * POST /api/v1/property/search/async
468
+ *
469
+ * @param request Property search async request
470
+ * @returns Property search async API response
471
+ */
472
+ async searchPropertiesAsync(request) {
473
+ const url = `${this.v1BaseUrl}/property/search/async`;
474
+ // Apply default webhook URLs if configured
475
+ const requestWithDefaults = this.applyDefaultWebhookUrls(request, "propertySearch");
476
+ this.logger.debug(`Searching properties async: POST ${url}`, {
477
+ query: requestWithDefaults.searchCriteria.query,
478
+ });
479
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, requestWithDefaults), "Failed to search properties async", url);
480
+ this.logger.debug(`Property search async completed`, {
481
+ status,
482
+ asyncStatus: data.status?.text,
483
+ });
484
+ return data;
485
+ }
486
+ /**
487
+ * Skip trace property owners (v1 API)
488
+ * POST /api/v1/property/skip-trace
489
+ *
490
+ * @param request Property skip trace request
491
+ * @returns Property skip trace API response
492
+ */
493
+ async skipTraceProperty(request) {
494
+ const url = `${this.v1BaseUrl}/property/skip-trace`;
495
+ this.logger.debug(`Skip tracing property: POST ${url}`, {
496
+ requestCount: request.requests.length,
497
+ });
498
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to skip trace property", url);
499
+ this.logger.debug(`Property skip trace completed`, {
500
+ status,
501
+ propertyCount: data.results?.properties?.length,
502
+ });
503
+ return data;
504
+ }
505
+ /**
506
+ * Skip trace property owners asynchronously (v1 API)
507
+ * POST /api/v1/property/skip-trace/async
508
+ *
509
+ * @param request Property skip trace async request
510
+ * @returns Property skip trace async API response
511
+ */
512
+ async skipTracePropertyAsync(request) {
513
+ const url = `${this.v1BaseUrl}/property/skip-trace/async`;
514
+ // Apply default webhook URLs if configured
515
+ const requestWithDefaults = this.applyDefaultWebhookUrls(request, "skipTrace");
516
+ this.logger.debug(`Skip tracing property async: POST ${url}`, {
517
+ requestCount: requestWithDefaults.requests.length,
518
+ });
519
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, requestWithDefaults), "Failed to skip trace property async", url);
520
+ this.logger.debug(`Property skip trace async completed`, {
521
+ status,
522
+ asyncStatus: data.status?.text,
523
+ });
524
+ return data;
525
+ }
526
+ /**
527
+ * Verify phone numbers (v1 API)
528
+ * POST /api/v1/phone/verification
529
+ *
530
+ * @param request Phone verification request
531
+ * @returns Phone verification API response
532
+ */
533
+ async verifyPhone(request) {
534
+ const url = `${this.v1BaseUrl}/phone/verification`;
535
+ this.logger.debug(`Verifying phone: POST ${url}`, {
536
+ requestCount: request.requests.length,
537
+ });
538
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to verify phone", url);
539
+ this.logger.debug(`Phone verification completed`, {
540
+ status,
541
+ phoneCount: data.results?.phoneNumbers?.length,
542
+ });
543
+ return data;
544
+ }
545
+ /**
546
+ * Verify phone numbers asynchronously (v1 API)
547
+ * POST /api/v1/phone/verification/async
548
+ *
549
+ * @param request Phone verification async request
550
+ * @returns Phone verification async API response
551
+ */
552
+ async verifyPhoneAsync(request) {
553
+ const url = `${this.v1BaseUrl}/phone/verification/async`;
554
+ // Apply default webhook URLs if configured
555
+ const requestWithDefaults = this.applyDefaultWebhookUrls(request, "phoneVerification");
556
+ this.logger.debug(`Verifying phone async: POST ${url}`, {
557
+ requestCount: requestWithDefaults.requests.length,
558
+ });
559
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, requestWithDefaults), "Failed to verify phone async", url);
560
+ this.logger.debug(`Phone verification async completed`, {
561
+ status,
562
+ asyncStatus: data.status?.text,
563
+ });
564
+ return data;
565
+ }
566
+ /**
567
+ * Check phone numbers against DNC (Do Not Call) list (v1 API)
568
+ * POST /api/v1/phone/dnc
569
+ *
570
+ * @param request Phone DNC request
571
+ * @returns Phone DNC API response
572
+ */
573
+ async checkPhoneDNC(request) {
574
+ const url = `${this.v1BaseUrl}/phone/dnc`;
575
+ this.logger.debug(`Checking phone DNC: POST ${url}`, {
576
+ requestCount: request.requests.length,
577
+ });
578
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to check phone DNC", url);
579
+ this.logger.debug(`Phone DNC check completed`, {
580
+ status,
581
+ phoneCount: data.results?.phoneNumbers?.length,
582
+ });
583
+ return data;
584
+ }
585
+ /**
586
+ * Check phone numbers against DNC list asynchronously (v1 API)
587
+ * POST /api/v1/phone/dnc/async
588
+ *
589
+ * @param request Phone DNC async request
590
+ * @returns Phone DNC async API response
591
+ */
592
+ async checkPhoneDNCAsync(request) {
593
+ const url = `${this.v1BaseUrl}/phone/dnc/async`;
594
+ // Apply default webhook URLs if configured
595
+ const requestWithDefaults = this.applyDefaultWebhookUrls(request, "phoneDNC");
596
+ this.logger.debug(`Checking phone DNC async: POST ${url}`, {
597
+ requestCount: requestWithDefaults.requests.length,
598
+ });
599
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, requestWithDefaults), "Failed to check phone DNC async", url);
600
+ this.logger.debug(`Phone DNC check async completed`, {
601
+ status,
602
+ asyncStatus: data.status?.text,
603
+ });
604
+ return data;
605
+ }
606
+ /**
607
+ * Check phone numbers for TCPA compliance (v1 API)
608
+ * POST /api/v1/phone/tcpa
609
+ *
610
+ * @param request Phone TCPA request
611
+ * @returns Phone TCPA API response
612
+ */
613
+ async checkPhoneTCPA(request) {
614
+ const url = `${this.v1BaseUrl}/phone/tcpa`;
615
+ this.logger.debug(`Checking phone TCPA: POST ${url}`, {
616
+ requestCount: request.requests.length,
617
+ });
618
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to check phone TCPA", url);
619
+ this.logger.debug(`Phone TCPA check completed`, {
620
+ status,
621
+ phoneCount: data.results?.phoneNumbers?.length,
622
+ });
623
+ return data;
624
+ }
625
+ /**
626
+ * Check phone numbers for TCPA compliance asynchronously (v1 API)
627
+ * POST /api/v1/phone/tcpa/async
628
+ *
629
+ * @param request Phone TCPA async request
630
+ * @returns Phone TCPA async API response
631
+ */
632
+ async checkPhoneTCPAAsync(request) {
633
+ const url = `${this.v1BaseUrl}/phone/tcpa/async`;
634
+ // Apply default webhook URLs if configured
635
+ const requestWithDefaults = this.applyDefaultWebhookUrls(request, "phoneTCPA");
636
+ this.logger.debug(`Checking phone TCPA async: POST ${url}`, {
637
+ requestCount: requestWithDefaults.requests.length,
638
+ });
639
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, requestWithDefaults), "Failed to check phone TCPA async", url);
640
+ this.logger.debug(`Phone TCPA check async completed`, {
641
+ status,
642
+ asyncStatus: data.status?.text,
643
+ });
644
+ return data;
645
+ }
646
+ /**
647
+ * Get all property subscriptions (v2 API)
648
+ * GET /api/v2/property-subscription
649
+ *
650
+ * @returns Array of property subscriptions
651
+ */
652
+ async getPropertySubscriptions() {
653
+ const url = `${this.v2BaseUrl}/property-subscription`;
654
+ this.logger.debug(`Getting property subscriptions: GET ${url}`);
655
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.get(url), "Failed to get property subscriptions", url);
656
+ this.logger.debug(`Get property subscriptions completed`, {
657
+ status,
658
+ subscriptionCount: Array.isArray(data) ? data.length : 0,
659
+ });
660
+ return data;
661
+ }
662
+ /**
663
+ * Get property subscription by ID (v2 API)
664
+ * GET /api/v2/property-subscription/{id}
665
+ *
666
+ * @param id Subscription ID
667
+ * @returns Property subscription detail
668
+ */
669
+ async getPropertySubscription(id) {
670
+ const url = `${this.v2BaseUrl}/property-subscription/${id}`;
671
+ this.logger.debug(`Getting property subscription: GET ${url}`, {
672
+ subscriptionId: id,
673
+ });
674
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.get(url), "Failed to get property subscription", url);
675
+ this.logger.debug(`Get property subscription completed`, {
676
+ status,
677
+ hasSubscription: !!data.results?.subscription,
678
+ });
679
+ return data;
680
+ }
681
+ /**
682
+ * Delete property subscription (v2 API)
683
+ * DELETE /api/v2/property-subscription/{id}
684
+ *
685
+ * @param id Subscription ID
686
+ * @returns Delete response
687
+ */
688
+ async deletePropertySubscription(id) {
689
+ const url = `${this.v2BaseUrl}/property-subscription/${id}`;
690
+ this.logger.debug(`Deleting property subscription: DELETE ${url}`, {
691
+ subscriptionId: id,
692
+ });
693
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.delete(url), "Failed to delete property subscription", url);
694
+ this.logger.debug(`Delete property subscription completed`, {
695
+ status,
696
+ });
697
+ return data;
698
+ }
699
+ /**
700
+ * Get property permits (v2 API)
701
+ * POST /api/v2/property/get-property-permits
702
+ *
703
+ * @param request Property permit request
704
+ * @returns Property permit API response
705
+ */
706
+ async getPropertyPermit(request) {
707
+ const url = `${this.v2BaseUrl}/property/get-property-permits`;
708
+ this.logger.debug(`Getting property permits: POST ${url}`);
709
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to get property permits", url);
710
+ this.logger.debug(`Get property permits completed`, {
711
+ status,
712
+ permitCount: data.result?.permits?.length,
713
+ });
714
+ return data;
715
+ }
716
+ /**
717
+ * Skip trace property owners (v3 API)
718
+ * POST /api/v3/property/skip-trace
719
+ *
720
+ * @param request Property skip trace v3 request
721
+ * @returns Property skip trace v3 API response
722
+ */
723
+ async skipTracePropertyV3(request) {
724
+ const url = `${this.v3BaseUrl}/property/skip-trace`;
725
+ this.logger.debug(`Skip tracing property v3: POST ${url}`, {
726
+ requestCount: request.requests.length,
727
+ });
728
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, request), "Failed to skip trace property v3", url);
729
+ this.logger.debug(`Property skip trace v3 completed`, {
730
+ status,
731
+ dataCount: data.result?.data?.length,
732
+ });
733
+ return data;
734
+ }
735
+ /**
736
+ * Skip trace property owners asynchronously (v3 API)
737
+ * POST /api/v3/property/skip-trace/async
738
+ *
739
+ * @param request Property skip trace v3 async request
740
+ * @returns Property skip trace v3 async API response
741
+ */
742
+ async skipTracePropertyV3Async(request) {
743
+ const url = `${this.v3BaseUrl}/property/skip-trace/async`;
744
+ // Apply default webhook URLs if configured
745
+ const requestWithDefaults = this.applyDefaultWebhookUrls(request, "skipTraceV3");
746
+ this.logger.debug(`Skip tracing property v3 async: POST ${url}`, {
747
+ requestCount: requestWithDefaults.requests.length,
748
+ });
749
+ const { data, status } = await this.handleApiCall(() => this.axiosInstance.post(url, requestWithDefaults), "Failed to skip trace property v3 async", url);
750
+ this.logger.debug(`Property skip trace v3 async completed`, {
751
+ status,
752
+ requestId: data.result?.requestId,
753
+ });
754
+ return data;
755
+ }
756
+ }
757
+ exports.BatchDataClient = BatchDataClient;