@taxora/sdk 1.7.0 → 1.8.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/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  All notable changes to `@taxora/sdk` are documented here. Versions follow [SemVer](https://semver.org/).
4
4
 
5
+ ## [1.8.0] — 2026-10-25
6
+
7
+ ### Added
8
+
9
+ - **`smartEnrichment.recheck(jobId, { rows?, dryRun? })`** searches again for rows without a usable answer
10
+ (`not_found`, or a number registered to another company). Every recheck counts as a check like a new lookup (Deep
11
+ Verify ×2), found or not; max 3 per row, 7 days apart; `no_vat_exists` and found rows are never rechecked.
12
+ `dryRun` reports rows, skipped rows and cost without starting anything. Returns `SmartEnrichmentRecheck`
13
+ (`rows`, `checks`, `skipped`, `dryRun`).
14
+ - **`SmartEnrichmentJob.recheck`** (`SmartEnrichmentRecheckState`: `eligible`, `runs`, `maxPerRow`, `cooldownDays`,
15
+ `isActive()`) and **`recheckCount` / `lastRecheckedAt`** on each result.
16
+
17
+ ### Changed
18
+
19
+ - Bundled OpenAPI specs (`resources/`) refreshed from the current API.
20
+
21
+ ## [1.7.1] — 2026-10-24
22
+
23
+ ### Fixed
24
+
25
+ - **`smartEnrichment.export()` keeps the CSV's UTF-8 BOM.** `Response.text()` silently dropped it, so Excel opened a
26
+ saved export as ANSI and garbled umlauts. The body is now returned byte-for-byte, as documented.
27
+
5
28
  ## [1.7.0] — 2026-10-24
6
29
 
7
30
  ### Added
package/README.md CHANGED
@@ -448,6 +448,26 @@ for (const row of history) {
448
448
  }
449
449
  ```
450
450
 
451
+ ### Check rows again
452
+
453
+ Search once more for rows without a usable answer (`not_found`, or a number registered to another company). Every
454
+ recheck counts as a check like a new lookup (Deep Verify ×2), whether a VAT number is found or not. A row can be
455
+ rechecked up to 3 times, 7 days apart; `no_vat_exists` and found rows are never rechecked.
456
+
457
+ ```ts
458
+ // What would run, and what it costs — nothing is started
459
+ const plan = await client.smartEnrichment.recheck(jobId, { dryRun: true });
460
+ console.log(plan.rows.length, 'rows,', plan.checks, 'checks, skipped:', plan.skipped);
461
+
462
+ await client.smartEnrichment.recheck(jobId); // every row that qualifies
463
+ await client.smartEnrichment.recheck(jobId, { rows: [3, 7] }); // or just these rows
464
+
465
+ // Improved rows arrive via the `enrichment.updated` webhook; or poll:
466
+ const job = await client.smartEnrichment.get(jobId);
467
+ console.log(job.recheck?.isActive() ? 'still rechecking' : 'done');
468
+ console.log(job.results[3]?.recheckCount, job.results[3]?.lastRecheckedAt);
469
+ ```
470
+
451
471
  ### Quota usage & billing history
452
472
 
453
473
  ```ts
package/dist/index.cjs CHANGED
@@ -63,6 +63,8 @@ __export(index_exports, {
63
63
  SmartEnrichmentHistoryRow: () => SmartEnrichmentHistoryRow,
64
64
  SmartEnrichmentJob: () => SmartEnrichmentJob,
65
65
  SmartEnrichmentMode: () => SmartEnrichmentMode,
66
+ SmartEnrichmentRecheck: () => SmartEnrichmentRecheck,
67
+ SmartEnrichmentRecheckState: () => SmartEnrichmentRecheckState,
66
68
  SmartEnrichmentRejectionReason: () => SmartEnrichmentRejectionReason,
67
69
  SmartEnrichmentResource: () => SmartEnrichmentResource,
68
70
  SmartEnrichmentStatistics: () => SmartEnrichmentStatistics,
@@ -2318,6 +2320,73 @@ var EReportingEndpoint = class {
2318
2320
  }
2319
2321
  };
2320
2322
 
2323
+ // src/dto/SmartEnrichmentRecheck.ts
2324
+ var toIndexes = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "number" && Number.isInteger(v)) : [];
2325
+ var SmartEnrichmentRecheck = class _SmartEnrichmentRecheck {
2326
+ constructor(jobId, rows, checks, skipped, dryRun) {
2327
+ this.jobId = jobId;
2328
+ this.rows = rows;
2329
+ this.checks = checks;
2330
+ this.skipped = skipped;
2331
+ this.dryRun = dryRun;
2332
+ }
2333
+ jobId;
2334
+ rows;
2335
+ checks;
2336
+ skipped;
2337
+ dryRun;
2338
+ static fromArray(data) {
2339
+ const skipped = {};
2340
+ const rawSkipped = data["skipped"];
2341
+ if (typeof rawSkipped === "object" && rawSkipped !== null && !Array.isArray(rawSkipped)) {
2342
+ for (const [reason, count] of Object.entries(rawSkipped)) {
2343
+ if (typeof count === "number") skipped[reason] = count;
2344
+ }
2345
+ }
2346
+ return new _SmartEnrichmentRecheck(
2347
+ typeof data["jobId"] === "string" ? data["jobId"] : "",
2348
+ toIndexes(data["rows"]),
2349
+ typeof data["checks"] === "number" ? data["checks"] : 0,
2350
+ skipped,
2351
+ data["dryRun"] === true
2352
+ );
2353
+ }
2354
+ };
2355
+ var SmartEnrichmentRecheckState = class _SmartEnrichmentRecheckState {
2356
+ constructor(eligible, runs, maxPerRow, cooldownDays) {
2357
+ this.eligible = eligible;
2358
+ this.runs = runs;
2359
+ this.maxPerRow = maxPerRow;
2360
+ this.cooldownDays = cooldownDays;
2361
+ }
2362
+ eligible;
2363
+ runs;
2364
+ maxPerRow;
2365
+ cooldownDays;
2366
+ static fromArray(data) {
2367
+ const runs = {};
2368
+ const rawRuns = data["runs"];
2369
+ if (typeof rawRuns === "object" && rawRuns !== null && !Array.isArray(rawRuns)) {
2370
+ for (const [index, run] of Object.entries(rawRuns)) {
2371
+ if (!/^\d+$/.test(index) || typeof run !== "object" || run === null) continue;
2372
+ const { state, resolved } = run;
2373
+ if (state !== "queued" && state !== "running" && state !== "finished" && state !== "failed") continue;
2374
+ runs[Number(index)] = typeof resolved === "boolean" ? { state, resolved } : { state };
2375
+ }
2376
+ }
2377
+ return new _SmartEnrichmentRecheckState(
2378
+ toIndexes(data["eligible"]),
2379
+ runs,
2380
+ typeof data["maxPerRow"] === "number" ? data["maxPerRow"] : 3,
2381
+ typeof data["cooldownDays"] === "number" ? data["cooldownDays"] : 7
2382
+ );
2383
+ }
2384
+ /** True while any recheck of this lookup is queued or running. */
2385
+ isActive() {
2386
+ return Object.values(this.runs).some((run) => run.state === "queued" || run.state === "running");
2387
+ }
2388
+ };
2389
+
2321
2390
  // src/enums/SmartEnrichmentMode.ts
2322
2391
  var SmartEnrichmentMode = {
2323
2392
  DEFAULT: "default",
@@ -2447,7 +2516,7 @@ function toOfficialAddress(value) {
2447
2516
  return Object.keys(address).length > 0 ? address : void 0;
2448
2517
  }
2449
2518
  var SmartEnrichmentResource = class _SmartEnrichmentResource {
2450
- constructor(status, vatNumber, vatType, confidence, matchedCompanyName, matchedAddress, country, source, mode = void 0, providerVerdicts = [], addressQuality = void 0, rejectedCandidates = [], sourceUrl = void 0, registeredOwner = void 0, sourceUrls = [], taxNumber = void 0, vatValid = void 0, aiScore = void 0, nameMatch = void 0, officialName = void 0, officialAddress = void 0, taxNumberType = void 0, taxNumberLabel = void 0, taxNumberSource = void 0) {
2519
+ constructor(status, vatNumber, vatType, confidence, matchedCompanyName, matchedAddress, country, source, mode = void 0, providerVerdicts = [], addressQuality = void 0, rejectedCandidates = [], sourceUrl = void 0, registeredOwner = void 0, sourceUrls = [], taxNumber = void 0, vatValid = void 0, aiScore = void 0, nameMatch = void 0, officialName = void 0, officialAddress = void 0, taxNumberType = void 0, taxNumberLabel = void 0, taxNumberSource = void 0, recheckCount = void 0, lastRecheckedAt = void 0) {
2451
2520
  this.status = status;
2452
2521
  this.vatNumber = vatNumber;
2453
2522
  this.vatType = vatType;
@@ -2472,6 +2541,8 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
2472
2541
  this.taxNumberType = taxNumberType;
2473
2542
  this.taxNumberLabel = taxNumberLabel;
2474
2543
  this.taxNumberSource = taxNumberSource;
2544
+ this.recheckCount = recheckCount;
2545
+ this.lastRecheckedAt = lastRecheckedAt;
2475
2546
  }
2476
2547
  status;
2477
2548
  vatNumber;
@@ -2497,6 +2568,8 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
2497
2568
  taxNumberType;
2498
2569
  taxNumberLabel;
2499
2570
  taxNumberSource;
2571
+ recheckCount;
2572
+ lastRecheckedAt;
2500
2573
  /** True when the register knows the number under a different name than the one requested. */
2501
2574
  nameMismatch() {
2502
2575
  return this.nameMatch === "mismatch";
@@ -2547,7 +2620,9 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
2547
2620
  toOfficialAddress(data["officialAddress"]),
2548
2621
  text("taxNumberType"),
2549
2622
  text("taxNumberLabel"),
2550
- data["taxNumberSource"] === "vat_id" || data["taxNumberSource"] === "company_website" ? data["taxNumberSource"] : void 0
2623
+ data["taxNumberSource"] === "vat_id" || data["taxNumberSource"] === "company_website" ? data["taxNumberSource"] : void 0,
2624
+ num("recheckCount"),
2625
+ text("lastRecheckedAt")
2551
2626
  );
2552
2627
  }
2553
2628
  toArray() {
@@ -2575,20 +2650,23 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
2575
2650
  officialAddress: this.officialAddress ?? null,
2576
2651
  taxNumberType: this.taxNumberType ?? null,
2577
2652
  taxNumberLabel: this.taxNumberLabel ?? null,
2578
- taxNumberSource: this.taxNumberSource ?? null
2653
+ taxNumberSource: this.taxNumberSource ?? null,
2654
+ recheckCount: this.recheckCount ?? null,
2655
+ lastRecheckedAt: this.lastRecheckedAt ?? null
2579
2656
  };
2580
2657
  }
2581
2658
  };
2582
2659
 
2583
2660
  // src/dto/SmartEnrichmentJob.ts
2584
2661
  var SmartEnrichmentJob = class _SmartEnrichmentJob {
2585
- constructor(jobId, status, itemCount, completedCount, foundCount, results) {
2662
+ constructor(jobId, status, itemCount, completedCount, foundCount, results, recheck = void 0) {
2586
2663
  this.jobId = jobId;
2587
2664
  this.status = status;
2588
2665
  this.itemCount = itemCount;
2589
2666
  this.completedCount = completedCount;
2590
2667
  this.foundCount = foundCount;
2591
2668
  this.results = results;
2669
+ this.recheck = recheck;
2592
2670
  }
2593
2671
  jobId;
2594
2672
  status;
@@ -2596,6 +2674,7 @@ var SmartEnrichmentJob = class _SmartEnrichmentJob {
2596
2674
  completedCount;
2597
2675
  foundCount;
2598
2676
  results;
2677
+ recheck;
2599
2678
  static fromArray(data) {
2600
2679
  const num = (key) => {
2601
2680
  const v = data[key];
@@ -2613,7 +2692,8 @@ var SmartEnrichmentJob = class _SmartEnrichmentJob {
2613
2692
  num("itemCount"),
2614
2693
  num("completedCount"),
2615
2694
  num("foundCount"),
2616
- results
2695
+ results,
2696
+ typeof data["recheck"] === "object" && data["recheck"] !== null && !Array.isArray(data["recheck"]) ? SmartEnrichmentRecheckState.fromArray(data["recheck"]) : void 0
2617
2697
  );
2618
2698
  }
2619
2699
  isProcessing() {
@@ -2859,6 +2939,41 @@ var SmartEnrichmentEndpoint = class {
2859
2939
  const data = await this.parseJsonResponse(response);
2860
2940
  return SmartEnrichmentJob.fromArray(data);
2861
2941
  }
2942
+ /**
2943
+ * Search again for rows of a finished lookup that have no usable answer (`not_found`, or a
2944
+ * number registered to another company). Without `rows` every row that qualifies is rechecked.
2945
+ *
2946
+ * Every recheck counts as a check like a new lookup (Deep Verify ×2), whether a VAT number is
2947
+ * found or not. A row can be rechecked up to 3 times, with 7 days between two rechecks;
2948
+ * `no_vat_exists` and found rows are never rechecked. Pass `dryRun` to see which rows would run
2949
+ * and what they cost without starting anything. Improved rows arrive via the
2950
+ * `enrichment.updated` webhook; `get(jobId).recheck` shows the progress.
2951
+ *
2952
+ * Throws a ValidationException when nothing can be rechecked, the lookup is still running, or
2953
+ * rechecks are not available (sandbox, flat-fee import), and an HttpException (429) when the
2954
+ * start credit or monthly hard cap does not cover it.
2955
+ */
2956
+ async recheck(jobId, options = {}) {
2957
+ this.validateJobId(jobId);
2958
+ const body = {};
2959
+ if (options.rows !== void 0) {
2960
+ if (options.rows.length === 0) {
2961
+ throw new TaxoraException("rows must not be empty; omit it to recheck every row that qualifies.");
2962
+ }
2963
+ if (options.rows.some((index) => !Number.isInteger(index) || index < 0)) {
2964
+ throw new TaxoraException("rows must contain 0-based row indexes (integers >= 0).");
2965
+ }
2966
+ body["rows"] = [...new Set(options.rows)];
2967
+ }
2968
+ if (options.dryRun) body["dryRun"] = true;
2969
+ const response = await this.sendRequest(
2970
+ "POST",
2971
+ `${this.baseUrl}/smart-enrichment/${encodeURIComponent(jobId)}/recheck`,
2972
+ body
2973
+ );
2974
+ const data = await this.parseJsonResponse(response, [200, 202]);
2975
+ return SmartEnrichmentRecheck.fromArray(data);
2976
+ }
2862
2977
  /**
2863
2978
  * Poll get(jobId) until the job is no longer processing (completed, failed or resolved).
2864
2979
  * Throws a TaxoraException when the job does not finish within `timeoutMs`.
@@ -3063,7 +3178,7 @@ var SmartEnrichmentEndpoint = class {
3063
3178
  * verbatim (text/csv) instead of parsing JSON.
3064
3179
  */
3065
3180
  async parseCsvResponse(response) {
3066
- const responseText = await response.text();
3181
+ const responseText = new TextDecoder("utf-8", { ignoreBOM: true }).decode(await response.arrayBuffer());
3067
3182
  if (response.status === 422) {
3068
3183
  throw new ValidationException(
3069
3184
  jsonErrorMessage(responseText) ?? "Validation failed",
@@ -4033,7 +4148,7 @@ var AuthRetryHttpClient = class {
4033
4148
  };
4034
4149
 
4035
4150
  // src/version.ts
4036
- var SDK_VERSION = "1.7.0";
4151
+ var SDK_VERSION = "1.8.0";
4037
4152
 
4038
4153
  // src/http/SdkVersionHttpClient.ts
4039
4154
  var SDK_VERSION_HEADER = "X-Taxora-SDK-Version";
@@ -4264,6 +4379,8 @@ function toBytes(rawBody) {
4264
4379
  SmartEnrichmentHistoryRow,
4265
4380
  SmartEnrichmentJob,
4266
4381
  SmartEnrichmentMode,
4382
+ SmartEnrichmentRecheck,
4383
+ SmartEnrichmentRecheckState,
4267
4384
  SmartEnrichmentRejectionReason,
4268
4385
  SmartEnrichmentResource,
4269
4386
  SmartEnrichmentStatistics,