@taxora/sdk 1.1.0 → 1.2.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/README.md CHANGED
@@ -331,7 +331,35 @@ console.log(result?.status); // 'found' | 'no_vat_exists' | 'not_found'
331
331
  console.log(result?.vatNumber); // e.g. 'ATU12345678'
332
332
  console.log(result?.confidence); // 0–100
333
333
 
334
+ // Search mode: 'default' searches with one AI provider and only consults a second one when
335
+ // the first finds nothing. 'complex' has two independent providers search from the start and
336
+ // puts every number either of them proposes through the tax authority's checks. Complex finds
337
+ // companies a single search misses — typically ones whose VAT only appears in a website
338
+ // Impressum — and costs more per lookup, so it is opt-in.
339
+ const deep = await client.smartEnrichment.lookup({
340
+ companyName: 'Vodafone D2 GmbH',
341
+ country: 'DE',
342
+ postalCode: '40547',
343
+ mode: SmartEnrichmentMode.COMPLEX,
344
+ });
345
+
346
+ // When more than one provider searched, you can see what each one concluded on its own.
347
+ // Two of them reporting the same number is the strongest confirmation this layer produces.
348
+ for (const verdict of deep.result()?.providerVerdicts ?? []) {
349
+ console.log(`${verdict.provider}: ${verdict.vatNumber ?? 'nothing'}`);
350
+ }
351
+
352
+ // The API also grades the address you sent. Worth writing back into your own records: a
353
+ // 'postal_code_city_mismatch' / 'postal_code_unassigned' warning means the postal code in
354
+ // your source data is wrong, and `derivedPlace` is the area it actually points at.
355
+ const quality = deep.result()?.addressQuality;
356
+ if (quality && quality.warnings.length > 0) {
357
+ console.log(quality.warnings.join(', '), '→', quality.derivedPlace);
358
+ }
359
+
334
360
  // Bulk lookup (always async → webhook + polling)
361
+ // The batch-level mode applies to every row; a row carrying its own `mode` overrides it,
362
+ // so you can pay for 'complex' on just the rows that need it.
335
363
  const bulk = await client.smartEnrichment.bulkLookup([
336
364
  { companyName: 'A GmbH', country: 'DE', city: 'Berlin' },
337
365
  { companyName: 'B SARL', country: 'FR' },
@@ -668,11 +696,73 @@ try {
668
696
  console.error('Validation errors:', err.getErrors()); // Record<string, string[]>
669
697
  } else if (err instanceof HttpException) {
670
698
  // Any other HTTP error
671
- console.error(`HTTP ${err.getStatusCode()}:`, err.getResponseBody());
699
+ console.error(`HTTP ${err.getStatusCode()}:`, err.message);
672
700
  }
673
701
  }
674
702
  ```
675
703
 
704
+ **Exception messages are always short and safe to log.** A response body is
705
+ never used as the message: gateways in front of the API answer `502`/`503`/`504`
706
+ with a full HTML error page, and that page would otherwise land in every log
707
+ line of your application. The SDK uses the `message` / `error` field of a JSON
708
+ error body when there is one, and falls back to the status line
709
+ (`Taxora API request failed (HTTP 504 Gateway Timeout).`) otherwise. The
710
+ untouched body stays available via `err.getResponseBody()`.
711
+
712
+ ### Automatic retries
713
+
714
+ The SDK retries what never produced an answer from the API itself: **3 attempts,
715
+ with 500 ms and 1000 ms of backoff in between**. That covers
716
+
717
+ - **gateway failures (`502`, `503`, `504`)** — the infrastructure in front of the
718
+ API, where a second attempt a moment later usually succeeds;
719
+ - **connection-level failures** — reset connections, client-side timeouts, DNS
720
+ hiccups (`fetch failed`, `ECONNRESET`, `UND_ERR_CONNECT_TIMEOUT`, …). Once the
721
+ attempts are used up, the original error is rethrown unchanged;
722
+ - **`429 Too Many Requests`, but only with a `Retry-After`** — then the SDK waits
723
+ exactly as long as the API asked instead of using its own backoff. Without that
724
+ header a retry would only hammer a rate limit that is already tripped, so the
725
+ error is surfaced immediately. A `Retry-After` longer than `maxRetryAfterMs`
726
+ (10 s by default) also fails immediately rather than blocking your worker — the
727
+ raw header stays readable via `err.getRetryAfter()`.
728
+
729
+ Everything else fails on the first attempt, including any `5xx` the API produced
730
+ itself (a `500` is a bug, not a hiccup — repeating it just costs time).
731
+
732
+ Retries apply to read-only calls only — VAT validation, lookups, state, history,
733
+ search, certificate downloads, company and e-reporting reads, login and token
734
+ refresh. Calls that change state or cost quota (`certificatesBulkExport()`,
735
+ `smartEnrichment.lookup()`, e-reporting enrollment/transaction writes) are
736
+ **never** retried automatically, because a gateway timeout does not tell us
737
+ whether the API already processed the request.
738
+
739
+ Once the attempts are used up you get one clean error:
740
+
741
+ ```
742
+ Taxora VAT validation failed after 3 attempts (HTTP 504 Gateway Timeout).
743
+ ```
744
+
745
+ Tune or disable it via `RetryPolicy`:
746
+
747
+ ```ts
748
+ import { RetryPolicy, TaxoraClientFactory } from '@taxora/sdk';
749
+
750
+ const client = TaxoraClientFactory.create({
751
+ apiKey: 'YOUR_X_API_KEY',
752
+ retryPolicy: new RetryPolicy({
753
+ maxAttempts: 5,
754
+ initialDelayMs: 250,
755
+ retryableStatusCodes: [502, 503, 504], // add or drop status codes
756
+ respectRetryAfter: true, // false = ignore the header entirely
757
+ maxRetryAfterMs: 10_000, // longest wait accepted from Retry-After
758
+ retryOnNetworkErrors: true, // false = surface transport errors at once
759
+ }),
760
+ });
761
+
762
+ // or, for callers that do their own retrying (queue workers, cron jobs):
763
+ const noRetries = TaxoraClientFactory.create({ apiKey: '…', retryPolicy: RetryPolicy.disabled() });
764
+ ```
765
+
676
766
  ---
677
767
 
678
768
  ## 🔌 VatState Helpers